- Fine, I've figured out what's wrong with the sound updates in my emulator. The PPU (graphics) updates the screen 60 times per second. The pAPU (sound) is a bit longer. After the PPU frame sync, the sound is updated a bit later, and an infinity 'while' loop is used to poll the audiostream, in order to transfer the next amount of samples. It causes the emulation to desync after a few moments. By the way, it's noticeable only when the emulator uses a stretched mode (heavy).
- I'm unsure about how to define 'frame' for the sound unit, but after reading most of sound topics, there's a number: 29830 CPU cycles, against 29780,67 of the PPU frame time.
Right, the APU frame counter in mode $4017=$00 cycles through every 29830 CPU clocks. But why does this matter for sound generation? Overall timing should be based on PPU frames, with the APU simply running however many CPU clocks that frame happened to be. There's no reason for the APU's "frames" (which have no relation to PPU frames) to play a part in any overall timing.
blargg wrote:
But why does this matter for sound generation?
- I
probably used this value as reference for my own pAPU debugging. Anyway, it lies on wave resampling, as 29830*60 cycles per second DIV audiocard_samplerate, resulting the number of CPU cycles per sample. Yes, that number is not used anywhere in the code, except for the calculation I mentioned.
- As quick question, the correct reference for the NES sound unit is APU or pAPU (p=pseudo)?
There could be if the emulation loop is regulated against the sound output, and then video output is also stalling things with vsync locking. This could also be a problem with using WaitForVerticalBlank, or OpenGL swap interval control, depending on the driver implementation. At least with my ATI Radeon 9800 using recent drivers, either of those will saturate the CPU while polling for the blanking interval. The Direct3D synced presentation mode doesn't seem to do that, but it does appear to limit updates to a maximum of one per interval.
So I guess in that case it would be a good idea to employ smarter buffering. I need to do the same with my interface code eventually, since it currently stalls completely when the output buffer is full, and disables vsync when the buffer empties below a certain threshold. Needs a more careful balance of low latency buffering and frame display timing.
Of course, this could simply use the vsync locking, but then it would absolutely require a 60fps display mode. Or it could use a timer, but most Windows (and maybe Linux, or whatever) systems only have a timer precision of 1ms, which is slightly off for the ~16.683ms needed for 59.94/60/such fps. Or maybe I'm overthinking this and that difference is just not noticeable.
Uh... I'm using the Allegro library. ^_^;;
Fx3 wrote:
kode54 wrote:
There could be if the emulation loop is regulated against the sound output, and then video output is also stalling things with vsync locking. This could also be a problem with using WaitForVerticalBlank
Uh... I'm using the Allegro library. ^_^;;
The only API-specific thing I saw in kode54's comment was WaitForVerticalBlank, which in Allegro is called vsync(). The gist is that if your sound code is about to fall behind, don't vsync() the next frame.
I don't call vsync, I call rest(0) inside an infinit loop.