It is currently Mon Sep 16, 2019 1:43 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 9 posts ] 
Author Message
PostPosted: Wed Sep 11, 2019 5:19 am 
Offline
User avatar

Joined: Mon Mar 13, 2017 5:21 pm
Posts: 67
I'm using the sweep for several sound effects. Sometimes, it sounds like the sweep is skipping the first pitch and going directly to the second pitch. It seems to be happening right after playing a different sound effect. I thought maybe the data from the previous sound was affecting the next one, so I tried loading all zeros into the pulse channel before loading the next set of data, but that didn't seem to help. Then I thought that maybe I need to reset the frame counter, so I tried loading $FF into $4017 on every frame, and also right before loading the data into the pulse registers and neither of those worked. I thought maybe the order mattered, so I tried loading the data in reverse (starting at $4003 and going backward to $4000), but I got the same thing. I'm really at a loss. I'm not even sure if I'm doing a good job explaining what my problem is. it's like, if I use the sweep to make an arpeggio sort of sound, sometimes it starts on the second pitch rather than the first one. Does anybody know what I'm talking about?


Top
 Profile  
 
PostPosted: Wed Sep 11, 2019 6:55 am 
Offline

Joined: Mon Sep 27, 2004 2:57 pm
Posts: 1266
This happens on actual hardware and is the result of a fun timing quirk. :P

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.


Top
 Profile  
 
PostPosted: Wed Sep 11, 2019 3:26 pm 
Offline
User avatar

Joined: Mon Mar 13, 2017 5:21 pm
Posts: 67
So how do you reset the Sweep to prevent it? like I said, I tried writing $FF to $4017 right beforehand, and also every frame, neither helped. I also tried writing 0 to $4015, then re-enabling the channels right beforehand, and that didn't help either. I also tried writing the data to the audio registers twice (just grasping at straws), which didn't fix it. Is there a specific order I should be writing data to the audio registers for the pulse channels?


Top
 Profile  
 
PostPosted: Wed Sep 11, 2019 6:46 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21591
Location: NE Indiana, USA (NTSC)
Did you try starting the note without sweep (write $08 to $4001 or $4005) and then enabling sweep one or two frames later?

_________________
Pin Eight | Twitter | GitHub | Patreon


Top
 Profile  
 
PostPosted: Wed Sep 11, 2019 7:39 pm 
Offline

Joined: Mon Sep 27, 2004 2:57 pm
Posts: 1266
Unfortunately, the only thing that can reset the sweep counter is that reload flag, which reloads the counter on the next clock of the sweep unit. The sweep unit is clocked by the frame counter, and we know that writing a 1 to $4017.7 will immediately trigger a clock to everything connected to the frame counter (including the sweep units).

Try this:
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.


Top
 Profile  
 
PostPosted: Thu Sep 12, 2019 5:42 am 
Offline
User avatar

Joined: Mon Mar 13, 2017 5:21 pm
Posts: 67
Quote:
Try this:
sweep, #$FF -> $4017, pitch.


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:

Code:
   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


This solved the problem. My new problem is that this routine is called by anything that wants to load a sound effect, any number of times per frame. so to keep the APU running at an even pace, I'll have to buffer sound effect data and load all audio registers at once, making one write to $4017 in between loading the sweeps and pitch data for the pulse channels. Thanks again for your help.


Top
 Profile  
 
PostPosted: Sun Sep 15, 2019 8:37 am 
Offline
User avatar

Joined: Mon Mar 13, 2017 5:21 pm
Posts: 67
I finished my solution. basically, I buffer sound effects as various objects try to initiate them, then in my audio engine I loop through each audio channel once to process all the music or sound effect data and then load the first two bytes of each channel (the second of which doesn't really do anything on triangle or noise, but including them makes the loops easier to write). After that, I write $FF to $4017 (since my audio engine is called once per frame, this is very consistent now). After that, I loop through all the audio channels again and load the last two bytes (I have logic that checks and only loads the 4th byte if necessary to avoid refreshing the channel when I don't want it to), and also reload $4015 to begin DMC playback if that is necessary.

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?


Top
 Profile  
 
PostPosted: Sun Sep 15, 2019 12:12 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7582
Location: Canada
Doing all of the register writes in one relatively quick pass is a good idea.

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.


Top
 Profile  
 
PostPosted: Sun Sep 15, 2019 2:28 pm 
Offline

Joined: Mon Sep 27, 2004 2:57 pm
Posts: 1266
In the sound engine I wrote, I write $FF (or $C0, I forget) to $4017 on each call, because I use it to silence the triangle channel:
#$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.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 9 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: aquasnake and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group