On Fri, 2001-12-14 at 02:33, Josh Green wrote: > > I'll attach the current code for the full duplex test program I used. > Its not very general at the moment perhaps a real full-duplex test > program should be written for distributing with ALSA? > For detailed information uncomment the "#define HISTORY 1" which fills > up a history buffer until it gets to a certain size and then exits and > displays the results, piping through "less" is a good idea :) >
Ooops! Forgot the attachment. Here you go for anyone who missed it. -- Josh Green Smurf Sound Font Editor (http://smurf.sourceforge.net)
/* Full duplex ALSA test program Do whatever you want with it and use it at your own risk Josh Green <[EMAIL PROTECTED]> Fixes by Jaroslav Kysela November 28, 2001 */ #include <stdio.h> #include <alsa/asoundlib.h> #include <string.h> #include <sched.h> #define PERIOD_SIZE 384 #define BUFFER_SIZE PERIOD_SIZE * 2 #define CHANNELS 2 #define RATE 44100 /* uncomment for history logging (available data on each read/write and XRUNs) probably want to pipe through less though, runs until the history gets to a fixed length (HISTORY_SIZE entries) then quits and prints history */ #define HISTORY 1 #define HISTORY_SIZE 12000 #define OK 0 #define FAIL 1 #define TRUE 1 #define FALSE 0 char *audiobuf = NULL; int audiobuf_bytes = 0; snd_pcm_t *cards[2] = { NULL }; int fdcount = 0; struct pollfd *ufds = NULL; #ifdef HISTORY unsigned int history[HISTORY_SIZE]; int histndx = 0; #endif int setup_device (int index, char *device, int play) { int err; snd_pcm_t *handle; snd_pcm_hw_params_t *params; /* Hardware parameters */ snd_pcm_sw_params_t *swparams; /* Software parameters */ int thisbuf_bytes; /* this cards audio buffer size */ unsigned int rate; snd_pcm_uframes_t start_threshold, stop_threshold; /* Open output sound card */ if (err = snd_pcm_open (&handle, device, play ? 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, CHANNELS); rate = snd_pcm_hw_params_set_rate_near (handle, params, RATE, 0); if (snd_pcm_hw_params_set_period_size (handle, params, PERIOD_SIZE, 0) < 0) { fprintf (stderr, "Failed to set period size!\n"); return (FAIL); } 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); 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 */ thisbuf_bytes = BUFFER_SIZE * (16 / 8) * CHANNELS; if (audiobuf_bytes == 0) { audiobuf = malloc (thisbuf_bytes); audiobuf_bytes = thisbuf_bytes; } else if (audiobuf_bytes != thisbuf_bytes) { fprintf (stderr, "audio buffers not same size (%d != %d)!\n", audiobuf_bytes, thisbuf_bytes); return (FAIL); } return (OK); } void setup_pollfds (void) { int count1, count2; count1 = snd_pcm_poll_descriptors_count (cards[0]); count2 = snd_pcm_poll_descriptors_count (cards[1]); fdcount = count1 + count2; ufds = malloc (sizeof (struct pollfd) * (count1 + count2)); snd_pcm_poll_descriptors (cards[0], ufds, count1); snd_pcm_poll_descriptors (cards[1], ufds + count1, count2); } 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, PERIOD_SIZE * 2) < 0) { fprintf (stderr, "Failed to write initial bytes\n"); exit (1); } fprintf (stderr, "Wrote %d start bytes\n", err); snd_pcm_start(cards[1]); } int main (void) { int i, r, f, avail; short revents; int err; struct sched_param schp; snd_pcm_t *handle; snd_pcm_status_t *status; snd_pcm_state_t state; char *s; 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", TRUE) != OK) { fprintf (stderr, "Failed to setup output device\n"); exit (1); } /* Open input sound card */ if (setup_device (1, "plughw:0,0", FALSE) != OK) { fprintf (stderr, "Failed to setup input device\n"); exit (1); } setup_pollfds (); 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, PERIOD_SIZE * 2) < 0) { fprintf (stderr, "Failed to write initial bytes\n"); exit (1); } fprintf (stderr, "Wrote %d start bytes\n", err); snd_pcm_start (cards[1]); snd_pcm_status_malloc (&status); do { poll (ufds, fdcount, -1); for (i = fdcount - 1; i >= 0; i--) { revents = ufds[i].revents; if (revents & POLLOUT) handle = cards[0]; else handle = cards[1]; snd_pcm_status (handle, status); if (revents & POLLERR) { if ((state = snd_pcm_status_get_state (status)) == SND_PCM_STATE_XRUN) { #ifdef HISTORY history[histndx++] = 0xFFFF | ((revents & POLLOUT) != 0) << 31 | ((revents & POLLIN) != 0) << 30; #endif fprintf (stderr, "XRUN on %s\n", (revents & POLLOUT != 0) ? "output" : "input"); xrun_recovery(); break; } else fprintf (stderr, "Bad state %d on %s\n", state, (revents & POLLOUT != 0) ? "output" : "input"); } else if (revents & POLLIN) { #ifdef HISTORY history[histndx++] = snd_pcm_status_get_avail (status) | (1 << 31); #endif err = snd_pcm_readi (cards[1], audiobuf, PERIOD_SIZE); if (err > 0 && err != PERIOD_SIZE) fprintf (stderr, "read error: requested %i read %i\n", audiobuf_bytes, err); break; } else if (revents & POLLOUT) { #ifdef HISTORY history[histndx++] = snd_pcm_status_get_avail (status) | (1 << 30); #endif err = snd_pcm_writei (cards[0], audiobuf, PERIOD_SIZE); if (err > 0 && err != PERIOD_SIZE) fprintf (stderr, "write error: requested %i written %i\n", audiobuf_bytes, err); break; } } } #ifdef HISTORY while (histndx < HISTORY_SIZE); #else while (TRUE); #endif #ifdef HISTORY printf ("----------------------\n"); for (i = 0; i < histndx; i++) { r = history[i] & 0xFFFF; f = history[i] >> 30; switch (f) { case 0x1: s = "INPUT"; break; case 0x2: s = "OUTPUT"; break; case 0x3: s = "INPUT and OUTPUT"; break; } if (r == 0xFFFF) printf ("XRUN on %s\n", s); else printf ("%s avail %d\n", s, r); } #endif exit (0); }