Sound question

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Sound question
by on (#30039)
Alrighty. I've gotten an NSF included in my program and I've gotten the play routines and everything. One problem I'm having involves sound effects and music. I can play music fine, though I need to reset the sound registers when the track is done in order for sound effects to work. Obviously, I can't reset the registers as it's playing and therefore I'm unable to have both music and sound effects together.

The sound effects right now are simple calls and writes to the sound registers, although I kind of wanted to try making a few more complex ones with Famitracker.

Question is: Can I have it so that the sound effects are part of the NSF and have both them and another track playing at once?

Any easy answers? 8)

by on (#30040)
Depends entirely on the engine. Sound effects have to prevent the music from using the same sound channels as it until the sound effect is complete, at which time the music would have to reset the regs to what it needs them to be to play music. So basically, sound effects and music have to "work together" if you want them to play on the same channels at the same time.

I can't say for certain -- but I wouldn't be suprised if the driver in NSF trackers are not built with sound effects in mind (they seem to be oriented for homebrew NSFs, and not really for full homebrew ROMs). In which case you're SOL unless you reserve one or more sound channel exclusively for sound effects (like -- don't use square 2 for your music, and just have square 2 be your sound effects) -- or unless you rip up the driver to add sound effect support ;P

But really, I'm just making assumptions. See what your tracker can and can't do in this department.

by on (#30042)
I like Memblers' solution: virtualize $4000-$4015 so the music engine's use of the physical registers can be mediated by your code. Just replace writes in the music engine to $40xx with a jsr to the appropriate virtual version. A decent NES debugger should make it easy to find all the places that have these writes, so you could even do a binary patch.

by on (#30043)
ooo... yeah that's a really good idea =D

by on (#30058)
It might've been kevtris' idea originally, I got it working in that Capcom player though. I also wrote up an autopatcher for it, for my NSF loader on Squeedo (NES patches it before writing to Flash) but I never added it in since it required a rewrite of some other stuff to be less likely to break.

In this case though, Sivak has the source code. What I do is make variables like v4000, v4001, go in and replace the $ signs with v in the player.

by on (#30063)
Ahhh, so you don't go the whole way and replace writes with JSRs, you simply change where the values are written, and periodically flush these to the real registers (except when a sound effect is playing). Doing this is simpler, for sure, though it might affect timing of some writes where the music engine wants to write multiple differing values to the same register (like my trick to change a square's period without ever resetting its phase).

by on (#30073)
I guess I'm not entirely certain as to what the idea behind the thread is. I know it's trying to say to use variables for the sound registers somehow... Though with the way the program is, it seems that doing anything to the sound registers won't affect this situation.

Right now I have some simple routines involving a load, play, and init address.

During the NMI, the play address is constantly called and will play whatever would need to be played... I was thinking of implementing a condition on it not being called for points of the game where silence would be.

Relating to sound effects, I have simple procedures such as this:

Code:
SFXDrawGem:
lda #%10001111
sta $4004
lda #%11011010
sta $4005
lda #%10111010
sta $4006
lda #%10001000
sta $4007
rts


Whenever I want that played, I just do a JSR SFXDrawGem...

Anyway, I'll leave it at that for now. I'm interested in seeing if there's a better solution to sound than what I'm doing.

by on (#30077)
Before:
Code:
music_tick:
    ...
    sta $4000
    ...
    sta $4015
    rts

do_tick:
    jsr music_tick
    ...

After:
Code:
music_tick:
    ...
    sta music_4000
    ...
    sta music_4015
    rts

do_tick:
    jsr music_tick
    jsr sfx_tick
   
    lda sfx_running
    bne sfx
music:
    lda music_4000
    sta $4000
    ...
    lda music_4015
    sta $4015
    rts

sfx:
    lda sfx_4000
    sta $4000
    ...
    lda sfx_4015
    sta $4015
    rts

This puts your code in control of the sound registers, so it can decide whether music or sfx get a particular sound channel.

by on (#30081)
Also make a timer that counts how many frames long your sound effect is, so you know how long to play it instead of the music.

by on (#31504)
I know this thread is old, but I wanted to look back into it.

Basically, I got nice-sounding stuff with Famitracker and the NSFs I've created. The real trick now is to just get sound effects and music at the same time from the same NSF.

What I'm unclear on is what do I store into the "virtual" sound variables? Is there something within the NSF itself that writes to some memory spot that I need to load into those and then write to the sound registers? I know that Famitracker generated NSFs use part of zero page and $0200 through $02DC.

I would probably want to try to make an experimental program that gets all this squared away and then try implementing in my games. I'd think I'd need some kind of flag to ID between sound effect and music.

JSR wrote something in this post at his forum, although I don't quite know what to make of it. It's the last post:
http://famitracker.shoodot.net/forum/posts.php?id=235

by on (#31505)
"Virtual" sound registers means that you modify the player code to never touch the actual sound registers. You treat the sound player code as a black box, which returns "what it wants to write to the sound registers". Then you can override any decision made by the music code, for example, if you are playing a sound effect in Square A, wait for that sound effect to finish before applying the music code's sound writes to that channel.

by on (#38379)
I've been trying to mess with implementing this virtual registers thing, and I've gone through in the NT2 replay code and changed all of the $40xx values to different variables in zeropage (minus the DMC channels, since I won't be using those).

What I'm running into, is that I'm not sure where in the code the music 'tick' stuff would be. Does anyone have a general idea of where this is in the code, as I can't make out where it would be exactly, and how to implement these virtual registers.

by on (#38384)
The music 'tick' is just the whole play routine each frame.
If you've got all the reg data in virtual regs, all you have to do then is blast those into the real regs right after the play routine. (and overwrite the virtual regs with your sound effects first, as needed)

by on (#38396)
Man, I just noticed that the replay code doesn't have anything assigned specifically for the second square channel. Why is that? Also, this is how the code is right now, and it's not working well at all hehe

Code:
   jsr play
   lda v4000
   sta $4000
   lda v4001
   sta $4001
   lda v4002
   sta $4002
   lda v4003
   sta $4003
   lda v4008
   sta $4008
   lda v400E
   sta $400E
   lda v400F
   sta $400F
   lda v4015
   sta $4015


There were no other $40xx to be found in the replay code, so those are the only ones that I have changed.

by on (#38397)
To avoid clicks and buzzes due to resetting of the waveform's phase as a side effect, you should write to $4003 (or $4007) only if v4003 (or v4007) has changed since the last frame. This will need two more variables last_v4003 and possibly last_v4007.

by on (#38403)
Yeah, I had a problem with that grumbly noise as well. Since I have to do some calculations in my arpeggio routine, I have some result variables that hold the result of the calculation of the current 11-bit pitch value. I compare the high 3 bits to what it was last frame, which is actually still in the virtual $4003 register as it hasn't been updated yet. So I don't actually need anything like last_$4003.