git stults online alsa-signals / master record.c
master

Tree @master (Download .tar.gz)

record.c @masterraw · history · blame

#include <stdio.h>
#include <alsa/asoundlib.h>

/* structs */

typedef struct snd_dev_t
{
  volatile char        kill;
  char                 read;
  char                 write;
  char                *name;
  char                *bf;
  unsigned int         bflen;
  unsigned int         byps;
  unsigned int         chans;
  unsigned int         rate;
  unsigned int         frames;
  unsigned int         pdusec;
  snd_pcm_t           *handle;
  snd_pcm_hw_params_t *params;
} snd_dev_t;

/* forward declarations */

static inline void snd_error (int err);

snd_dev_t *snd_dev_create (const char *device, snd_pcm_stream_t stream,
			   snd_pcm_access_t access, snd_pcm_format_t format,
			   unsigned int channels, unsigned int rate, 
			   snd_pcm_uframes_t frames);

void       snd_dev_destroy (snd_dev_t *sd);

/* meat */

static inline void snd_error (int err)
{
  if (err < 0)
    {
      fprintf (stderr, "error: %s\n", snd_strerror(err));
      exit (1);
    }
  return;
}

snd_dev_t *snd_dev_create (const char *device, snd_pcm_stream_t stream,
			   snd_pcm_access_t access, snd_pcm_format_t format,
			   unsigned int channels, unsigned int rate, 
			   snd_pcm_uframes_t frames)
{
  int        dir;
  int        val;
  ssize_t    bytesper;
  snd_dev_t *sd;

  if ((sd = calloc (1, sizeof(snd_dev_t))) == NULL)
    return NULL;

  /* open device */
  snd_error (snd_pcm_open (&sd->handle, device, stream, 0));

  /* create param structure */
  snd_error (snd_pcm_hw_params_malloc (&sd->params));

  /* set params to default values */
  snd_error (snd_pcm_hw_params_any (sd->handle, sd->params));

  /* now set a few of the params */

  /* access mode */
  snd_error (snd_pcm_hw_params_set_access(sd->handle, sd->params, access));

  /* set format */
  snd_error (snd_pcm_hw_params_set_format(sd->handle, sd->params, format));

  /* channels */
  snd_error (snd_pcm_hw_params_set_channels(sd->handle, sd->params, channels));

  /* will try to set the rate as near to this as possible; if not
     equal dir is set accordingly; we don't check dir currently */
  snd_error (snd_pcm_hw_params_set_rate_near (sd->handle, sd->params, &rate, &dir));

  /* set period size to n frames */
  snd_error (snd_pcm_hw_params_set_period_size_near (sd->handle, sd->params, &frames, &dir));

  /* Write the parameters to the driver */
  snd_error (snd_pcm_hw_params (sd->handle, sd->params));

  /* deal with internal buffer in terms of period */

  /* want a buffer large enough to hold one period */
  /* this will be frames/period * bytes/sample * channels */
  snd_error (snd_pcm_hw_params_get_period_size (sd->params, &frames, &dir));
  snd_error ((bytesper = snd_pcm_format_size (format, 1))); /* bytes/1samp */
  sd->bflen = frames * bytesper * channels;
  if ((sd->bf = calloc (sd->bflen, sizeof(char))) == NULL)
    {
      snd_dev_destroy (sd);
      return NULL;
    }

  /* find out how long one period takes (usecs) */
  snd_error (snd_pcm_hw_params_get_period_time(sd->params, &val, &dir));

  /* set convenience values */
  sd->kill   = 0;
  sd->byps   = (unsigned int)bytesper;
  sd->chans  = channels;
  sd->rate   = rate;
  sd->frames = (unsigned int)frames;
  sd->pdusec = (unsigned int)val;
  sd->read   = (!strncmp (snd_pcm_stream_name (stream), "CAPTURE", 1024)) ? 1 : 0;
  sd->write  = (!strncmp (snd_pcm_stream_name (stream), "PLAYBACK", 1024)) ? 1 : 0;
  return sd;
}

void snd_dev_destroy (snd_dev_t *sd)
{
  if (sd == NULL)
    return;
  snd_pcm_hw_params_free (sd->params);
  snd_pcm_drain (sd->handle);
  snd_pcm_close (sd->handle);
  free (sd->bf);
  free (sd);
  sd = NULL;
  return;
}

static void snd_dev_poll_and_read (snd_dev_t *sd, FILE *f)
{
  int rc;
  int fd = fileno (f);
  while (!sd->kill)
    {
      rc = snd_pcm_readi (sd->handle, sd->bf, sd->frames);
      if (rc == -EPIPE)
	{
	  /* EPIPE means overrun */
	  fprintf(stderr, "overrun occurred\n");
	  snd_pcm_prepare (sd->handle);
	}
      else if (rc < 0)
	fprintf(stderr, "error from read: %s\n", snd_strerror(rc));
      else if (rc != (int)sd->frames)
	fprintf(stderr, "short read, read %d frames\n", rc);
      rc = write (fd, sd->bf, sd->bflen);
      if (rc != sd->bflen)
	fprintf(stderr, "short write: wrote %d bytes\n", rc);
    }
  snd_pcm_drain (sd->handle);
  return;
}

static void snd_dev_poll_and_write (snd_dev_t *sd, FILE *f)
{
  int rc;
  int fd = fileno (f);
  while (!sd->kill)
    {
      rc = read(fd, sd->bf, sd->bflen);
      if (rc == 0)
	{
	  fprintf(stderr, "end of file on input\n");
	  break;
	} 
      else if (rc != sd->bflen) 
	fprintf(stderr, "short read: read %d bytes\n", rc);
      rc = snd_pcm_writei(sd->handle, sd->bf, sd->frames);
      if (rc == -EPIPE)
	{
	  /* EPIPE means underrun */
	  fprintf(stderr, "underrun occurred\n");
	  snd_pcm_prepare (sd->handle);
	} 
      else if (rc < 0)
	fprintf(stderr, "error from writei: %s\n", snd_strerror(rc));
      else if (rc != (int)sd->frames)
	fprintf(stderr, "short write, write %d frames\n", rc);
    }
  snd_pcm_drain (sd->handle);
  return;
}

void snd_dev_transfer (snd_dev_t *sd, FILE *f)
{
  if (sd == NULL || f == NULL)
    return;

  if (sd->read)
    return snd_dev_poll_and_read (sd, f);
  else if (sd->write)
    return snd_dev_poll_and_write (sd, f);
  else
    return;
}

void main(int argc, char *argv[])
{
  FILE                *file;
  const char          *device = "default";
  snd_dev_t           *capture_handle;
  unsigned int         capture_channels = 2;
  unsigned int         capture_rate     = 44100;
  snd_pcm_stream_t     capture_stream   = SND_PCM_STREAM_CAPTURE;
  snd_pcm_access_t     capture_access   = SND_PCM_ACCESS_RW_INTERLEAVED;
  snd_pcm_format_t     capture_format   = SND_PCM_FORMAT_S16_LE;
  snd_pcm_uframes_t    capture_frames   = 32;

  capture_handle = snd_dev_create (device, capture_stream, capture_access,
				   capture_format, capture_channels, 
				   capture_rate, capture_frames);
  
  file = stdout;
  snd_dev_transfer (capture_handle, file);

  snd_dev_destroy (capture_handle);

  return;
}