Fx3 wrote:
The basic thing is...
Code:
do {
sound_buffer = (unsigned short *)get_audio_stream_buffer(sound_stream);
// rest(0);
} while(!sound_buffer);
memcpy(sound_buffer, buffer, bytes_per_sample);
free_audio_stream_buffer(sound_stream);
What happens? It always copies
the same amount of bytes, as written in the Allegro docs, and the stream buffer size "must be" a power of two, like 2048 bytes. Here, once the buffer is full, this "infinity" loop is called to update the sound. That's what Allegro's docs teach you.
In other words, even if I specify a large buffer based on 'frames' (44100/60 = 735 samples per sync), there's no way to send less or more buffers AFAIK.
*bump*The things you said about the timer looks correct, but two answers were given. Anyway, it would work for a program that LACKS sound. Plus, I already tried out to call
get_audio_stream_buffer per frames, but it didn't work for the previous reasons... -_-;;;
Yes, that's why you have to use a framebuffer (or as someone else above said, a ring buffer). You'll never be able to output the same exact number of samples that the audio output API wants (almost all APIs are like this) - especially because the NES doesn't actually generate 735 samples per frame. It's more like a sequence of 734, 733, 733, 733 or something like that every 4 frames, and almost all audio output APIs HATE this kind of stuff - emulation is largely incompatible with PCs in general. That's why you have to buffer it and always just send what's in the buffer, even if you send no data or the same data twice.
Anywho, threads was my answer to the infinity loop problem. You can't really do that (the code example you provided) because you'll cause some massive drift between any other timers and the sound code (the rates are not perfect - even the docs point this out in more than one place). Things like vsync have this exact same problem, too. That's why it's just best to run sound and video output in a separate thread so they can be completely separated from the emulation timing and tuned exactly to what the OS wants, or something much closer at the very least. Anything that requires a "hard" polling loop (meaning it doesn't return until finished - e.g, audio output, vsync, netplay sync) cannot be done alongside the emulation without inducing moderate to severe timer drift over time.
This is how almost all media players, games, etc. work on every modern OS. Emulators shouldn't be any different, although due to simultaneous audio/video output *on top* of a video("main") timing (post-60Hz for NTSC NES) that doesn't exactly match up to anything a monitor can do without a refresh rate hack, means you're going to have a lot of problems trying to do everything from the same linear code layout. It's just too many things to time correctly, and too many hard loops and too much timeslice yielding (via Allegro's rest() function) to account for the massive amounts of drift that can occur.
Consider this situation:
Code:
printf ("Current video frames to emulate: %d\n", framesToRender);
printf ("Waiting for audio buffer...");
while (!(void *buffer = get_audio_stream_buffer (stream)))
rest (0)
printf (" done!\n");
printf ("Current video frames to emulate: %d\n", framesToRender);
Might produce the output:
Code:
Current video frames to emulate: 1
Waiting for audio buffer... done!
Current video frames to emulate: 3
You now have to skip several frames of video to catch up and maintain the NES's logic rate. Ugh.
Another thing that will help is to think of the output system in samples, not frames. There's no real reason to send entire frames of NES audio (or even whole frames that matter). This solves the sample count problem. I'm sorry for using the word 'frames' in my original post, and I figured I probably should've gone back and corrected it to 'samples'. Oh well. ^^;;
Fx3 wrote:
Additionally, I don't do 'native' Windows programming code (C++). To fit the things into Win32, I use that Allegro hack, END_OF_MAIN. Code is compiled with MinGW32, binaries *are* for Windows, but I let Allegro create the program window and GUI.
I realize this. I never do native programming either. That's why there's
pthreads, which works on every platform (even DOS, I do believe). This is the Win32 version. =)
wark on.