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);
}