On Mon, 2001-10-15 at 02:46, Jaroslav Kysela wrote: > On 15 Oct 2001, Josh Green wrote: > > > I finally got around to doing a full duplex test with my SB Live card. > > At first I was experiencing the same problems I was having a while back > > (although to a lesser degree) with clicks increasing/decreasing in > > intensity over time. I was searching the ALSA archives (with google as > > There shouldn't be any clicks (only on xruns). Could you show me your > code? >
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. > > geocrawler is useless) when alsa-lib/test/latency.c was brought to my > > attention. This program works fine with full duplex, no clicks (except > > on overruns of course). I did notice that it likes to take 100% CPU > > which isn't so nice when running SCHED_RR. It looks like "snd_pcm_link" > > is what does the magic. Putting this in my test program makes things > > work all nice and sweet :) Is this function call required in a program > > to synchronize PCMs or can this be done externally (asound.conf?). > > This call is a part of API and it changes the behaviour of some functions > (it means that all operations are synced and you need to start/stop only > one stream from the linked group). > Yeah. But does a program need to have special support for full duplex mode or can a program that has a configurable input and output device be used without problems? (ecasound in particular) > Jaroslav > -- Josh Green Smurf Sound Font Editor (http://smurf.sourceforge.net)
#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); 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); } int main (void) { int i; 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 output sound card */ if (setup_device (0, "plughw:0,0", 1) != OK) { fprintf (stderr, "Failed to setup output device\n"); exit (1); } /* Open input sound card */ if (setup_device (1, "plughw:0,0", 0) != OK) { fprintf (stderr, "Failed to setup input device\n"); exit (1); } if (snd_pcm_link (cards[1], cards[0]) < 0) { fprintf (stderr, "Failed to link devices\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"); snd_pcm_prepare (handle); snd_pcm_start (handle); break; default: fprintf (stderr, "Bad state %d on %s\n", state, (revents & POLLOUT != 0) ? "output" : "input"); } } else if (revents & POLLOUT) { if (err = snd_pcm_writei (cards[0], audiobuf, audiobuf_bytes) < 0) { if (err == -EPIPE) { fprintf (stderr, "writei XRUN on output\n"); snd_pcm_prepare (cards[0]); // snd_pcm_start (cards[0]); } } } else if (revents & POLLIN) { if (err = snd_pcm_readi (cards[1], audiobuf, audiobuf_bytes) < 0) { if (err == -EPIPE) { fprintf (stderr, "readi XRUN on input\n"); snd_pcm_prepare (cards[1]); // snd_pcm_start (cards[1]); } } } } } while (1); exit (0); }