shithub: sox

Download patch

ref: 4d841254a948f0ed9f12ea7c77b0c1cda238b111
parent: 2e5330ec479f2476849fef57c5a2b7c238f4630c
author: cbagwell <cbagwell>
date: Wed Oct 15 21:31:57 EDT 2008

Initial Mac OSX CoreAudio driver.  Playing works but recording is untested.

--- a/ChangeLog
+++ b/ChangeLog
@@ -77,6 +77,7 @@
   o New -b/--bits, -e/--encoding alternative options for specifying
     audio encoding parameters.  (robs)
   o [FR 1958680] Support more than 32 input files.  (robs)
+  o New native Mac OSX audio handler for playing/recording. (cbagwell)
 
 Other bug fixes:
 
--- a/configure.ac
+++ b/configure.ac
@@ -239,6 +239,21 @@
 fi
 AM_CONDITIONAL(HAVE_SUN_AUDIO, test x$enable_sun_audio = xyes)
 
+dnl Check for Mac OSX CoreAudio
+AC_MSG_CHECKING([whether to try building CoreAudio driver])
+AC_ARG_ENABLE(coreaudio,
+    AC_HELP_STRING([--disable-coreaudio], [Don't build CoreAudio driver.]),,enable_coreaudio=yes)
+AC_MSG_RESULT($enable_coreaudio)
+if test "$enable_coreaudio" = "yes"; then
+    AC_CHECK_HEADERS(CoreAudio/CoreAudio.h, [COREAUDIO_LIBS="$COREAUDIO_LIBS -Wl,-framework,CoreAudio"], enable_coreaudio=no)
+fi
+if test "$enable_coreaudio" = yes; then
+   AC_DEFINE(HAVE_COREAUDIO, 1, [Define to 1 if you have CoreAudio.])
+   audio_driver_found=yes
+fi
+AM_CONDITIONAL(HAVE_COREAUDIO, test x$enable_coreaudio = xyes)
+AC_SUBST(COREAUDIO_LIBS)
+
 dnl Check if we want play/rec links
 AC_MSG_CHECKING([whether to make play and rec symlinks])
 AC_ARG_ENABLE(playrec-symlinks,
@@ -589,6 +604,7 @@
 echo "libao driver...................... $enable_libao"
 echo "OSS driver........................ $enable_oss"
 echo "SUN audio driver.................. $enable_sun_audio"
+echo "CoreAudio driver.................. $enable_coreaudio"
 echo "play and rec symlinks............. $enable_playrec_symlinks"
 echo "libgsm............................ $gsm_option"
 echo "liblpc10.......................... $lpc10_option"
--- a/soxformat.7
+++ b/soxformat.7
@@ -217,6 +217,18 @@
 samples in each CDDA track is always a multiple of 588 which is why it
 needs its own handler.
 .TP
+\fBcoreaudio\fR (optional)
+Mac OSX CoreAudio device driver: supports both playing and recording
+audio.  Examples:
+.EX
+	sox infile -t coreaudio
+	sox infile -t coreaudio default
+.EE
+See also
+.BR play (1)
+and
+.BR rec (1).
+.TP
 \&\fB.cvsd\fR, \fB.cvs\fR
 Continuously Variable Slope Delta modulation.
 A headerless format used to compress speech audio for applications such as voice mail.
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -108,6 +108,11 @@
 libsox_fmt_avr_la_LIBADD = libsox.la
 libsox_fmt_cdr_la_SOURCES = cdr.c
 libsox_fmt_cdr_la_LIBADD = libsox.la
+if HAVE_COREAUDIO
+libsox_fmt_coreaudio_la_SOURCES = coreaudio.c
+libsox_fmt_coreaudio_la_LIBADD = libsox.la @COREAUDIO_LIBS@
+pkglib_LTLIBRARIES += libsox_fmt_coreaudio.la
+endif
 libsox_fmt_cvsd_la_SOURCES = cvsd-fmt.c
 libsox_fmt_cvsd_la_LIBADD = libsox.la
 libsox_fmt_dvms_la_SOURCES = dvms-fmt.c
@@ -326,6 +331,11 @@
     libsox_la_SOURCES += amr-nb.c amr.h amr1.h amr2.h
     libsox_la_LIBADD += @AMR_NB_LIBS@
     sox_LDADD += @AMR_NB_LIBS@
+endif
+if HAVE_COREAUDIO
+    libsox_la_SOURCES += coreaudio.c
+    libsox_la_LIBADD += @COREAUDIO_LIBS@
+    sox_LDADD += @COREAUDIO_LIBS@
 endif
 if HAVE_WAVPACK
     libsox_la_SOURCES += wavpack.c
--- /dev/null
+++ b/src/coreaudio.c
@@ -1,0 +1,305 @@
+/* AudioCore sound handler
+ *
+ * Copyright 2008 Chris Bagwell And Sundry Contributors
+ */
+
+#include "sox_i.h"
+
+#include <CoreAudio/CoreAudio.h>
+#include <pthread.h>
+
+typedef struct {
+  AudioDeviceID adid;
+  pthread_mutex_t mutex;
+  pthread_cond_t cond;
+  int device_started;
+  size_t buf_size;
+  size_t buf_offset;
+  float *buffer;
+} priv_t;
+
+static OSStatus PlaybackIOProc(AudioDeviceID inDevice UNUSED, 
+                               const AudioTimeStamp *inNow UNUSED, 
+                               const AudioBufferList *inInputData UNUSED,
+                               const AudioTimeStamp *inInputTime UNUSED,
+                               AudioBufferList *outOutputData,
+                               const AudioTimeStamp *inOutputTime UNUSED,
+                               void *inClientData)
+{
+  sox_format_t *ft = (sox_format_t *)inClientData;
+  priv_t *ac = (priv_t *)ft->priv;
+  float *buf = outOutputData->mBuffers[0].mData;
+
+  pthread_mutex_lock(&ac->mutex);
+
+  memcpy(buf, ac->buffer, (ac->buf_offset) * sizeof(float));
+  ac->buf_offset = 0;
+
+  pthread_mutex_unlock(&ac->mutex);
+  pthread_cond_signal(&ac->cond);
+
+  return kAudioHardwareNoError;
+}
+
+static OSStatus RecIOProc(AudioDeviceID inDevice UNUSED, 
+                          const AudioTimeStamp *inNow UNUSED, 
+                          const AudioBufferList *inInputData,
+                          const AudioTimeStamp *inInputTime UNUSED,
+                          AudioBufferList *outOutputData UNUSED,
+                          const AudioTimeStamp *inOutputTime UNUSED,
+                          void *inClientData)
+{
+  sox_format_t *ft = (sox_format_t *)inClientData;
+  priv_t *ac = (priv_t *)ft->priv;
+  float *buf = inInputData->mBuffers[0].mData;
+
+  pthread_mutex_lock(&ac->mutex);
+
+  memcpy(ac->buffer, buf, ac->buf_size * sizeof(float));
+  ac->buf_offset = ac->buf_size-1;
+
+  pthread_mutex_unlock(&ac->mutex);
+  pthread_cond_signal(&ac->cond);
+
+  return kAudioHardwareNoError;
+}
+
+static int setup(sox_format_t *ft, int is_input)
+{
+  priv_t *ac = (priv_t *)ft->priv;
+  OSStatus status;
+  UInt32 property_size;
+  struct AudioStreamBasicDescription stream_desc;
+  int32_t buf_size;
+  int rc;
+
+  property_size = sizeof(ac->adid);
+  if (is_input)
+    status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice,
+                                      &property_size, &ac->adid);
+  else
+    status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice,
+                                      &property_size, &ac->adid);
+
+  if (status || ac->adid == kAudioDeviceUnknown)
+  {
+    lsx_fail_errno(ft, SOX_EPERM, "can not open audio device");
+    return SOX_EOF;
+  }
+
+  /* Query device to get initial values */
+  property_size = sizeof(struct AudioStreamBasicDescription);
+  status = AudioDeviceGetProperty(ac->adid, 0, is_input,
+                                  kAudioDevicePropertyStreamFormat,
+                                  &property_size, &stream_desc);
+  if (status)
+  {
+    lsx_fail_errno(ft, SOX_EPERM, "can not get audio device properties");
+    return SOX_EOF;
+  }
+
+  if (!(stream_desc.mFormatFlags & kLinearPCMFormatFlagIsFloat))
+  {
+    lsx_fail_errno(ft, SOX_EPERM, "audio device does not accept floats");
+    return SOX_EOF;
+  }
+
+  /* TODO: My limited experience with hardware can only get floats working which a fixed sample
+   * rate and stereo.  I know that is a limitiation of audio device I have so this may not be
+   * standard operating orders.  If some hardware supports setting sample rates and channel counts
+   * then should do that over resampling and mixing.
+   */
+#if  0
+  stream_desc.mSampleRate = ft->signal.rate;
+  stream_desc.mChannelsPerFrame = ft->signal.channels;
+
+  /* Write them back */
+  property_size = sizeof(struct AudioStreamBasicDescription);
+  status = AudioDeviceSetProperty(ac->adid, NULL, 0, is_input,
+                                  kAudioDevicePropertyStreamFormat,
+                                  property_size, &stream_desc);
+  if (status)
+  {
+    lsx_fail_errno(ft, SOX_EPERM, "can not set audio device properties");
+    return SOX_EOF;
+  }
+
+  /* Query device to see if it worked */
+  property_size = sizeof(struct AudioStreamBasicDescription);
+  status = AudioDeviceGetProperty(ac->adid, 0, is_input,
+                                  kAudioDevicePropertyStreamFormat,
+                                  &property_size, &stream_desc);
+
+  if (status)
+  {
+    lsx_fail_errno(ft, SOX_EPERM, "can not get audio device properties");
+    return SOX_EOF;
+  }
+#endif
+
+  if (stream_desc.mChannelsPerFrame != ft->signal.channels)
+  {
+    sox_debug("audio device did not accept %d channels. Use %d channels instead.", (int)ft->signal.channels, 
+              (int)stream_desc.mChannelsPerFrame);
+    ft->signal.channels = stream_desc.mChannelsPerFrame;
+  }
+
+  if (stream_desc.mSampleRate != ft->signal.rate)
+  {
+    sox_debug("audio device did not accept %d sample rate. Use %d instead.", (int)ft->signal.rate, 
+              (int)stream_desc.mSampleRate);
+    ft->signal.rate = stream_desc.mSampleRate;
+  }
+
+  ac->buf_size = sox_globals.bufsiz;
+  ac->buf_offset = 0;
+  ac->buffer = lsx_malloc(ac->buf_size * sizeof(sox_sample_t));
+
+  buf_size = sox_globals.bufsiz * sizeof(float);
+  property_size = sizeof(buf_size);
+  status = AudioDeviceSetProperty(ac->adid, NULL, 0, is_input, 
+                                  kAudioDevicePropertyBufferSize,
+                                  property_size, &buf_size);
+
+  rc = pthread_mutex_init(&ac->mutex, NULL);
+  if (rc) 
+  {
+    lsx_fail_errno(ft, SOX_EPERM, "failed initializing mutex");
+    return SOX_EOF;
+  }
+
+  rc = pthread_cond_init(&ac->cond, NULL);
+  if (rc) 
+  {
+    lsx_fail_errno(ft, SOX_EPERM, "failed initializing condition");
+    return SOX_EOF;
+  }
+
+  ac->device_started = 0;
+
+  /* Registers callback with the device without activating it. */
+  if (is_input)
+    status = AudioDeviceAddIOProc(ac->adid, RecIOProc, (void *)ft);
+  else
+    status = AudioDeviceAddIOProc(ac->adid, PlaybackIOProc, (void *)ft);
+
+  return SOX_SUCCESS;
+}
+
+static int startread(sox_format_t *ft)
+{
+    return setup(ft, 1);
+}
+
+static size_t read_samples(sox_format_t *ft, sox_sample_t *buf, size_t nsamp)
+{
+  priv_t *ac = (priv_t *)ft->priv;
+  size_t len = nsamp;
+  size_t samp_left;
+  OSStatus status;
+  float *p;
+
+  if (!ac->device_started)
+  {
+    status = AudioDeviceStart(ac->adid, RecIOProc);
+    ac->device_started = 1;
+  }
+
+  pthread_mutex_lock(&ac->mutex);
+
+  /* Wait until input buffer has been filled by device driver */
+  while (ac->buf_offset == 0 || ac->buf_offset > ac->buf_size)
+    pthread_cond_wait(&ac->cond, &ac->mutex);
+
+  if (len > ac->buf_size - ac->buf_offset)
+    len = ac->buf_size - ac->buf_offset;
+  samp_left = len;
+
+  p = &ac->buffer[ac->buf_offset];
+
+  while (samp_left--)
+    *buf++ = SOX_FLOAT_32BIT_TO_SAMPLE(*p++, ft->clips);
+
+  ac->buf_offset -= len;
+
+  pthread_mutex_unlock(&ac->mutex);
+
+  return len;
+}
+
+static int stopread(sox_format_t * ft)
+{
+  priv_t *ac = (priv_t *)ft->priv;
+
+  AudioDeviceStop(ac->adid, RecIOProc);
+
+  return SOX_SUCCESS;
+}
+
+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 *ac = (priv_t *)ft->priv;
+  size_t len = nsamp;
+  size_t samp_left;
+  OSStatus status;
+  float *p;
+
+  if (!ac->device_started)
+  {
+    status = AudioDeviceStart(ac->adid, PlaybackIOProc);
+    ac->device_started = 1;
+  }
+
+  pthread_mutex_lock(&ac->mutex);
+
+  /* Wait until there is some room to copy some samples */
+  while (ac->buf_offset >= ac->buf_size - 1)
+    pthread_cond_wait(&ac->cond, &ac->mutex);
+
+  if (len > ac->buf_size - ac->buf_offset)
+    len = ac->buf_size - ac->buf_offset;
+  samp_left = len;
+
+  p = &ac->buffer[ac->buf_offset];
+
+  while (samp_left--)
+    *p++ = SOX_SAMPLE_TO_FLOAT_32BIT(*buf++, ft->clips);
+
+  ac->buf_offset += len;
+
+  pthread_mutex_unlock(&ac->mutex);
+
+  return len;
+}
+
+
+static int stopwrite(sox_format_t * ft)
+{
+  priv_t *ac = (priv_t *)ft->priv;
+
+  AudioDeviceStop(ac->adid, PlaybackIOProc);
+
+  return SOX_SUCCESS;
+}
+
+SOX_FORMAT_HANDLER(coreaudio)
+{
+  static char const *const names[] = { "coreaudio", NULL };
+  static unsigned const write_encodings[] = {
+    SOX_ENCODING_SIGN2, 16, 8, 0,
+    SOX_ENCODING_UNSIGNED, 16, 8, 0,
+    0};
+  static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
+    "Mac AudioCore device driver",
+    names, SOX_FILE_DEVICE | SOX_FILE_NOSTDIO,
+    startread, read_samples, stopread,
+    startwrite, write_samples, stopwrite,
+    NULL, write_encodings, NULL, sizeof(priv_t)
+  };
+  return &handler;
+}
--- a/src/formats.h
+++ b/src/formats.h
@@ -103,6 +103,9 @@
 #if defined(HAVE_SYS_AUDIOIO_H) || defined(HAVE_SUN_AUDIOIO_H)
   FORMAT(sunau)
 #endif
+#if defined(HAVE_COREAUDIO) || defined(HAVE_COREAUDIO)
+  FORMAT(coreaudio)
+#endif
 #if defined HAVE_OGG_VORBIS
   FORMAT(vorbis)
 #endif
--- a/src/sox.c
+++ b/src/sox.c
@@ -2056,7 +2056,7 @@
     return NULL;
   if (!strcmp(type, "sunau")) name = "/dev/audio";
   else if (!strcmp(type, "oss" ) || !strcmp(type, "ossdsp")) name = "/dev/dsp";
-  else if (!strcmp(type, "alsa") || !strcmp(type, "ao"))     name = "default";
+  else if (!strcmp(type, "alsa") || !strcmp(type, "ao") || !strcmp(type, "coreaudio")) name = "default";
   return name? from_env? from_env : name : NULL;
 }
 
@@ -2064,6 +2064,7 @@
 {
   /* Default audio driver type in order of preference: */
   if (!f->filetype) f->filetype = getenv("AUDIODRIVER");
+  if (!f->filetype && sox_find_format("coreaudio", sox_false)) f->filetype = "coreaudio";
   if (!f->filetype && sox_find_format("alsa", sox_false)) f->filetype = "alsa";
   if (!f->filetype && sox_find_format("oss" , sox_false)) f->filetype = "oss";
   if (!f->filetype && sox_find_format("sunau",sox_false)) f->filetype = "sunau";