OpenAL Streaming Glitches: Diagnosing Source Buffer Underruns
Intermittent crackles in OpenAL streaming usually mean buffer starvation. Check AL_BUFFERS_PROCESSED and AL_QUEUED_BUFFERS, match buffer size to thread latency, and know when to escalate beyond buffer tuning.
17 Oct 2025, 08:38 UTC

The problem you hear is buffer starvation, not a bad file
Intermittent crackles, pops, or brief silences during OpenAL streaming playback with a source that reports AL_PLAYING is the classic sign of a buffer underrun. The useful takeaway: if buffers are not being re-queued fast enough, the OpenAL mixer runs out of audio data for a few frames and you hear a glitch even though the source state looks correct.
OpenAL streaming works by queuing several OpenAL buffers to a source and refilling buffers that the mixer has finished. AL_BUFFERS_PROCESSED is the number of buffers the mixer has consumed, AL_QUEUED_BUFFERS is the number still waiting. Healthy streaming shows a steady turnover of processed buffers.
Recognizable condition
- Intermittent crackles or pops, or brief silent gaps, during continuous playback.
- alGetSourcei(source, AL_SOURCE_STATE) returns AL_PLAYING but audible output drops for frames.
- Glitches worsen under CPU load or when the app does work on the main thread.
Cause and diagnostic table
| Observed symptom | Likely cause | Diagnostic clue |
|---|---|---|
| AL_PLAYING, AL_BUFFERS_PROCESSED stays 0 or grows slowly | Source starved, buffers not re-queued | AL_QUEUED_BUFFERS drops to 0 or 1 |
| Regular pops at fixed intervals | Buffer size too small for thread latency | Small buffer e.g. 512 samples with high jitter |
| Glitches under load only | High CPU load or thread preemption delays refill | Refill callback latency spikes |
| First second silent then plays | alSourcePlay called before enough buffers queued | Play started with < 2 buffers queued |
| Silence that matches distance | Listener/source attenuation misinterpreted | AL_SOURCE_RELATIVE and distance model active |
Ordered checks
Check buffer turnover. In your audio update loop, run:
ALint processed, queued; alGetSourcei(source, AL_BUFFERS_PROCESSED, &processed); alGetSourcei(source, AL_QUEUED_BUFFERS, &queued);Run this where your game loop updates audio, typically on the main thread or a dedicated audio thread with no special permissions required. Expect processed > 0 over time when streaming. If processed stays 0 while queued falls, the source is starved.
Verify source state vs starvation. Query AL_SOURCE_STATE. If state is AL_PLAYING and processed == 0 for multiple frames, refill is not keeping up.
Inspect buffer size and count. Note the size used in alBufferData and the number of buffers you cycle. Common streaming setups use 2-4 buffers. Example configuration: 3 buffers of 2048 samples at 48 kHz is about 128 ms total queued audio. Smaller sizes reduce latency but increase refill frequency.
Measure refill latency. Around your refill code, record a high-resolution timestamp before dequeuing processed buffers with alSourceUnqueueBuffers and after enqueuing new data with alSourceQueueBuffers. Large variance indicates scheduling problems. This measurement is for diagnostics only and adds minimal overhead in a debug build.
Confirm play start order. Ensure you queue at least two buffers before calling alSourcePlay. Starting playback with one buffer invites immediate underrun.
Enable error checking. Call alGetError after each critical OpenAL call in debug builds. AL_INVALID_OPERATION often means an invalid buffer ID or using a deleted buffer. AL_OUT_OF_MEMORY indicates allocation failure.
Fixes tied to findings
Buffers starved, processed not re-queued
Increase queued buffers to 3-4 and/or enlarge each buffer, e.g. from 512 to 2048 samples. This raises the time the mixer can run without new data. Risk: higher latency, which may be unacceptable for interactive cues.
Refill thread latency high
Move refill work off the main thread to a dedicated audio thread and raise its scheduling priority if your platform allows. Avoid real-time priorities that can starve system threads. Test on target hardware.
Source started too early
Restructure initialization: fill buffers, call alSourceQueueBuffers for at least two buffers, then call alSourcePlay. Do not call alSourcePlay before queuing.
API misuse errors
If alGetError returns AL_INVALID_OPERATION, verify buffer IDs are valid and not deleted while queued. Ensure the source is not paused or stopped while you queue.
Limitations and verification
Increasing buffer size or count raises latency. For games, keep total queued audio under roughly 200 ms if responsiveness matters.
To verify a fix, run with a deterministic source such as a looping sine wave and vary CPU load by spawning background work. Log AL_BUFFERS_PROCESSED and AL_QUEUED_BUFFERS each frame in a debug build. A steady non-zero processed count and stable queued count indicates healthy streaming. Confirm a clean error log over the test run by checking alGetError after critical calls.
Escalation criteria
- Glitches persist after 4 buffers at 4096 samples with a dedicated high-priority audio thread.
- Repeated AL_INVALID_OPERATION errors despite correct queue order and valid IDs.
- Suspected platform driver issues, e.g., outdated vendor OpenAL driver. Test with OpenAL Soft and compare behavior.
If escalation is needed, capture buffer turnover logs, thread latency measurements, and the exact OpenAL version and driver used. Consider an alternative audio backend for the affected platform.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.