ref: 621b1586820b67ec7b5989f6eef8616c79ba8d86
parent: b6a9d52af9ee8e0f124d61adc66505c8dbdc4d3d
author: Doug Cook <idigdoug@users.sourceforge.net>
date: Tue Feb 28 20:41:00 EST 2012
Apple CoreAudio - use virtual formats (works on more hardware) From Michael Chen: tried out on imac and it turns out virtual format is the way to do it. imac happened to have older codec which didn't have physical support for 32-bit float. Virtual formats are all 32-bit floats with various sampling rates.
--- a/src/coreaudio.c
+++ b/src/coreaudio.c
@@ -6,20 +6,24 @@
#include "sox_i.h"
#include <CoreAudio/CoreAudio.h>
+
+#include <assert.h>
+#include <limits.h>
#include <pthread.h>
-#define Buffactor 4
+#define Buffactor 8
typedef struct {
- AudioDeviceID adid;
- pthread_mutex_t mutex;
- pthread_cond_t cond;
- int device_started;
- size_t bufsize;
- size_t bufrd;
- size_t bufwr;
- size_t bufrdavail;
- float *buf;
+ AudioDeviceID adid;
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+ int device_started;
+ size_t bufsize;
+ size_t bufrd;
+ size_t bufwr;
+ size_t bufrdavail;
+ float *buf;
+ UInt32 strmidx;
} priv_t;
static OSStatus PlaybackIOProc(AudioDeviceID inDevice UNUSED,
@@ -33,23 +37,33 @@
priv_t *ac = (priv_t*)((sox_format_t*)inClientData)->priv;
AudioBuffer *buf;
size_t copylen, avail;
+ UInt32 i;
pthread_mutex_lock(&ac->mutex);
- for(buf = outOutputData->mBuffers;
- buf != outOutputData->mBuffers + outOutputData->mNumberBuffers;
- buf++){
+ assert(ac->strmidx < outOutputData->mNumberBuffers);
- copylen = buf->mDataByteSize / sizeof(float);
- if(copylen > ac->bufrdavail)
- copylen = ac->bufrdavail;
+ for(i = 0; i < outOutputData->mNumberBuffers; i++) {
+ buf = outOutputData->mBuffers + i;
+ if(i != ac->strmidx) {
+ buf->mDataByteSize = 0;
+ continue;
+ }
+
+ assert(buf->mData != NULL);
+
+ copylen = buf->mDataByteSize / sizeof(float);
+ if(copylen > ac->bufrdavail)
+ copylen = ac->bufrdavail;
+
avail = ac->bufsize - ac->bufrd;
if(buf->mData == NULL){
/*do nothing-hardware can't play audio*/
}else if(copylen > avail){
memcpy(buf->mData, ac->buf + ac->bufrd, avail * sizeof(float));
- memcpy((float*)buf->mData + avail, ac->buf, (copylen - avail) * sizeof(float));
+ memcpy((float*)buf->mData + avail, ac->buf,
+ (copylen - avail) * sizeof(float));
}else{
memcpy(buf->mData, ac->buf + ac->bufrd, copylen * sizeof(float));
}
@@ -76,40 +90,36 @@
void *inClientData)
{
priv_t *ac = (priv_t *)((sox_format_t*)inClientData)->priv;
- AudioBuffer *buf;
+ const AudioBuffer *buf;
size_t nfree, copylen, avail;
pthread_mutex_lock(&ac->mutex);
- for(buf = inInputData->mBuffers;
- buf != inInputData->mBuffers + inInputData->mNumberBuffers;
- buf++){
+ assert(ac->strmidx < inInputData->mNumberBuffers);
+ buf = inInputData->mBuffers + ac->strmidx;
+ assert(buf->mData != NULL);
- if(buf->mData == NULL)
- continue;
+ copylen = buf->mDataByteSize / sizeof(float);
+ nfree = ac->bufsize - ac->bufrdavail - 1;
+ if(nfree == 0)
+ lsx_warn("coreaudio: unhandled buffer overrun. Data discarded.");
- copylen = buf->mDataByteSize / sizeof(float);
- nfree = ac->bufsize - ac->bufrdavail - 1;
- if(nfree == 0)
- lsx_warn("coreaudio: unhandled buffer overrun. Data discarded.");
+ if(copylen > nfree)
+ copylen = nfree;
- if(copylen > nfree)
- copylen = nfree;
-
- avail = ac->bufsize - ac->bufwr;
- if(copylen > avail){
- memcpy(ac->buf + ac->bufwr, buf->mData, avail * sizeof(float));
- memcpy(ac->buf, (float*)buf->mData + avail, (copylen - avail) * sizeof(float));
- }else{
- memcpy(ac->buf + ac->bufwr, buf->mData, copylen * sizeof(float));
- }
-
- ac->bufwr += copylen;
- if(ac->bufwr >= ac->bufsize)
- ac->bufwr -= ac->bufsize;
- ac->bufrdavail += copylen;
+ avail = ac->bufsize - ac->bufwr;
+ if(copylen > avail){
+ memcpy(ac->buf + ac->bufwr, buf->mData, avail * sizeof(float));
+ memcpy(ac->buf, (float*)buf->mData + avail, (copylen - avail) * sizeof(float));
+ }else{
+ memcpy(ac->buf + ac->bufwr, buf->mData, copylen * sizeof(float));
}
+ ac->bufwr += copylen;
+ if(ac->bufwr >= ac->bufsize)
+ ac->bufwr -= ac->bufsize;
+ ac->bufrdavail += copylen;
+
pthread_cond_signal(&ac->cond);
pthread_mutex_unlock(&ac->mutex);
@@ -116,171 +126,244 @@
return kAudioHardwareNoError;
}
-static int setup(sox_format_t *ft, int is_input)
+static int
+distance(const struct AudioStreamRangedDescription *desc,
+ const sox_format_t *ft)
{
- priv_t *ac = (priv_t *)ft->priv;
- OSStatus status;
- UInt32 property_size;
- struct AudioStreamBasicDescription stream_desc;
- int32_t buf_size;
- int rc;
+ int distance = 0;
- if (strncmp(ft->filename, "default", (size_t)7) == 0)
- {
- property_size = sizeof(ac->adid);
- if (is_input)
- status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &property_size, &ac->adid);
- else
- status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &property_size, &ac->adid);
- }
- else
- {
- Boolean is_writable;
- status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &property_size, &is_writable);
+ if ((desc->mFormat.mFormatFlags & kLinearPCMFormatFlagIsFloat) == 0)
+ return INT_MAX;
+
+ if (desc->mSampleRateRange.mMinimum > ft->signal.rate)
+ distance += (desc->mSampleRateRange.mMinimum - ft->signal.rate) / 1000;
- if (status == noErr)
- {
- int device_count = property_size/sizeof(AudioDeviceID);
- AudioDeviceID *devices;
+ if (desc->mSampleRateRange.mMaximum < ft->signal.rate)
+ distance += (ft->signal.rate - desc->mSampleRateRange.mMaximum) / 1000;
- devices = malloc(property_size);
- status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &property_size, devices);
+ distance += abs(ft->signal.channels - desc->mFormat.mChannelsPerFrame);
- if (status == noErr)
- {
- int i;
- for (i = 0; i < device_count; i++)
- {
- char name[256];
- status = AudioDeviceGetProperty(devices[i],0,false,kAudioDevicePropertyDeviceName,&property_size,&name);
+ return distance;
+}
- lsx_report("Found Audio Device \"%s\"\n",name);
+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, i , count, best;
+ AudioStreamID *strms, strm;
+ Boolean is_writable;
+ struct AudioStreamRangedDescription *strmdescs;
- /* String returned from OS is truncated so only compare
- * as much as returned.
- */
- if (strncmp(name,ft->filename,strlen(name)) == 0)
- {
- ac->adid = devices[i];
- break;
- }
- }
- }
- free(devices);
- }
- }
+ if (strncmp(ft->filename, "default", (size_t)7) == 0) {
+ property_size = sizeof(ac->adid);
+ status = AudioHardwareGetProperty(
+ is_input? kAudioHardwarePropertyDefaultInputDevice :
+ kAudioHardwarePropertyDefaultOutputDevice,
+ &property_size, &ac->adid);
+ if (status != noErr || ac->adid == kAudioDeviceUnknown) {
+ lsx_fail_errno(ft, SOX_EPERM, "can not open default audio device");
+ return SOX_EOF;
+ }
+
+ } else {
+ AudioDeviceID *devices;
+
+ status = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
+ &property_size, &is_writable);
+ if (status != noErr) {
+ lsx_fail_errno(ft, SOX_EPERM, "can not get hardware prop info");
+ return SOX_EOF;
+ }
+
+ count = property_size / sizeof(AudioDeviceID);
+ devices = malloc((size_t)property_size);
+ status = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
+ &property_size, devices);
+ if (status != noErr) {
+ lsx_fail_errno(ft, SOX_EPERM, "can not enum devices");
+ free(devices);
+ return SOX_EOF;
+ }
- if (status || ac->adid == kAudioDeviceUnknown)
- {
- lsx_fail_errno(ft, SOX_EPERM, "can not open audio device");
- return SOX_EOF;
- }
+ for (i = 0; i < count; i++) {
+ char name[256];
+
+ property_size = sizeof(name);
+ status = AudioDeviceGetProperty(devices[i], 0, false,
+ kAudioDevicePropertyDeviceName, &property_size,
+ &name);
+ if (status != noErr)
+ continue;
+
+ lsx_report("Found Audio Device \"%s\"\n",name);
+
+ /* String returned from OS is truncated so only compare
+ * as much as returned.
+ */
+ if (strncmp(name,ft->filename,strlen(name)) == 0) {
+ ac->adid = devices[i];
+ break;
+ }
+ }
+ free(devices);
+ }
- /* 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;
- }
+ /* Find the stream */
+ status = AudioDeviceGetPropertyInfo(ac->adid, 0, is_input,
+ kAudioDevicePropertyStreams, &property_size,
+ &is_writable);
+ if (status != noErr) {
+ 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;
- }
+ count = property_size / sizeof(AudioStreamID);
+ assert(count >= 1);
+ strms = (AudioStreamID *)malloc(property_size);
- /* OS X effectively only supports these values. */
- ft->signal.channels = 2;
- ft->signal.rate = 44100;
- ft->encoding.bits_per_sample = 32;
+ status = AudioDeviceGetProperty(ac->adid, 0, is_input,
+ kAudioDevicePropertyStreams, &property_size,
+ strms);
+ if (status != noErr) {
+ lsx_fail_errno(ft, SOX_EPERM, "can not enum streams");
+ free(strms);
+ return SOX_EOF;
+ }
- /* TODO: My limited experience with hardware can only get floats working
- * withh 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;
+ /* TODO: allow the user to select the stream */
+ lsx_report("Found %d Streams for selected audio device, defaulting to 0\n",
+ count);
- /* 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;
- }
+ ac->strmidx = 0;
+ strm = strms[0];
+ free(strms);
+
+ status = AudioStreamGetPropertyInfo(strm, 0,
+ kAudioStreamPropertyAvailableVirtualFormats, &property_size,
+ &is_writable);
+ if (status != noErr) {
+ lsx_fail_errno(ft, SOX_EPERM, "failed to get format size");
+ 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);
+ count = property_size / sizeof(struct AudioStreamRangedDescription);
+ assert(count > 0);
- if (status)
- {
- lsx_fail_errno(ft, SOX_EPERM, "can not get audio device properties");
- return SOX_EOF;
- }
-#endif
+ strmdescs = (struct AudioStreamRangedDescription *) malloc(property_size);
+
+ status = AudioStreamGetProperty(strm, 0,
+ kAudioStreamPropertyAvailableVirtualFormats, &property_size,
+ strmdescs);
+ if (status != noErr) {
+ lsx_fail_errno(ft, SOX_EPERM, "failed to get formats");
+ free(strmdescs);
+ return SOX_EOF;
+ }
- if (stream_desc.mChannelsPerFrame != ft->signal.channels)
- {
- lsx_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;
- }
+ best = 0;
+ for (i = 0; i < count; i++) {
+ lsx_report("Supported format: %lu bits, %lu nchan, %lf rate, %lf min"
+ ", %lf max\n",
+ strmdescs[i].mFormat.mBitsPerChannel,
+ strmdescs[i].mFormat.mChannelsPerFrame,
+ strmdescs[i].mFormat.mSampleRate,
+ strmdescs[i].mSampleRateRange.mMinimum,
+ strmdescs[i].mSampleRateRange.mMaximum);
- if (stream_desc.mSampleRate != ft->signal.rate)
- {
- lsx_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;
- }
+ if (distance(&strmdescs[i], ft) < distance(&strmdescs[best], ft))
+ best = i;
+ }
- ac->bufsize = sox_globals.bufsiz / sizeof(sox_sample_t) * Buffactor;
- ac->bufrd = 0;
- ac->bufwr = 0;
- ac->bufrdavail = 0;
- ac->buf = lsx_malloc(ac->bufsize * sizeof(float));
+ stream_desc = strmdescs[best].mFormat;
+ if (ft->signal.rate < strmdescs[best].mSampleRateRange.mMinimum) {
+ ft->signal.rate = strmdescs[best].mSampleRateRange.mMinimum;
+ stream_desc.mSampleRate = strmdescs[best].mSampleRateRange.mMinimum;
- buf_size = sox_globals.bufsiz / sizeof(sox_sample_t) * sizeof(float);
- property_size = sizeof(buf_size);
- status = AudioDeviceSetProperty(ac->adid, NULL, 0, is_input,
- kAudioDevicePropertyBufferSize,
- property_size, &buf_size);
+ } else if (ft->signal.rate > strmdescs[best].mSampleRateRange.mMaximum) {
+ ft->signal.rate = strmdescs[best].mSampleRateRange.mMaximum;
+ stream_desc.mSampleRate = strmdescs[best].mSampleRateRange.mMaximum;
- rc = pthread_mutex_init(&ac->mutex, NULL);
- if (rc)
- {
- lsx_fail_errno(ft, SOX_EPERM, "failed initializing mutex");
- return SOX_EOF;
- }
+ } else {
+ stream_desc.mSampleRate = ft->signal.rate;
+ }
- rc = pthread_cond_init(&ac->cond, NULL);
- if (rc)
- {
- lsx_fail_errno(ft, SOX_EPERM, "failed initializing condition");
- return SOX_EOF;
- }
+ ft->signal.channels = stream_desc.mChannelsPerFrame;
+ free(strmdescs);
- ac->device_started = 0;
+ if (!(stream_desc.mFormatFlags & kLinearPCMFormatFlagIsFloat)) {
+ lsx_fail_errno(ft, SOX_EPERM, "audio device does not accept floats");
+ return SOX_EOF;
+ }
- /* 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;
+ /* Write them back */
+ property_size = sizeof(stream_desc);
+ status = AudioStreamSetProperty(strm, NULL, 0,
+ kAudioStreamPropertyVirtualFormat, property_size, &stream_desc);
+ if (status != noErr) {
+ 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 = AudioStreamGetProperty(strm, 0,kAudioStreamPropertyPhysicalFormat,
+ &property_size, &stream_desc);
+
+ if (status) {
+ lsx_fail_errno(ft, SOX_EPERM, "can not get audio device properties");
+ return SOX_EOF;
+ }
+
+ assert(stream_desc.mChannelsPerFrame == ft->signal.channels);
+ if (stream_desc.mChannelsPerFrame != ft->signal.channels) {
+ lsx_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;
+ }
+
+ assert(stream_desc.mSampleRate == ft->signal.rate);
+ if (stream_desc.mSampleRate != ft->signal.rate) {
+ lsx_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->bufsize = sox_globals.bufsiz / sizeof(sox_sample_t) * Buffactor;
+ ac->bufrd = 0;
+ ac->bufwr = 0;
+ ac->bufrdavail = 0;
+ ac->buf = lsx_malloc(ac->bufsize * sizeof(float));
+
+ buf_size = sox_globals.bufsiz / sizeof(sox_sample_t) /
+ stream_desc.mChannelsPerFrame;
+ property_size = sizeof(buf_size);
+ status = AudioDeviceSetProperty(ac->adid, NULL, 0, is_input,
+ kAudioDevicePropertyBufferFrameSize,
+ 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. */
+ status = AudioDeviceAddIOProc(ac->adid,
+ is_input? RecIOProc : PlaybackIOProc, ft);
+
+ return SOX_SUCCESS;
}
static int startread(sox_format_t *ft)