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";