Managing Audio Streaming with OpenAL Buffer Queuing
Avoid memory bloat in OpenAL by using buffer queuing to stream large audio files. Learn how to implement a rotating buffer system to maintain a constant memory footprint.
27 Feb 2026, 05:31 UTC

The Problem: Memory Exhaustion with Large Audio Files
Loading a five-minute high-fidelity WAV file entirely into memory using alBufferData can consume dozens of megabytes per sound. In a game or application with multiple long ambient tracks or voice lines, this approach quickly leads to memory exhaustion or long initial load times that frustrate users.
The solution is buffer queuing. Instead of one giant buffer, you use a small set of rotating buffers (typically three) and stream data from the disk or a network socket in real-time. This keeps the memory footprint constant regardless of the audio file's length.
How Buffer Queuing Works
OpenAL handles streaming through a queue system associated with a source. You "queue" several buffers to the source; as the hardware finishes playing one, it marks it as processed. Your application must then "unqueue" that processed buffer, refill it with the next chunk of audio data, and queue it back to the end of the line.
This creates a circular pipeline. If the application fails to refill the buffers faster than the hardware plays them, the source will enter an AL_STOPPED state, resulting in an audible gap or "stutter."
Worked Example: Implementing a Streaming Loop
This example demonstrates the logic required to maintain a streaming source. It assumes you have an initialized OpenAL context and a function getNextChunk() that reads PCM data from a file.
/* Configuration */
#define NUM_BUFFERS 3
#define BUFFER_SIZE 4096 * 4
ALuint buffers[NUM_BUFFERS];
ALuint source;
/* Initialization */
alGenBuffers(NUM_BUFFERS, buffers);
alGenSources(1, &source);
/* Initial fill: Queue all buffers before playing */
for (int i = 0; i < NUM_BUFFERS; i++) {
void* data = getNextChunk();
alBufferData(buffers[i], AL_FORMAT_MONO16, data, BUFFER_SIZE, 44100);
alSourceQueueBuffers(source, 1, &buffers[i]);
}
alSourcePlay(source);
/* The Update Loop: Run this frequently (e.g., every frame) */
void updateStreaming() {
ALint processed;
alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed);
while (processed--) {
ALuint buffer;
/* Remove the played buffer from the queue */
alSourceUnqueueBuffers(source, 1, &buffer);
/* Refill with new data */
void* data = getNextChunk();
if (data) {
alBufferData(buffer, AL_FORMAT_MONO16, data, BUFFER_SIZE, 44100);
alSourceQueueBuffers(source, 1, &buffer);
} else {
/* End of file reached */
}
}
/* Recovery: If the source stopped due to buffer underrun, restart it */
ALint state;
alGetSourcei(source, AL_SOURCE_STATE, &state);
if (state == AL_STOPPED) {
alSourcePlay(source);
}
}
Execution Details: Run this in a C/C++ environment linked with -lopenal. The updateStreaming function must be called on the same thread that owns the OpenAL context, as contexts are not inherently thread-safe. Required permissions are standard user-level audio device access.
Trade-offs and Limitations
- CPU Overhead: Frequent calls to
alSourceUnqueueBuffersandalBufferDataincrease CPU overhead compared to static buffers. - Latency: The larger the buffer size, the lower the CPU load, but the higher the latency for changes (like stopping the sound instantly or changing pitch).
- Disk I/O: Streaming relies on consistent disk read speeds. If the OS hangs on I/O, the audio will stutter unless you implement a secondary software-side ring buffer.
- Format Constraints: Core OpenAL 1.1 is limited to 16-bit PCM. If you need floating-point streaming, you must verify
AL_EXT_float_buffersupport at runtime.
Practical Verification
To verify your streaming implementation is working without underruns, monitor the AL_BUFFERS_PROCESSED value. If it ever reaches NUM_BUFFERS, your application is not refilling the queue fast enough. You can also test the recovery logic by artificially introducing a sleep() call in the update loop to trigger an AL_STOPPED state and ensure the audio resumes seamlessly.
Actionable Closing
When implementing audio streaming in OpenAL:
- Use at least three buffers to provide a safety margin against OS scheduling jitters.
- Always check
AL_SOURCE_STATEto restart playback if a buffer underrun occurs. - Keep the update loop on the main audio thread to avoid context concurrency issues.
- Scale your
BUFFER_SIZEbased on the target hardware's disk latency—smaller for SSDs, larger for HDDs.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.