It is currently Wed Dec 13, 2017 9:32 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 10 posts ] 
Author Message
 Post subject: APU Sweep Test ROMs
PostPosted: Thu Mar 03, 2005 3:42 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
I wrote a couple more NES APU emulator test ROMs to check the sweep unit. It tests the upper silence cutoffs for each shift value, the lower cutoff, and the subtract mode. I might write a few more tests soon.

test_apu_sweep.zip

The archive contains two iNES ROMs, the asm source, and sound files of the output when run on a NES. Reply if you get a different result than expected.


Last edited by blargg on Fri Dec 31, 2010 7:02 am, edited 2 times in total.

Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 03, 2005 6:34 pm 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
both of them are working for me now... although I had to add a tweak to get sweep_cutoff to work.

For the most part it played as expected... only notes *very briefly* played before the noise signal (when you're not supposed to hear anything). I mean briefly as in it's only making 1 full square briefly -- it sounded mostly like a pop (or really several pops since it's happening on each test).

My previous method is I have a flag which indicates if the sweep unit is silencing the channel (1 if channel is silenced by sweep unit, 0 otherwise). I refreshed the value by checking the conditions on every $4001/2/3 write...as well as on sweep unit clocks.

The tweak I added makes it so the flag can only go from 1->0 on Sweep Unit clocks... meaning if the sweep unit is silenceing the channel... it will always be silent until at least the next sweep unit clock.

The tweak fixed sweep_cutoff.... but I'm not sure on it's accuracy. Could you verify for me? Is that what I'm supposed to be doing or am I doing something else wrong?


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 03, 2005 10:36 pm 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
Disch wrote:
My previous method is I have a flag which indicates if the sweep unit is silencing the channel (1 if channel is silenced by sweep unit, 0 otherwise). I refreshed the value by checking the conditions on every $4001/2/3 write...as well as on sweep unit clocks.


That seems fine. In the code below it would be equivalent to caching the value of is_silenced() and updating it at the end of clock_sweep() and write_4001(). In the recording of the NES there are slight clicks before the noise burst at the middle. My guess is that you aren't silencing until the period is set above 0x7ff, thus this doesn't happen until the next sweep clock; the correct behavior is to silence the channel if the shifter/adder's output is above 0x7ff, even before the sweep clock has occurred (see code below).

Quote:
The tweak I added makes it so the flag can only go from 1->0 on Sweep Unit clocks... meaning if the sweep unit is silenceing the channel... it will always be silent until at least the next sweep unit clock.


Definitely incorrect, since silencing has no relation to clocking. The only thing clocking does is drive the actual sweeping (adjusting the period in third and fourth registers).

Here's the code I use for sweep handling (which matches what is described in the APU reference). Note the comment in clock_sweep() about the different behavior of the second square channel.

Code:
int square_period;    // value in $4002/$4003
int sweep_period = 1; // must never be zero
int sweep_delay = 1;  // must never be zero
int sweep_shift;
bool sweep_enabled;
bool sweep_reload;
bool sweep_negate;

void clock_sweep()
{
    if ( --sweep_delay == 0 )
    {
        sweep_delay = sweep_period;
       
        if ( sweep_enabled && square_period >= 8 )
        {
            int offset = square_period >> sweep_shift;
            if ( sweep_negate ) {
                // no + 1 for second square channel
                square_period -= offset + 1;
            }
            else if ( square_period + offset < 0x800 ) {
                square_period += offset;
            }
        }
    }
   
    if ( sweep_reload )  {
        sweep_reload = false;
        sweep_delay = sweep_period;
    }
}

bool is_silenced()
{
    int offset = square_period >> sweep_shift;
    return square_period < 8 ||
            (!sweep_negate && square_period + offset >= 0x800);
}

void write_4001( int n )
{
    sweep_negate  = (n >> 3) & 1;
    sweep_shift   = n & 7;
    sweep_period  = ((n >> 4) & 7) + 1;
    sweep_enabled = (n & 0x80) && sweep_shift;
    sweep_reload = true;
}


Last edited by blargg on Fri Mar 04, 2005 2:31 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 03, 2005 11:41 pm 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
blargg wrote:
That seems fine. In the code below it would be equivalent to caching the value of is_silenced() and updating it at the end of clock_sweep() and write_4001(). In the recording of the NES there are slight clicks before the noise burst at the middle. My guess is that you aren't silencing until the period is set above 0x7ff, thus this doesn't happen until the next sweep clock; the correct behavior is to silence the channel if the shifter/adder's output is above 0x7ff, even before the sweep clock has occurred (see code below).


Nope... I'm doing it with a method similar to yours. Sample code below.

blargg wrote:
Definitely incorrect, since silencing has no relation to clocking. The only thing clocking does is drive the actual sweeping (adjusting the period in third and fourth registers).


K... that tweak removed (I didn't think it was proper).

code in my emu (edited for ease of read):

Code:
void CheckSweepForcedSilence()
{
  bSweepForceSilence = 0;
  if(nFreqTimer < 8)      bSweepForceSilence = 1;
  else if(!bSweepMode)
  {
    if((nFreqTimer + (nFreqTimer >> nSweepShift) & 0x800)
      bSweepForceSilence = 1;
  }
}

void write_4001( u8 v )
{
  RunAPU(nCPUCycle);
  bSweepEnabled =   (v & 0x80) && (v & 0x07);
  nSweepTimer =      (v & 0x70) >> 4;
  bSweepMode =       v & 0x08;
  nSweepShift =        v & 0x07;
  bSweepReset =       1;
  CheckSweepForcedSilence();
}

void clock_sweep()
{
  if(bSweepEnabled && !bSweepForceSilence)
  {
    if(nSweepCount)
      nSweepCount--;
    else
    {
      nSweepCount = nSweepTimer;
      if(bSweepMode)  nFreqTimer -= (nFreqTimer >> nSweepShift) + 1;  //no +1 on square 2
      else  nFreqTimer += (nFreqTimer >> nSweepShift);
      CheckSweepForceSilence();
    }
  }
  if(bSweepReset)
  {
    bSweepReset = 0;
    nSweepCount = nSweepTimer;
  }
}



If bSweepForceSilence is ever on, the channel is silenced and output is 0. CheckSweepForcedSilence() is also called at the end of 4002 and 4003 writes (whenever the frequency changes -- could this be the problem? perhaps it's only supposed to be checked on 4001 writes?)

I'm gonna look into what's going on more and see if I can't figure out my problem


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 04, 2005 1:00 am 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
Your code looks correct, and worked when I pasted it in my emulator and ran the cutoff test. As you mentioned, you should also call CheckSweepForcedSilence() after writes to $4002 and $4003.

If you post a sound sample (8-bit WAVE compresses best) I'll examine it.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 04, 2005 9:47 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 10:59 pm
Posts: 1393
Initially, my emulator was not producing proper results - the first test had one single sweep before the burst of noise (the rest was fine), and the 2nd test didn't play everything in the same pitch. FCE Ultra has similar problems, though the first test had even more noise at the beginning.

It turns out I was running the half-frame stuff (i.e. sweeps) on quarter-frames 1 and 3 rather than 0 and 2 - changing this immediately fixed the results on both tests.

Looking at the APU Frame Counter wiki page, the frame sequences seem wrong somehow - are these taking into account the fact that writing $4017 starts the 4-frame sequence after one quarter frame versus starting the 5-frame sequence immediately?

If you could come up with some proper tests to verify all of that behaviour, we emulator authors would greatly appreciate it.

_________________
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 04, 2005 11:49 pm 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
I figured out what's happening. In between register writes, bSweepForceSilence is briefly 0... so it outputs until it's shut off a few cycles later... creating a quick pop.

For example the demo does the following writes (status of bSweepForceSilence is in parenthesis):

A1 -> 4001 (1)
07 -> 4002 (1)
00 -> 4003 (1)
F1 -> 4002 (0) * here
07 -> 4003 (1)

The few short cycles between the 4002 write and 4003 write, the channel is outputting nonzero... creating the pop.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Mar 05, 2005 1:19 am 
Offline
User avatar

Joined: Mon Sep 27, 2004 8:33 am
Posts: 3715
Location: Central Texas, USA
Disch, sorry about the slight pops that the ROM makes. I looked at the asm earlier today and saw that it does create them because I'm setting the sweep shift a few cycles after changing the period; setting it before keeps the silencing in effect continuously. Maybe the pops are louder on your emulator due to the method of band-limiting you use (averaging). I'll make sure future sound test ROMs generate more unambiguous output.

Quietust, I would like to make comprehensive sound test ROMs that help find subtle sound issues, since they can have a significant effect on sound effect accuracy. Test ROMs would also serve to double-check the current understanding of APU operation.

I'd like to also make identical test NSFs for players, but making the tests into a routine that gets called 60 times a second and returns each time will be more difficult. I'm not sure if NSF players can handle an NSF whose init routine never returns. Disch, do you know of any NSFs that never return from their init or play routines, just sit there in a loop handling timing themselves?

Regarding the frame counter, I've edited the NesDevWiki page to be (hopefully) clearer about the delay for the 4-step sequence and lack of one for the 5-step sequence. In the side effects for a write to $4017, I rewrote it to "The sequencer is restarted at step 1 of the selected mode. If mode is 1 the sequencer is then clocked, causing the first step to be carried out immediately. Finally, the divider is reloaded, resulting in a 1/240 second delay before the sequencer is next clocked."


Top
 Profile  
 
 Post subject:
PostPosted: Sat Mar 05, 2005 2:03 am 
Offline
User avatar

Joined: Wed Nov 10, 2004 6:47 pm
Posts: 1845
blargg wrote:
Disch, sorry about the slight pops that the ROM makes. I looked at the asm earlier today and saw that it does create them because I'm setting the sweep shift a few cycles after changing the period; setting it before keeps the silencing in effect continuously. Maybe the pops are louder on your emulator due to the method of band-limiting you use (averaging). I'll make sure future sound test ROMs generate more unambiguous output.


I've actually implimented a a method based on your BL-synth docs (the whole sine wave transition buffer thing). However the pops are a lot more significant than they should be -- the up transition and down transition are waaaaay further apart than just a few cycles. There must be a slight problem with my sound timing somewhere.. I'm still looking into it.

Quote:
Disch, do you know of any NSFs that never return from their init or play routines, just sit there in a loop handling timing themselves?


Not very many. The only ones I know of (that intentionally don't return from the Play routine) are ones that do timed $4011 writes... and even then it's only a few tracks on the whole NSF. First one that comes to mind is Mitokoumon. Xod did that TMNT theme song dealie with an NSF as well (did the whole TMNT cartoon theme song through $4011 streaming). I think Blades of Steel has a few tracks too...


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 25, 2005 6:39 pm 
Offline
Formerly Fx3
User avatar

Joined: Fri Nov 12, 2004 4:59 pm
Posts: 3076
Location: Brazil
Quietust wrote:
Looking at the APU Frame Counter wiki page, the frame sequences seem wrong somehow - are these taking into account the fact that writing $4017 starts the 4-frame sequence after one quarter frame versus starting the 5-frame sequence immediately?


Well, the wiki is still outdated (missing the information above).

_________________
Zepper
RockNES developer


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 1 guest


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