I'm out of practice, but I believe this happens when you start a new note at the same time that the sweep unit was about to adjust the pitch of the old note.
This page on the wiki (under "updating the period") seems to explain why: when you write to the sweep register (like you would when starting a new sound effect), it doesn't immediately reset the sweep timer, it only sets the "reload" flag, which would normally reset the sweep timer and allow the initial pitch to play, except when the timer's current value is already at 0, in which case the pitch adjustment happens just like normal as the timer is being reset.
sweep, #$FF -> $4017, pitch.
The first write sets the reload flag, the second write forces the sweep units to clock (and reload) and also resets the frame counter so we know the sweep units will not naturally clock themselves again for another 14913 CPU cycles (no unintentional double-clock here in other words), and then the third write is the pitch, which occurs safely after we know the sweep counter cannot be about to clock at zero (unless you intentionally set the sweep speed to 0, and even then, the pitch adjustment cannot happen for another 14913 CPU cycles because we reset the frame counter with the 4017 write).
Be aware that writing to $4017 multiple times will clock the volume envelopes, length counters, sweep units, etc multiple times, so to keep the APU's timings consistent, I recommend figuring out how to write your sound driver such that it always writes to $4017 exactly once per call.
Thanks Drag, I think I'm on the right track now. in my routine that loads sound effects, as a quick and dirty test, I inserted a write to $4017 in between updating the sweep and the pitch:Try this:
sweep, #$FF -> $4017, pitch.
Code: Select all
LDA [sFXPointerLo], Y STA $4000, X INY LDA [sFXPointerLo], Y STA $4001, X LDA #$FF STA $4017 INY LDA [sFXPointerLo], Y STA $4002, X INY LDA [sFXPointerLo], Y STA $4003, X
I'm happy with my solution, but I'm still curious how other people have approached this. Do you reload $4017 each frame? and if so, when do you do it?
Writing $4017 once per frame is also very common, I think many of Nintendo's games write $FF to it once per frame. In my own engine I wrote $C0 to it once at the start of the APU register update pass each frame.
#$00 -> $4008 ;Set linear counter to 0
#$07 -> $400B ;Set reload flag of linear counter, set pitch really low to avoid pops
#$FF -> $4017 ;Immediately clock linear counter, causing the linear counter to reload to 0, silencing triangle channel
(Note: Since I last worked on my music engine, it's been discovered that the $400B write isn't necessary if you use a different strategy for writing to $4008.)
The writes to $4008 and $400B are skipped if the triangle channel doesn't need to be silenced, but the $4017 write always happens regardless, for consistency. I used to just quickly flip the triangle bit in $4015, but needed to switch to this current method once I added DMC support.
My engine doesn't currently use the sweep channels, but it would be easy to add the square channel sweep/pitch writes to this logic as well.