[alsa-devel] Problem when using silence 'trick'
bryan at ischo.com
Thu Feb 24 09:11:12 CET 2011
Hi ALSA developers!
I am working on my first project that uses sound under Linux, and I've
chosen to implement directly to the ALSA API. I am amazed that I was
able to get sound working in so few lines of code with ALSA. I like the
API and its performance alot but I have a problem!
My program, early in its development, wanted to play just one sound
stream at a time; and it is a sound stream which normally delivers audio
at a sufficent rate to avoid XRUN but in some circumstances the thread
generating audio cannot keep up and it falls behind, resulting in XRUN.
Well I found that in cases where the audio thread is 'almost' keeping
up, the standard behavior of allowing an XRUN to occur, detecting it on
the next call to snd_pcm_writei, and 'fixing' it by calling
snd_pcm_prepare() and then proceeding to continue delivering audio
frames, had the following unfortunate problems:
1. The audio sounds crackly and awful (which is to be expected to some
degree with XRUN)
2. The time taken to re-prepare the ALSA device is counterproductive; it
takes even more time away from the thread that is delivering audio and
thus makes the case where the audio is very marginally behind 'real
time' much worse.
I thought, if only I could tell ALSA to just play silence if it has run
out of frames of audio instead of XRUNning, and then I can add real
frames of audio back in as soon as they are available and pick up the
audio stream where it left off.
Some digging into the ALSA API and I found that someone on the ALSA team
had already beaten me to the thought! The API docs kind of hint at how
you would go about doing this, and I found this technique to be very
1. Use snd_pcm_sw_params_set_stop_threshold to set the stop threshold to
0. Now, no XRUN - ALSA will keep looping in the circular buffer and
play whatever is there; and if you haven't updated quickly enough, you
just get looping snippets of audio.
2. Use snd_pcm_sw_params_set_silence_threshold to 0 and
snd_pcm_sw_params_set_silence_size to set the silence 'size' to the
boundary value obtained by calling snd_pcm_sw_params_get_boundary().
Now ALSA is instructed to fill the already-played portions of the
circular buffer with silence, which means that even when looping due to
XRUN, no sound is played over and over, instead silence is played.
Hey presto - works perfectly!
Then later on I needed to play more than one sound simultaneously. It
turns out that I had been opening the "plughw:0,0" device originally and
this had been working great with my sound trick. The problem is, you
cannot use plughw:0,0 and open it more than once to play more than one
sound simultaneously; the second open will fail.
So after some digging I found that the device I really should have been
using all along is called "default" (hey, you'd think it would be
obvious, but somehow I never found ALSA documentation that told me to do
Now the real problem: when I use the "default" device with my silence
technique, I have no problem playing multiple audio streams
simultaneously by opening the "default" device multiple times and
simultaneously playing streams into the different devices. BUT,
whenever I cannot deliver audio fast enough to a stream, and the silence
trick takes over and the stream plays some silence - boom, the device
goes silent forever. I don't get any errors from any ALSA function, I
just get silence no matter what I write into the device after that
point. This is in sharp contrast to the plughw:0,0 device, which does
play silence if I don't deliver audio fast enough but immediately plays
whatever audio I do end up delivering to it when I finally have audio to
Am I using an invalid technique with this silence trick?
Is this a known and expected behavior of the "default" device?
Or could something else be going on?
Thanks for any advice you can give!
More information about the Alsa-devel