> Why is the application audio buffer necessary?
It's more about the APIs used. With something like OSS, you just write samples to a (virtualized to many devices) /dev/dsp handle. So you have to take your background music, and each sound effect channel, and mix the values together.
But if you're writing a PC game on Windows, you'd open separate IXAudio2SourceVoice handles for each, and I'm presuming that XAudio2 will handle all the mixing and when to optimally push samples to the hardware for you. You're working at a higher level here when you can provide the hardware/OS audio API with more information.
But an emulator just gets a solid stream of pre-mixed samples. Sometimes the game software have their own mixers (like Tales of Phantasia on the SNES), and these tend to fall back on game console hardware mixers, that then pass a rendered stream of audio out to the speakers (hence, indirection.)
With audio, how you handle it depends on the backend API. With DirectSound and WASAPI exclusive, it acts like a ring buffer in RAM that loops around and you poll the cursor (sample) position to know when and where to write. With XAudio2 and WASAPI shared, you build up tiny blocks of samples and throw them at the sound API. In both cases, even at 44KHz, it's not a smart idea to do this for every single sample output. So you're going to have a software queue that accumulates these samples. And the size of these queues aren't just about minimizing calls to the sound API, but it's also about the API's own precision and at what resolution it can report when a pushed buffer is consumed or where the cursor is at. This varies wildly between PCs. So the best you can really do is offer the user a latency selection and let them see how low they can set the value without audio distortion from misses.
A final complexity with emulation is that we can still have multiple audio streams. When you plug a Super Game Boy cart into an SNES, or a CD attachment onto a Genesis; the emulation of these usually results in two streams.
As mentioned in the article, generally you can merge two streams in software in near-realtime. Presuming both sound sources run at the same frequency, you only need one sample queued from both sources before you can mix them and send them to your sound API output buffer. Though keep in mind we are making a parallel process serial, and the context switches between components is the biggest source of slowdown in all of emulation, so sometimes you'll have quite a few samples from one component queuing up before we get any from another. It gets a bit fancier when they vary frequencies (and they do in emulation) ... and then you get to pull in anti-aliasing, resampling audio filters (I use butterworth biquad type-2 IIR with cubic resampling in higan), so the queues have to get a little tiny bit larger.
On anti-aliasing, that's yet more fun ... a lot of these old game consoles produced sound in the megahertz range, so you have to strip out the frequencies above the Nyquist limit to prevent aliasing (annoying buzzing) when you resample it to rates PCs can handle (in the kilohertz range.)