On 15 Oct 2001, Josh Green wrote: > On Mon, 2001-10-15 at 03:22, Josh Green wrote: > > > > Sure, I'll attach it to this email. I've been testing it for a while now > > and I'm realizing that the problem hasn't completely gone away. Since > > the start/prepare operations happen in sync, each time an xrun occurs > > the two devices are resynced. If I start it as root (I'm setting > > SCHED_FIFO currently) the test will rarely get xruns. For the most part > > the test runs smooth, but every once in a while little pops and clicks > > will be heard in series. They increase in # and then decrease and > > dissapear again (in a periodic fashion, sometimes sounds like a > > scratched vinyl record). This is the same problem, just a lot harder to > > reproduce, since it seems the two buffers are much more syncronized. > > > > Just wanted to add that my XRUN detection is rather messed up right now, > and I've already locked up my machine once (stuck in loop with > SCHED_FIFO @:(), so you probably want to run it as non root. Not sure > whats wrong with the way I'm detecting XRUNS, but if I just do a > snd_pcm_prepare, the output stream starts returning > "SND_PCM_STATE_PREPARE" until I restart it. I'll have to look into it > more, but if you have any ideas, please let me know.
There are many thinkos in your code. I found these bugs: 1) initial write: the ring buffer should contain data for two periods before the streams are started 2) xrun recovery: if you call snd_pcm_prepare, you have to call snd_pcm_start, too; otherwise the state will be SND_PCM_STATE_PREPARED 3) count of written / read frames is invalid for i/o operations (you're using count in bytes) 4) poll is not synchronized as you think; it's the main problem causing scratchy sound; you must always preserve the order of read/write i/o calls; please, look to 'r' variable and for "unsync" messages in my code I've attached my test code (heavy modified) which shows or fixes all above bugs. Have a clear sound, Jaroslav ----- Jaroslav Kysela <[EMAIL PROTECTED]> SuSE Linux http://www.suse.com ALSA project http://www.alsa-project.org
#include <stdio.h> #include <alsa/asoundlib.h> #include <string.h> #include <sched.h> #define OK 0 #define FAIL 1 char *audiobuf = NULL; int audiobuf_bytes = 0; snd_pcm_t *cards[2] = { NULL }; int fdcount = 0; struct pollfd *ufds = NULL; int setup_device (int index, char *device, int read) { int err; snd_pcm_t *handle; snd_pcm_hw_params_t *params; /* Hardware parameters */ snd_pcm_sw_params_t *swparams; /* Software parameters */ int period_size; /* Number of frames in period */ int period_bytes; /* size of period */ int buffer_size; /* size of entire audio buffer */ unsigned int rate; snd_pcm_uframes_t start_threshold, stop_threshold; int count; /* Open output sound card */ if (err = snd_pcm_open (&handle, device, read ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0) { fprintf (stderr, "snd_pcm_open failed: %s\n", snd_strerror (err)); return (FAIL); } cards[index] = handle; snd_pcm_hw_params_alloca (¶ms); snd_pcm_sw_params_alloca (&swparams); snd_pcm_hw_params_any (handle, params); snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); snd_pcm_hw_params_set_format (handle, params, SND_PCM_FORMAT_S16_LE); snd_pcm_hw_params_set_channels (handle, params, 1); rate = snd_pcm_hw_params_set_rate_near (handle, params, 44100, 0); period_size = 384; if (snd_pcm_hw_params_set_period_size (handle, params, period_size, 0) < 0) { fprintf (stderr, "Failed to set period size!\n"); return (FAIL); } buffer_size = period_size * 2; if (snd_pcm_hw_params_set_buffer_size (handle, params, buffer_size) < 0) { fprintf (stderr, "Failed to set buffer size!\n"); return (FAIL); } err = snd_pcm_hw_params (handle, params); /* set software parameters */ snd_pcm_sw_params_current (handle, swparams); snd_pcm_sw_params_set_sleep_min (handle, swparams, 0); // snd_pcm_sw_params_set_avail_min (handle, swparams, period_size); // start_threshold = (double) rate * (1 / 1000000); start_threshold = 0x7fffffff; snd_pcm_sw_params_set_start_threshold(handle, swparams, start_threshold); stop_threshold = buffer_size; snd_pcm_sw_params_set_stop_threshold(handle, swparams, stop_threshold); snd_pcm_sw_params (handle, swparams); /* Prepare our buffer */ period_bytes = period_size * 2; /* 2 bytes for 16 bit */ if (audiobuf_bytes == 0) { audiobuf = malloc (period_bytes); audiobuf_bytes = period_bytes; } else if (audiobuf_bytes != period_bytes) { fprintf (stderr, "audio buffers not same size (%d != %d)!\n", audiobuf_bytes, period_bytes); return (FAIL); } count = snd_pcm_poll_descriptors_count (handle); if (count <= 0) { fprintf (stderr, "invalid poll descriptors count!\n"); return (FAIL); } if (fdcount == 0) ufds = malloc (sizeof (struct pollfd) * count); else ufds = realloc (ufds, sizeof (struct pollfd) * (fdcount + count)); snd_pcm_poll_descriptors (handle, &ufds[fdcount], count); fdcount += count; return (OK); } void xrun_recovery() { int err; fprintf(stderr, "XRUN recovery\n"); if (err = snd_pcm_prepare(cards[0]) < 0) { fprintf (stderr, "Failed to prepare devices\n"); exit (1); } if (err = snd_pcm_writei (cards[0], audiobuf, audiobuf_bytes) < 0) { fprintf (stderr, "Failed to write initial bytes\n"); exit (1); } snd_pcm_start(cards[1]); } int main (void) { int i, r = 0; short revents; int err; struct sched_param schp; memset (&schp, 0, sizeof (schp)); schp.sched_priority = sched_get_priority_max (SCHED_FIFO); if (sched_setscheduler (0, SCHED_FIFO, &schp) != 0) fprintf (stderr, "Failed to run SCHED_FIFO\n"); /* Open input sound card */ if (setup_device (1, "plughw:0,0", 0) != OK) { fprintf (stderr, "Failed to setup input device\n"); exit (1); } /* Open output sound card */ if (setup_device (0, "plughw:0,0", 1) != OK) { fprintf (stderr, "Failed to setup output device\n"); exit (1); } if (snd_pcm_link (cards[1], cards[0]) < 0) { fprintf (stderr, "Failed to link devices\n"); exit (1); } if (err = snd_pcm_prepare (cards[0]) < 0) { fprintf (stderr, "Failed to prepare devices\n"); exit (1); } if (err = snd_pcm_writei (cards[0], audiobuf, audiobuf_bytes) < 0) { fprintf (stderr, "Failed to write initial bytes\n"); exit (1); } snd_pcm_start (cards[1]); do { poll (ufds, fdcount, -1); for (i=0; i < fdcount; i++) { revents = ufds[i].revents; if (revents & POLLERR) { snd_pcm_t *handle; snd_pcm_state_t state; if (revents & POLLOUT) handle = cards[0]; else handle = cards[1]; switch ((state = snd_pcm_state (handle))) { case SND_PCM_STATE_XRUN: fprintf (stderr, "XRUN on %s\n", (revents & POLLOUT != 0) ? "output" : "input"); xrun_recovery(); r = 0; goto __end; default: fprintf (stderr, "Bad state %d on %s\n", state, (revents & POLLOUT != 0) ? "output" : "input"); } } else if (revents & POLLIN) { if (r != 0) { printf("unsync: POLLIN\n"); continue; } if ((err = snd_pcm_readi (cards[1], audiobuf, audiobuf_bytes)) < 0) { if (err == -EPIPE) { fprintf (stderr, "readi XRUN on input\n"); } } else if (err > 0 && err != audiobuf_bytes) fprintf (stderr, "read error: requested %i read %i\n", audiobuf_bytes, err); printf("POLLIN: res = %i\n", err); r = 1; } else if (revents & POLLOUT) { if (r != 1) { printf("unsync: POLLOUT\n"); continue; } if ((err = snd_pcm_writei (cards[0], audiobuf, audiobuf_bytes)) < 0) { if (err == -EPIPE) { fprintf (stderr, "writei XRUN on output\n"); } } else if (err > 0 && err != audiobuf_bytes) fprintf (stderr, "write error: requested %i written %i\n", audiobuf_bytes, err); printf("POLLOUT: res = %i\n", err); r = 0; } } __end: } while (1); exit (0); }