[BUG?] audio cracking with SBLive, ALSA, and SDL

Hello,

I’m hearing a peculiar crackling problem with all my programs that use
SDL. Everything is fine until one of the following happens:

  • the program gets bogged down (in I/O, for example)
  • I change the current virtual terminal
  • I suspend the program momentarily and then resume it

Once the cracking start, it stays until the program is restarted.

I’ve been hearing this problem for a while, and I’m afraid I can’t
remember all the changes in kernel or userspace since everything was
last ok. I tried to narrow it down and discovered that it only happens
with SDL’s ALSA backend, and only with my SBLive card; my motherboard
has a built-in nVidia CK8S audio chipset, and if I modify my ~/.asoundrc
to use that card, it sounds ok. To summarize:

SBLive + ALSA + SDL: crackles
SBLive + ALSA: works fine
SBLive + OSS (emulation) + SDL: works fine
CK8S + ALSA + SDL: works fine

At this point, I really can’t tell where the bug lies, but I’m hoping
somebody here might be able to point me in the right direction.

The cracking itself is a series of regularly-spaced clicks (about
32/second); when the sound is quiet, the clicks are quiet; when the
sound is loud, the clicks are loud. Left and right channels click
independently. Here’s a sample clip where I suspend and then resume
mplayer after approximately 6 seconds:

I’m running Debian Sid on an Athlon64 3400+, with a 64-bit kernel. This
happens with both 64-bit and 32-bit userspace. I can reproduce the
problem with Debian’s packaged SDL or with SDL I compiled myself.
1.2.13, 1.2.12, 1.2.11, and 1.2.10 all show the same problem; older
versions don’t compile for me.

I should note that I’m fairly sure this isn’t due to SDL bug 649:
http://bugzilla.libsdl.org/show_bug.cgi?id=649

Compiling SDL with --disable-assembly makes no difference.

Does this sort of problem sound familiar to anyone? I can file a bug
report if that would be preferable, but I don’t even know if this is an
SDL problem yet… I’d appreciate any help or ideas.

$ uname -a
Linux bugfood 2.6.28.2 #1 Tue Jan 27 18:28:48 PST 2009 x86_64 GNU/Linux

$ lspci | grep EMU
02:0b.0 Multimedia audio controller: Creative Labs SB Live! EMU10k1 (rev 08)

Thanks,
Corey

Hello, Corey!

CH> I’ve been hearing this problem for a while, and I’m afraid I can’t
CH> remember all the changes in kernel or userspace since everything was
CH> last ok. I tried to narrow it down and discovered that it only happens
CH> with SDL’s ALSA backend, and only with my SBLive card; my motherboard
CH> has a built-in nVidia CK8S audio chipset, and if I modify my
CH> ~/.asoundrc to use that card, it sounds ok. To summarize:

I had similar problem with SDL and QSA (QNX Sound Architecture) which was
based on ALSA 0.5.x but its API now a bit differs from ALSA’s. Each ALSA
sound driver gives to ALSA core default preferred fragment and buffer size,
less buffer and fragment size causes realtime (low latency) sound output,
but if some process will get more CPU time or has higher priority than sound
thread in the SDL, SDL will not able to provide new PCM data block to ALSA
and this will stop audio playback (PCM buffer underrun). After that sound
thread execution is resumed, this provides very fast sequence of stop/start
playback calls, the silence interval between stop and start causes sound
crackles.

This is why SDL has ThreadInit() call in the SDL’s audio driver
architecture. In QSA I do the following (QNX only code):

static void QSA_ThreadInit(_THIS)
{
struct sched_param param;
int status;

/* Increase default 10 priority to 25 to avoid jerky sound */
status=SchedGet(0, 0, &param);
param.sched_priority=param.sched_curpriority + 15;
status=SchedSet(0, 0, SCHED_NOCHANGE, &param);
}

Even small increase in sound thread priority can help to avoid "crackles"
effect. As far as I know, nor ALSA nor OSS do not use ThreadInit() callback
to increase sound thread priority. Since sound thread almost all time sleeps
(99.99%) on awaiting for free place to write new PCM data it is safe to
increase sound thread priority to any higher than other processes value.

I hope this helps.

With best regards, Mike Gorchak. E-mail: @Mike_Gorchak

This is a long standing SDL bug… I think.

You can get around it by tweaking the buffer sizes.

cu,

Mike Gorchak wrote:

Hello, Corey!

CH> I’ve been hearing this problem for a while, and I’m afraid I can’t
CH> remember all the changes in kernel or userspace since everything was
CH> last ok. I tried to narrow it down and discovered that it only happens
CH> with SDL’s ALSA backend, and only with my SBLive card; my motherboard
CH> has a built-in nVidia CK8S audio chipset, and if I modify my
CH> ~/.asoundrc to use that card, it sounds ok. To summarize:

I had similar problem with SDL and QSA (QNX Sound Architecture) which was
based on ALSA 0.5.x but its API now a bit differs from ALSA’s. Each ALSA
sound driver gives to ALSA core default preferred fragment and buffer size,
less buffer and fragment size causes realtime (low latency) sound output,
but if some process will get more CPU time or has higher priority than sound
thread in the SDL, SDL will not able to provide new PCM data block to ALSA
and this will stop audio playback (PCM buffer underrun). After that sound
thread execution is resumed, this provides very fast sequence of stop/start
playback calls, the silence interval between stop and start causes sound
crackles.

That makes sense. I had a feeling it was a buffer underrun problem, but
I didn’t know where to look.

The apparent trouble, though, is that after a buffer underrun the audio
keeps clicking. In the sample I posted, there ought to be an underrun at
about 6 seconds (because I suspended the mplayer process for 1 second),
but after that mplayer has all the CPU time it needs, so there shouldn’t
be further underruns. I put a debug printf where SDL_alsa_audio calls
snd_pcm_prepare() to “reset” the sound card; I see only one reset after
I resume mplayer, so apparently the clicking I hear isn’t caused by a
series of underruns–instead, a single underrun leaves something in an
incorrect state.

The other combinations I referred to in my original post (CK8S + ALSA +
SDL, etc.) all recover from the underrun and resume playback without any
trouble. Only SBLive + ALSA + SDL keeps clicking.

I found some commented out debug code in SDL_alsa_audio.c; apparently
SDL is using 2 fragments of 1024 samples each (those numbers are the
same for both my SBLive and CK8S). I increased the number of fragments
to 3, and that “fixed” the problem.

I still don’t know if this is an SDL bug or if SDL is just revealing a
bug in ALSA. I tried hacking mplayer’s ALSA output driver (direct ALSA,
not SDL–>ALSA) to use 2 fragments * 1024 samples, but that still worked
ok. There are many other differences between mplayer’s ALSA driver and
SDL’s ALSA driver, though, so that doesn’t mean much.

Here’s something interesting. I just put a printf in SDL_alsa_audio.c
after the call to snd_pcm_writei(). This is in a while() loop that keeps
attempting to write a buffer full of samples until the buffer is empty.

fprintf(stderr, “tried to write %d, wrote %d\n”, sample_len, status);

Here’s the output:

tried to write 1024, wrote 32
tried to write 992, wrote 52
tried to write 940, wrote 52
tried to write 888, wrote 52

(it keeps counting down; I’ll skip some lines for brevity)

tried to write 361, wrote 52
tried to write 309, wrote 50
tried to write 259, wrote 1
tried to write 258, wrote 51
tried to write 207, wrote 1

(here I suspend and then resume mplayer)

tried to write 206, wrote 206
tried to write 1024, wrote 1024
tried to write 1024, wrote 1024
tried to write 1024, wrote 1024
tried to write 1024, wrote 1024
tried to write 1024, wrote 1024
tried to write 1024, wrote 1024
tried to write 1024, wrote 1024
tried to write 1024, wrote 1024
tried to write 1024, wrote 1024
tried to write 1024, wrote 1024
tried to write 1024, wrote 1024

(etc.)

I don’t know what to make of that. Apparently ALSA is writing the entire
buffer every time, or at least claiming to.

I hope this helps.

It certainly does–I haven’t fixed the problem, but your message taught
me enough to be able to investigate more.

Thanks,
Corey