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 (&params);
  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);
}

Reply via email to