views:

415

answers:

3

I'm looking into making some software that makes the keyboard function like a piano (e.g., the user presses the 'W' key and the speakers play a D note). I'll probably be using OpenAL. I understand the basics of digital audio, but playing real-time audio in response to key presses poses some problems I'm having trouble solving.

Here is the problem: Let's say I have 10 audio buffers, and each buffer holds one second of audio data. If I have to fill buffers before they are played through the speakers, then I would would be filling buffers one or two seconds before they are played. That means that whenever the user tries to play a note, there will be a one or two second delay between pressing the key and the note being played.

How do you get around this problem? Do you just make the buffers as small as possible, and fill them as late as possible? Is there some trick that I am missing?

+4  A: 

Most software synthesizers don't use multiple buffers at all.

They just use one single, small ringbuffer that is constantly played.

A high priority thread will as often as possible check the current play-position and fill the free part (e.g. the part that has been played since the last time your thread was running) of the ringbuffer with sound data.

This will give you a constant latency that is only bound by the size of your ring-buffer and the output latency of your soundcard (usually not that much).

You can lower your latency even further:

In case of a new note to be played (e.g. the user has just pressed a key) you check the current play position within the ring-buffer, add some samples for safety, and then re-render the sound data with the new sound-settings applied.

This becomes tricky if you have time-based effects running (delay lines, reverb and so on), but it's doable. Just keep track of the last 10 states of your time based effects every millisecond or so. That'll make it possible to get back 10 milliseconds in time.

Nils Pipenbrinck
That makes sense. Can you recommend any audio libraries that would be good for this single ring buffer approach?
Tom Dalling
Yep. On Win32 it's directsound. Simle as that. For Linux i don't really know. Most probably ALSA. For a pure hardware solution (e.g. an embedded thing) you don't want any API at all but want your routine to be called from an interrupt handler..
Nils Pipenbrinck
A: 

PortAudio, and RtMIDI for cross platform development. wxWidgets or Qt for the graphical interface.

D. Casey Tucker
you talk absolute bollocks.
Pavan
+1  A: 

With the WinAPI, you can only get so far in terms of latency. Usually you can't get below 40-50ms which is quite nasty. The solution is to implement ASIO support in your app, and make the user run something like Asio4All in the background. This brings the latency down to 5ms but at a cost: other apps can't play sound at the same time.

I know this because I'm a FL Studio user.

Stefan Monov