Streaming Audio with OpenAL Buffer Queuing: A Practical Guide
Learn how to queue PCM buffers to an OpenAL source for gap‑free, low‑latency playback, including setup, loop, error checks, and recovery steps.
09 Jun 2026, 18:29 UTC

Desired Outcome
Play a continuous audio stream without audible gaps or clicks by queuing multiple PCM buffers to a single OpenAL source and replenishing them in real time.
Prerequisites
- An initialized OpenAL device and context (
alcOpenDevice,alcCreateContext). - At least one source generated with
alGenSources. - Audio data in PCM format (typically 16‑bit mono or stereo) ready to be uploaded with
alBufferData. - Knowledge of the desired sample rate (
SAMPLE_RATE) and buffer size in samples (BUFFER_SIZE). - A timing mechanism capable of invoking the update loop at roughly
SAMPLE_RATE / BUFFER_SIZEHz (e.g., a high‑resolution timer or audio callback).
Focused Procedure
- Create buffer objects
ALuint buffers[NUM_BUFFERS]; alGenBuffers(NUM_BUFFERS, buffers); - Fill initial buffers
for (int i = 0; i < NUM_BUFFERS; ++i) { void* pcmi = get_next_pcm_chunk(); // function you provide alBufferData(buffers[i], AL_FORMAT_STEREO16, pcmi, BUFFER_SIZE * 4, SAMPLE_RATE); // AL_FORMAT_STEREO16 assumes 16‑bit stereo; adjust for mono if needed } - Generate and configure a streaming source
ALuint source; alGenSources(1, &source); alSourcei(source, AL_LOOPING, AL_FALSE); // looping not used for queued streaming - Queue the initial buffers
alSourceQueueBuffers(source, NUM_BUFFERS, buffers); - Start playback
alSourcePlay(source); - Main loop (run at the audio update rate)
while (playing) { ALint processed = 0; alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed); while (processed-- > 0) { ALuint buf; alSourceUnqueueBuffers(source, 1, &buf); void* new_pcm = get_next_pcm_chunk(); // refill with fresh data if (new_pcm) { alBufferData(buf, AL_FORMAT_STEREO16, new_pcm, BUFFER_SIZE * 4, SAMPLE_RATE); alSourceQueueBuffers(source, 1, &buf); } else { // End of stream: optionally re‑queue a silence buffer or break alBufferData(buf, AL_FORMAT_STEREO16, silence_buf, BUFFER_SIZE * 4, SAMPLE_RATE); alSourceQueueBuffers(source, 1, &buf); } } // Optional: check for underrun ALint state; alGetSourcei(source, AL_SOURCE_STATE, &state); if (state != AL_PLAYING && state != AL_PAUSED) { // Source stopped because queue emptied – restart if more data is expected alSourcePlay(source); } // Error checking after each OpenAL call in production code ALenum err; while ((err = alGetError()) != AL_NO_ERROR) { // Log or handle error } // Sleep or wait until next update interval }
Expected Checks
- After every OpenAL function, verify
alGetError() == AL_NO_ERROR. - Confirm source state is
AL_PLAYING(orAL_PAUSED) while audio should be flowing. - Ensure the number of queued buffers never exceeds the source’s practical limit (determined by available memory; OpenAL does not expose a hard queue limit).
- Monitor
AL_BUFFERS_PROCESSEDto guarantee buffers are being returned faster than they are consumed.
Recovery Options
- Underrun (queue empties): If the source transitions to
AL_STOPPED, callalSourcePlayafter refilling at least one buffer. Consider increasingBUFFER_SIZE or the update loop frequency to reduce risk. - Persistent errors: Reset the source (
alSourceStop,alSourcei(source, AL_BUFFER, 0)) and recreate the buffer queue. - End of stream: Queue a silence buffer of the same format and let the source run until manually stopped, then call
alSourceStopandalSourceUnqueueBuffersto release all buffers.
Limitations and Practical Verification
Buffer size trades latency for CPU overhead: smaller buffers lower latency but increase the frequency of the update loop, raising CPU usage. Larger buffers reduce CPU load but increase the time before a change in the audio stream can be heard (e.g., when adjusting volume or applying effects).
To verify correct operation without claiming tested output:
- Compile a minimal program that loads a short PCM waveform, creates two buffers, queues them to a source, starts playback, and in the update loop refills buffers with silence while printing
AL_BUFFERS_PROCESSEDand the source state. - Run the program and listen for a continuous tone without clicks or gaps.
- Optionally capture the output with a waveform editor (e.g., Audacity) and inspect the waveform over several seconds for dropouts or glitches.
- Check that
alGetErrorreturnsAL_NO_ERRORafter each OpenAL call throughout the run.
If the tone remains smooth and the processed count increments steadily, the buffer queuing mechanism is functioning as specified.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.