On Wed, 2001-11-28 at 15:51, Paul Davis wrote:

<cut>

> 
> is there any existing code that uses poll(2) for full duplex and
> demonstrates working operation?
> 
> --p
> 

<cut>

I've attached the test code that I wrote with Jaroslav's modifications
which uses poll. It still has the clicking problem I mentioned.

-- 
    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 OK 0
#define FAIL 1

#define TRUE 1
#define FALSE 0

char *audiobuf = NULL;
int audiobuf_bytes = 0;

int period_size = 384;
int buffer_size = 384 * 2;

snd_pcm_t *cards[2] = { NULL };

int fdcount[2] = { 0 };
struct pollfd *ufds[2] = { NULL };

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, 2);

  rate = snd_pcm_hw_params_set_rate_near (handle, params, 44100, 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 = (period_size * (16 / 8) * 2) * 2;

  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[0] = count1;
  fdcount[1] = count2;

  ufds[0] = malloc (sizeof (struct pollfd) * (count1 + count2));
  ufds[1] = (void *)ufds[0] + count1 * sizeof (struct pollfd);

  snd_pcm_poll_descriptors (cards[0], ufds[0], count1);
  snd_pcm_poll_descriptors (cards[1], ufds[1], 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 = 1;
  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", 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]);

  do
    {
      poll (ufds[r], fdcount[r], -1);

      for (i = fdcount[r]; i > 0; i--)
	{
	  revents = ufds[r]->revents;
	  if (revents & POLLERR)
	    {
	      snd_pcm_t *handle;
	      snd_pcm_state_t state;

	      if (revents & POLLOUT) handle = cards[0];
	      else handle = cards[1];

	      if ((state = snd_pcm_state (handle)) == SND_PCM_STATE_XRUN)
		{
		  fprintf (stderr, "XRUN on %s\n",
			   (revents & POLLOUT != 0) ? "output" : "input");
		  xrun_recovery();
		  r = 1;
		  break;
		}
	      else fprintf (stderr, "Bad state %d on %s\n", state,
			    (revents & POLLOUT != 0) ? "output" : "input");
	    }
	  else if (revents & POLLIN)
	    {
	      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);
	      r = 0;
	      break;
	    }
	  else if (revents & POLLOUT)
	    {
	      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);
	      r = 1;
	      break;
	    }
	}
    }
  while (1);

  exit (0);
}

Reply via email to