sync to sound

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
sync to sound
by on (#14134)
I put this (maybe again) to ask about generated sound in the emu and how to sync it with dsound.

The thing is:
Im using 22.5KHZ, 8 bit per sample and mono for dsound output.. i mean the buffer that i create. I put it in this way in the post for simplicity since this could be 44.1 and 16 bit.

Well im using BCP (Before Cursor Position) method to fill the dsound buffer while it's looping.. i mean obtaining the playcursor every 10ms (as DS FAQ says) to avoid overhead, use a custom WriteCursor variable and obtaining the free bytes, but my problem is the following:

Im taking the output every 1789772.5 / 22050 = 81~ cpu cycles.

Taking that into accout the emulated buffer generates:

((341 * 262) / 3) / 81 = 367~ NES samples per frame.

But if the dsound playcursor increment (more or less) every 10ms and the code for runnnig an entire frame is (more or less again) < 3ms, it seems that we have to "wait" for the play cursor to advance 367 bytes so we can write to it.

In other words the NES generates samples faster than dsound play cursor advance.

What means that the emulator *must* be stopped to sync to dsound?
Re: sync to sound
by on (#14135)
Anes wrote:
Im taking the output every 1789772.5 / 22050 = 81~ cpu cycles.

Taking that into accout the emulated buffer generates:

((341 * 262) / 3) / 81 = 367~ NES samples per frame.

But if the dsound playcursor increment (more or less) every 10ms and the code for runnnig an entire frame is (more or less again) < 3ms, it seems that we have to "wait" for the play cursor to advance 367 bytes so we can write to it.

In other words the NES generates samples faster than dsound play cursor advance.

What means that the emulator *must* be stopped to sync to dsound?


Yes. If you generate your audio signal synchronized with CPU/PPU emulation and output it directly to DirectSound (with downsampling, of course), it will prevent your emulator from ever exceeding 100% speed. If you want the ability to exceed 100% speed (a 'turbo' function of some sort) without disabling sound, you'll need a more sophisticated buffer that can discard blocks instead of waiting for them to play.

Incidentally, you really shouldn't be dividing the audio into PPU frames, since video and audio "frames" on the NES are totally unrelated.

The problem with a 22050Hz sample rate is that it doesn't divide neatly by 60 (as well as the fact that higher frequencies will sound quite awful at that rate). These days, you shouldn't settle for anything less than 44100Hz - it divides evenly by 60 (which is a convenient number to use) and the audio will actually sound decent. 16-bit audio would also be useful, especially if you're going to be handling expansion sound.

by on (#14136)
You're dealing with the fundamental problem of two masters: the frame rate and sample rate. You're going to have to slave one to the other (work this out on paper until you can see that there's no way around this). It's easy to make the frame rate a slave to the sample rate, and much less noticeable than changes in sample rate. A simple method is to make the frame rate slightly higher than the corresponding sample rate (i.e. 61 FPS), then have your graphics wait until there is no more than a few frames of audio waiting to play before displaying a frame (otherwise you'll have the sound playing too late).

by on (#14137)
thanks very much for the info!!

by on (#14138)
blargg wrote:
You're dealing with the fundamental problem of two masters: the frame rate and sample rate. You're going to have to slave one to the other (work this out on paper until you can see that there's no way around this).


That's pretty much exactly what I do - make the frame rate depend on the sample rate, which should actually work out to ~60.0988 frames per second (1789772.7272727 cycles per second... / 29780.5 cycles per frame) on the NTSC NES.

by on (#14142)
A followup to Quietust's post: you'll just have to output one sample every [CPU cycles per second]/[samplerate] CPU cycle. In the case of 44100Hz, using integer maths for speed, for PAL it would be every 38+59/1232 cycle, and for NTSC every 40+45/77 cycle.

by on (#14173)
I don't like the tearing you get from not using vsync, so I make sure my frame rate is locked to 60Hz (block waiting for vsync, backed by a conventional timer in the emulation loop in case vertical refresh is >60Hz). For audio, I slightly adjust the sample rate based on how close the DirectSound play cursor is to the write cursor. The adjustment is small enough that you can rarely hear it (and, for me, only if I'm listening for it), but in ensures that the video frame rate stays locked at 60Hz without causing sound problems.

My refresh rate is locked at 60Hz (LCD display), so one missed vsync tends to cause sound buffer underflows. This probably isn't as big of an issue at higher refresh rates.

James

by on (#14179)
I sync to sound, as it runs the games at the correct speed, and resampling audio in realtime is a bitch. Use triple buffering, and one frame of every 600 is skipped. Or in interlace mode, 3 frames are shown twice of every 600. Yeah, it sucks, but oh well. At least there's no tearing.

by on (#14184)
I built my NES emu 'backend' so that it's able to work either way. The way I have it set up is the front supplies an audio buffer, as well as the size of that buffer, and whether or not it wants the whole buffer filled with audio, or if it just wants the audio that would be 'naturally' produced in that frame.

If the front specifies it only wants the natural audio output (like if it's syncing to sound), then the back simply runs for a frame and outputs the generated audio. Otherwise, if the back is to fill the entire buffer, once it runs for a frame, it continues to generate more audio samples until the buffer is filled. It's actually not that difficult with NES sound, since you're only dealing with simple repeating basic waveforms. When generating more audio, I just run things related to wave generation (Programmable timer, Duty Cycle Unit, Tri-Step Generator, etc) and leave all other areas (Length, Sweep, Linear, etc) untouched. That allows for sped up or slowed down sound without disrupting the pitch and will allow the game to run at any framerate without choppy sound.

The DMC complicates this, however. This doesn't work well with ROMs that use $4011... so games which stream data to it will end up sounding crackly or worse unless synced to sound. And stuff like blargg's sawtooth demo won't work right at all. I don't have a work-around for this with my method.

Also -- since running the DMC longer (or shorter) will disturb the rate of stolen cycles, and DMC IRQs, I had to actually have two seperate DMCs to emulate, each operating independently of each other -- one which actually produces the sound (which I can be lax with, and can be run more/less depending on the number of samples needed), and one which runs tied to the CPU (which is only responsible for stealing cycles and firing IRQs at the appropriate times).
Re: sync to sound
by on (#127799)
Excellent! What your discussed is just the problem I am thinking about.
Re: sync to sound
by on (#127809)
Boolean wrote:
Excellent! What your you've discussed is just the problem I am thinking about.
That's a mistake that a lot of English speaking people make. Well, your instead of you're they both sound the same. You've is you have. :)
Re: sync to sound
by on (#127817)
unregistered wrote:
Boolean wrote:
Excellent! What your you've discussed is just the problem I am thinking about.
That's a mistake that a lot of English speaking people make. Well, your instead of you're they both sound the same. You've is you have. :)

Thank you, my friend. :)
I know what you mean. A have+done stands for the Present Perfect Tense.
What your discussed is short for What your discussed thing.
Discuss is a verb. In most cases, a verb can be formatted with ed to become a adjective.
A adjective can stand for related things(equals a noun). I find I lost a verb. :oops:
I think a second way to correct it is
What you discussed ...(the Past Tense only)
Re: sync to sound
by on (#127830)
Boolean wrote:
In most cases, a verb can be formatted with ed to become an adjective.
An adjective can stand for related things(equals a noun). I find I lost a verb. :oops:
I think a second way to correct it is
What you discussed ...(the Past Tense only)

My friend, your extensive knowledge of all the ins and outs of English astounds me! :D In my head it just sounds more correct so I put that... all of the rules have kind of faded away for me, but the review is appreciated. Thank you again... you're doing awesome with English. :D Yes, "what you discussed" would work too.
Re: sync to sound
by on (#127841)
Thank you :)
unregistered wrote:
In my head it just sounds more correct so I put that...

I think the quoted text is the most important thing.
Re:
by on (#127956)
James wrote:
I don't like the tearing you get from not using vsync, so I make sure my frame rate is locked to 60Hz (block waiting for vsync, backed by a conventional timer in the emulation loop in case vertical refresh is >60Hz). For audio, I slightly adjust the sample rate based on how close the DirectSound play cursor is to the write cursor. The adjustment is small enough that you can rarely hear it (and, for me, only if I'm listening for it), but in ensures that the video frame rate stays locked at 60Hz without causing sound problems.

My refresh rate is locked at 60Hz (LCD display), so one missed vsync tends to cause sound buffer underflows. This probably isn't as big of an issue at higher refresh rates.

James


I have an odd solution to this. At the start of my game (though you could do this for an emulator I suppose) I run 15 frames locked to vsync, and divide the amount of time that takes by fifteen. If I find that the duration of a frame wasn't ~16.67ms, I then enable a timer. If it was 16.67ms, though, I determine that syncing to vsync is fine and just do that. This also solves the issue of terrible drivers like nouveau not properly implementing vsync.
Re: sync to sound
by on (#128969)
Boolean wrote:
Excellent! What your discussed is just the problem I am thinking about.


8 year-old thread bump. Is that a new nesdev record?
Re: sync to sound
by on (#128976)
WedNESday wrote:
Boolean wrote:
Excellent! What your discussed is just the problem I am thinking about.


8 year-old thread bump. Is that a new nesdev record?

Perhaps (with special attention toward the utterly pointless grammar discussion afterwards), but you deserve an honorable mention for bumping it again one month later.
Re: sync to sound
by on (#128984)
Quietust wrote:
WedNESday wrote:
Boolean wrote:
Excellent! What your discussed is just the problem I am thinking about.


8 year-old thread bump. Is that a new nesdev record?

Perhaps (with special attention toward the utterly pointless grammar discussion afterwards), but you deserve an honorable mention for bumping it again one month later.


I didn't realise! Just made my own day. :lol: