ref: 814486a2149e672dd9dac281992f28800445cd19
dir: /src/pulseaudio.c/
/* Pulse Audio sound handler
 *
 * Copyright 2008 Chris Bagwell And Sundry Contributors
 */
#include "sox_i.h"
#include <pulse/simple.h>
#include <pulse/error.h>
typedef struct {
  pa_simple *pasp;
} priv_t;
static int setup(sox_format_t *ft, int is_input)
{
  priv_t *pa = (priv_t *)ft->priv;
  char *server;
  pa_stream_direction_t dir;
  char *app_str;
  char *dev;
  pa_sample_spec spec;
  int error;
  /* TODO: If user specified device of type "server:dev" then
   * break up and override server.
   */
  server = NULL;
  if (is_input)
  {
    dir = PA_STREAM_RECORD;
    app_str = "record";
  }
  else
  {
    dir = PA_STREAM_PLAYBACK;
    app_str = "playback";
  }
  if (strncmp(ft->filename, "default", 7) == 0)
    dev = NULL;
  else
    dev = ft->filename;
  /* If user doesn't specify, default to some reasonable values.
   * Since this is mainly for recording case, default to typical
   * 16-bit values to prevent saving larger files then average user
   * wants.  Power users can override to 32-bit if they wish.
   */
  if (ft->signal.channels == 0)
    ft->signal.channels = 2;
  if (ft->signal.rate == 0)
    ft->signal.rate = 44100;
  if (ft->encoding.bits_per_sample == 0)
  {
    ft->encoding.bits_per_sample = 16;
    ft->encoding.encoding = SOX_ENCODING_SIGN2;
  }
 
  spec.format = PA_SAMPLE_S32NE;
  spec.rate = ft->signal.rate;
  spec.channels = ft->signal.channels;
  pa->pasp = pa_simple_new(server, "SoX", dir, dev, app_str, &spec,
                          NULL, NULL, &error);
  if (pa->pasp == NULL)
  {
    lsx_fail_errno(ft, SOX_EPERM, "can not open audio device: %s", pa_strerror(error));
    return SOX_EOF;
  }
  /* TODO: Is it better to convert format/rates in SoX or in
   * always let Pulse Audio do it?  Since we don't know what
   * hardware prefers, assume it knows best and give it
   * what user specifies.
   */
  return SOX_SUCCESS;
}
static int startread(sox_format_t *ft)
{
    return setup(ft, 1);
}
static int stopread(sox_format_t * ft)
{
  priv_t *pa = (priv_t *)ft->priv;
  pa_simple_free(pa->pasp);
  return SOX_SUCCESS;
}
static size_t read_samples(sox_format_t *ft, sox_sample_t *buf, size_t nsamp)
{
  priv_t *pa = (priv_t *)ft->priv;
  size_t len;
  int rc, error;
  /* Pulse Audio buffer lengths are true buffer lengths and not
   * count of samples. */
  len = nsamp * sizeof(sox_sample_t);
  rc = pa_simple_read(pa->pasp, buf, len, &error);
  if (rc < 0)
  {
    lsx_fail_errno(ft, SOX_EPERM, "error reading from pulse audio device: %s", pa_strerror(error));
    return SOX_EOF;
  }
  else
    return nsamp;
}
static int startwrite(sox_format_t * ft)
{
    return setup(ft, 0);
}
static size_t write_samples(sox_format_t *ft, const sox_sample_t *buf, size_t nsamp)
{
  priv_t *pa = (priv_t *)ft->priv;
  size_t len;
  int rc, error;
  /* Pulse Audio buffer lengths are true buffer lengths and not
   * count of samples. */
  len = nsamp * sizeof(sox_sample_t);
  rc = pa_simple_write(pa->pasp, buf, len, &error);
  if (rc < 0)
  {
    lsx_fail_errno(ft, SOX_EPERM, "error writing to pulse audio device: %s", pa_strerror(error));
    return SOX_EOF;
  }
  return nsamp;
}
static int stopwrite(sox_format_t * ft)
{
  priv_t *pa = (priv_t *)ft->priv;
  int error;
  pa_simple_drain(pa->pasp, &error);
  pa_simple_free(pa->pasp);
  return SOX_SUCCESS;
}
LSX_FORMAT_HANDLER(pulseaudio)
{
  static char const *const names[] = { "pulseaudio", NULL };
  static unsigned const write_encodings[] = {
    SOX_ENCODING_SIGN2, 32, 0,
    0};
  static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
    "Pulse Audio client",
    names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
    startread, read_samples, stopread,
    startwrite, write_samples, stopwrite,
    NULL, write_encodings, NULL, sizeof(priv_t)
  };
  return &handler;
}