This patch changes the file sound/core/oss/pcm_oss.c in the ALSA kernel modules. This patch needs to be applied against the kernel sources, not the sources that can be downloaded from www.alsa-project.org.
This patch causes the older OSS (Open Sound System) layer, that was added to ALSA for backwards compatibility, to restart a recording audio stream after it was stopped due to an overrun of the recording buffer. An overrun (also called xrun) occurs when the application doesn't read the opened audio device (i.e. "/dev/dsp") for a short time, causing the input buffer of the driver to run full. After a stream is stopped, subsequential data from the soundcard is ignored. When the application starts reading again however, the stream should be restarted; but this is not the case at the moment (the patch is offered to the ALSA development team at 13 July 2003).
You can test if your drivers are fixed by running the following test code:
testcode.c (download):
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/ioctl.h> #include <unistd.h> #include <sys/soundcard.h> #include <time.h> #include <stdlib.h> int main(void) { int fd; int res = 0x7fff0009; audio_buf_info info; int prev_size = 0; do { ++res; close(fd); fd = open("/dev/dsp", O_RDONLY); if (fd == -1) { perror("open"); exit(127); } if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &res) == -1) { perror("ioctl"); exit(127); } if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) == -1) { perror("read"); exit(127); } printf(" Allocated %d buffers of %d bytes.\n", info.fragstotal, info.fragsize); if (prev_size == info.fragsize * info.fragstotal) { printf(" It seems impossible to set a recording buffer with a\n" " total size of at least 8192 bytes. This is not going\n" " to work with ViaVoice. Sorry.\n"); exit(126); } prev_size = info.fragsize * info.fragstotal; } while (prev_size < 8192); printf(" Successfully allocated a buffer that is large enough.\n"); res = AFMT_S16_LE; if (ioctl(fd, SNDCTL_DSP_SETFMT, &res) == -1) { perror("ioctl"); exit(127); } res = 0; if (ioctl(fd, SNDCTL_DSP_STEREO, &res) == -1) { perror("ioctl"); exit(127); } res = 22050; if (ioctl(fd, SOUND_PCM_READ_RATE, &res) == -1) { perror("ioctl"); exit(127); } char buf[1024]; if (read(fd, buf, sizeof(buf)) < 0) { perror("read"); exit(127); } static struct timespec naptime = { 0, 100000000 }; int count = 0; do { if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) == -1) { perror("read"); exit(127); } printf(" Available bytes: %d\n", info.bytes); nanosleep(&naptime, 0); if (++count == 200) { printf(" Success: No overrun occurs.\n"); exit(0); } } while(info.bytes < info.fragsize * info.fragstotal); printf(" Successfully caused an xrun.\n"); printf(" non-blocking fragments: %d\n", info.fragments); printf(" non-blocking bytes: %d\n", info.bytes); ssize_t bufsize = info.bytes; if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) == -1) { perror("read"); exit(127); } printf(" Available bytes in buffer: %d\n", info.bytes); ssize_t trlen = 0; int nf = 0; for (;;) { if (info.fragments > 0) { ssize_t rlen; if ((rlen = read(fd, buf, sizeof(buf))) < 0) { perror("read"); exit(127); } printf(" Additionally read %d bytes.\n", rlen); trlen += rlen; if (trlen > bufsize) { printf(" Read %d bytes: stream successfully restarted.\n", trlen); break; } nf = 0; } else if (++nf > 10) { printf(" Stream is not restarted after xrun.\n"); exit(1); } if (ioctl(fd, SNDCTL_DSP_GETISPACE, &info) == -1) { perror("read"); exit(127); } } close(fd); return 0; }
To run this test download testcode.c and execute it. For example:
$ gcc testcode.c $ ./a.out Allocated 2 buffers of 1024 bytes. Allocated 2 buffers of 2048 bytes. Allocated 2 buffers of 4096 bytes. Successfully allocated a buffer that is large enough. Available bytes: 3072 Available bytes: 4704 Available bytes: 6336 Available bytes: 8000 Available bytes: 9664 Successfully caused an xrun. non-blocking fragments: 2 non-blocking bytes: 9664 Available bytes in buffer: 7232 Additionally read 1024 bytes. Additionally read 1024 bytes. Additionally read 1024 bytes. Additionally read 1024 bytes. Stream is not restarted after xrun.
Here is an example of how to apply the patch to your kernel. Please note that this only works if you use kernel modules for your sound. You also need at recent 2.5 development kernel of course (otherwise just use the OSS drivers, not ALSA).
This assumes you are already running the kernel 2.5.74 which you configured and compiled yourself:
$ su $ rmmod `lsmod | grep '^snd' | cut -d \ -f 1` $ cd /usr/src/linux-2.5.74 $ patch -p0 < /path/to/patch/ossfix.patch $ make modules $ RELEASE=`grep UTS_RELEASE include/linux/version.h | sed -e 's/[^"]*"\([^"]*\)"/\1/'` $ for f in `find sound -name '*.ko'`; do cp $f /lib/modules/$RELEASE/kernel/$f; done $ modprobe snd
Notes:
If you don't have any customized modules, then you can also
just run make modules_install
. Just keep in
mind that it will remove every modules that is not part of the
kernel.
The trick with the RELEASE
only works if you
actually compiled the kernel before (ie, did a
make bzImage
.
Make sure you don't run any sound applications that will
reload the sound modules before you can reload them.
If the last modprobe snd
says something like
"FATAL: Module snd already in kernel." then it's time to
kill sound applications and re-run the rmmod
line.
Now running the test program again will yield: