--- a/media/libcubeb/src/cubeb_audiounit.c
+++ b/media/libcubeb/src/cubeb_audiounit.c
@@ -11,57 +11,74 @@
#include <mach/mach_time.h>
#include <pthread.h>
#include <stdlib.h>
#include <AudioUnit/AudioUnit.h>
#if !TARGET_OS_IPHONE
#include <CoreAudio/AudioHardware.h>
#include <CoreAudio/HostTime.h>
#include <CoreFoundation/CoreFoundation.h>
-#else
+#endif
#include <CoreAudio/CoreAudioTypes.h>
#include <AudioToolbox/AudioToolbox.h>
-#endif
#include "cubeb/cubeb.h"
#include "cubeb-internal.h"
#include "cubeb_panner.h"
#if !TARGET_OS_IPHONE
#include "cubeb_osx_run_loop.h"
#endif
#if !defined(kCFCoreFoundationVersionNumber10_7)
/* From CoreFoundation CFBase.h */
#define kCFCoreFoundationVersionNumber10_7 635.00
#endif
#if !TARGET_OS_IPHONE && MAC_OS_X_VERSION_MIN_REQUIRED < 1060
-#define MACOSX_LESS_THAN_106
+#define AudioComponent Component
+#define AudioComponentDescription ComponentDescription
+#define AudioComponentFindNext FindNextComponent
+#define AudioComponentInstanceNew OpenAComponent
+#define AudioComponentInstanceDispose CloseComponent
#endif
#define CUBEB_STREAM_MAX 8
#define NBUFS 4
+#define AU_OUT_BUS 0
+#define AU_IN_BUS 1
+
+#if TARGET_OS_IPHONE
+#define CUBEB_AUDIOUNIT_SUBTYPE kAudioUnitSubType_RemoteIO
+#else
+#define CUBEB_AUDIOUNIT_SUBTYPE kAudioUnitSubType_HALOutput
+#endif
+
static struct cubeb_ops const audiounit_ops;
struct cubeb {
struct cubeb_ops const * ops;
pthread_mutex_t mutex;
int active_streams;
int limit_streams;
};
struct cubeb_stream {
cubeb * context;
- AudioUnit unit;
cubeb_data_callback data_callback;
cubeb_state_callback state_callback;
cubeb_device_changed_callback device_changed_callback;
void * user_ptr;
- AudioStreamBasicDescription sample_spec;
+ AudioConverterRef input_converter;
+ AudioStreamBasicDescription input_desc;
+ AudioStreamBasicDescription output_desc;
+ AudioUnit input_unit;
+ AudioUnit output_unit;
pthread_mutex_t mutex;
+ AudioBufferList input_buflst;
+ uint32_t input_fpb;
uint64_t frames_played;
uint64_t frames_queued;
int shutdown;
int draining;
uint64_t current_latency_frames;
uint64_t hw_latency_frames;
float panning;
};
@@ -96,80 +113,129 @@ audiotimestamp_to_latency(AudioTimeStamp
{
if (!(tstamp->mFlags & kAudioTimeStampHostTimeValid)) {
return 0;
}
uint64_t pres = AudioConvertHostTimeToNanos(tstamp->mHostTime);
uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
- return ((pres - now) * stream->sample_spec.mSampleRate) / 1000000000LL;
+ return ((pres - now) * stream->output_desc.mSampleRate) / 1000000000LL;
+}
+
+static OSStatus
+audiounit_input_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
+ AudioTimeStamp const * tstamp, UInt32 bus, UInt32 nframes,
+ AudioBufferList * bufs)
+{
+ cubeb_stream * stream = user_ptr;
+ long outframes;
+ void * outbuf = NULL, * inbuf = NULL;
+
+ pthread_mutex_lock(&stream->mutex);
+
+ assert(stream->input_unit != NULL);
+ assert(AU_IN_BUS == bus);
+
+ /* FIXME: kAudioUnitErr_TooManyFramesToProcess ????*/
+ OSStatus r = AudioUnitRender(stream->input_unit, flags, tstamp, bus,
+ nframes, &stream->input_buflst);
+ assert(r == noErr);
+
+ if (stream->input_converter == NULL) {
+ inbuf = stream->input_buflst.mBuffers[0].mData;
+ } else {
+ /* We will need to buffer */
+ assert(false);
+ }
+
+ if (stream->output_unit != NULL) {
+ // Full Duplex. We'll call data_callback in the AudioUnit output callback.
+ pthread_mutex_unlock(&stream->mutex);
+ return noErr;
+ }
+ pthread_mutex_unlock(&stream->mutex);
+
+ outframes = stream->data_callback(stream, stream->user_ptr, inbuf, outbuf, nframes);
+ if (outframes < 0) {
+ /* XXX handle this case. */
+ assert(false);
+ return noErr;
+ }
+ return noErr;
}
static OSStatus
audiounit_output_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
- AudioTimeStamp const * tstamp, UInt32 bus, UInt32 nframes,
- AudioBufferList * bufs)
+ AudioTimeStamp const * tstamp, UInt32 bus, UInt32 nframes,
+ AudioBufferList * bufs)
{
- cubeb_stream * stm;
- unsigned char * buf;
- long got;
- OSStatus r;
- float panning;
+ cubeb_stream * stream = user_ptr;
+ long outframes, curframes;
+ void * outbuf = NULL, * inbuf = NULL;
- assert(bufs->mNumberBuffers == 1);
- buf = bufs->mBuffers[0].mData;
+ pthread_mutex_lock(&stream->mutex);
- stm = user_ptr;
-
- pthread_mutex_lock(&stm->mutex);
-
- stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
- panning = stm->panning;
+ stream->current_latency_frames = audiotimestamp_to_latency(tstamp, stream);
- if (stm->draining || stm->shutdown) {
- pthread_mutex_unlock(&stm->mutex);
- if (stm->draining) {
- r = AudioOutputUnitStop(stm->unit);
- assert(r == 0);
- stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
- }
+ if (stream->draining) {
+ OSStatus r;
+ pthread_mutex_unlock(&stream->mutex);
+ r = AudioOutputUnitStop(stream->output_unit);
+ assert(r == 0);
+ stream->state_callback(stream, stream->user_ptr, CUBEB_STATE_DRAINED);
+ return noErr;
+ } else if (stream->shutdown) {
+ pthread_mutex_unlock(&stream->mutex);
return noErr;
}
- pthread_mutex_unlock(&stm->mutex);
- got = stm->data_callback(stm, stm->user_ptr, buf, nframes);
- pthread_mutex_lock(&stm->mutex);
- if (got < 0) {
+ assert(AU_OUT_BUS == bus);
+
+ assert(bufs->mNumberBuffers == 1);
+ outbuf = bufs->mBuffers[0].mData;
+
+ if (stream->input_unit != NULL) {
+ inbuf = stream->input_buflst.mBuffers[0].mData;
+ }
+
+ pthread_mutex_unlock(&stream->mutex);
+ outframes = stream->data_callback(stream, stream->user_ptr, inbuf, outbuf, nframes);
+ if (outframes < 0) {
/* XXX handle this case. */
assert(false);
- pthread_mutex_unlock(&stm->mutex);
return noErr;
}
- if ((UInt32) got < nframes) {
- size_t got_bytes = got * stm->sample_spec.mBytesPerFrame;
- size_t rem_bytes = (nframes - got) * stm->sample_spec.mBytesPerFrame;
+ AudioFormatFlags outaff;
+ float panning;
+ size_t outbpf;
- stm->draining = 1;
-
- memset(buf + got_bytes, 0, rem_bytes);
- }
+ pthread_mutex_lock(&stream->mutex);
+ outbpf = stream->output_desc.mBytesPerFrame;
+ stream->draining = (outframes < nframes);
+ stream->frames_played = stream->frames_queued;
+ stream->frames_queued += outframes;
- stm->frames_played = stm->frames_queued;
- stm->frames_queued += got;
- pthread_mutex_unlock(&stm->mutex);
+ outaff = stream->output_desc.mFormatFlags;
+ panning = (stream->output_desc.mChannelsPerFrame == 2) ? stream->panning : 0.0f;
+ pthread_mutex_unlock(&stream->mutex);
- if (stm->sample_spec.mChannelsPerFrame == 2) {
- if (stm->sample_spec.mFormatFlags & kAudioFormatFlagIsFloat)
- cubeb_pan_stereo_buffer_float((float*)buf, got, panning);
- else if (stm->sample_spec.mFormatFlags & kAudioFormatFlagIsSignedInteger)
- cubeb_pan_stereo_buffer_int((short*)buf, got, panning);
+ /* Post process output samples*/
+ if ((UInt32) outframes < nframes) {
+ /* Clear missing frames (silence) */
+ memset(outbuf + outframes * outbpf, 0, (nframes - outframes) * outbpf);
}
-
+ /* Pan stereo */
+ if (panning != 0.0f) {
+ if (outaff & kAudioFormatFlagIsFloat)
+ cubeb_pan_stereo_buffer_float((float*)outbuf, outframes, panning);
+ else if (outaff & kAudioFormatFlagIsSignedInteger)
+ cubeb_pan_stereo_buffer_int((short*)outbuf, outframes, panning);
+ }
return noErr;
}
/*static*/ int
audiounit_init(cubeb ** context, char const * context_name)
{
cubeb * ctx;
int r;
@@ -533,141 +599,291 @@ audiounit_destroy(cubeb * ctx)
assert(r == 0);
free(ctx);
}
static void audiounit_stream_destroy(cubeb_stream * stm);
static int
-audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
- cubeb_stream_params stream_params, unsigned int latency,
- cubeb_data_callback data_callback, cubeb_state_callback state_callback,
- void * user_ptr)
+audio_stream_desc_init (AudioStreamBasicDescription * ss,
+ const cubeb_stream_params * stream_params)
{
- AudioStreamBasicDescription ss;
-#if MACOSX_LESS_THAN_106
- ComponentDescription desc;
- Component comp;
-#else
- AudioComponentDescription desc;
- AudioComponent comp;
-#endif
- cubeb_stream * stm;
- AURenderCallbackStruct input;
- unsigned int buffer_size, default_buffer_size;
- OSStatus r;
- UInt32 size;
- AudioValueRange latency_range;
+ memset(ss, 0, sizeof(AudioStreamBasicDescription));
- assert(context);
- *stream = NULL;
-
- memset(&ss, 0, sizeof(ss));
- ss.mFormatFlags = 0;
-
- switch (stream_params.format) {
+ switch (stream_params->format) {
case CUBEB_SAMPLE_S16LE:
- ss.mBitsPerChannel = 16;
- ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger;
+ ss->mBitsPerChannel = 16;
+ ss->mFormatFlags = kAudioFormatFlagIsSignedInteger;
break;
case CUBEB_SAMPLE_S16BE:
- ss.mBitsPerChannel = 16;
- ss.mFormatFlags |= kAudioFormatFlagIsSignedInteger |
+ ss->mBitsPerChannel = 16;
+ ss->mFormatFlags = kAudioFormatFlagIsSignedInteger |
kAudioFormatFlagIsBigEndian;
break;
case CUBEB_SAMPLE_FLOAT32LE:
- ss.mBitsPerChannel = 32;
- ss.mFormatFlags |= kAudioFormatFlagIsFloat;
+ ss->mBitsPerChannel = 32;
+ ss->mFormatFlags = kAudioFormatFlagIsFloat;
break;
case CUBEB_SAMPLE_FLOAT32BE:
- ss.mBitsPerChannel = 32;
- ss.mFormatFlags |= kAudioFormatFlagIsFloat |
+ ss->mBitsPerChannel = 32;
+ ss->mFormatFlags = kAudioFormatFlagIsFloat |
kAudioFormatFlagIsBigEndian;
break;
default:
return CUBEB_ERROR_INVALID_FORMAT;
}
- ss.mFormatID = kAudioFormatLinearPCM;
- ss.mFormatFlags |= kLinearPCMFormatFlagIsPacked;
- ss.mSampleRate = stream_params.rate;
- ss.mChannelsPerFrame = stream_params.channels;
+ ss->mFormatID = kAudioFormatLinearPCM;
+ ss->mFormatFlags |= kLinearPCMFormatFlagIsPacked;
+ ss->mSampleRate = stream_params->rate;
+ ss->mChannelsPerFrame = stream_params->channels;
+
+ ss->mBytesPerFrame = (ss->mBitsPerChannel / 8) * ss->mChannelsPerFrame;
+ ss->mFramesPerPacket = 1;
+ ss->mBytesPerPacket = ss->mBytesPerFrame * ss->mFramesPerPacket;
+
+ return CUBEB_OK;
+}
+
+static int
+audiounit_create_unit (AudioUnit * unit, bool is_input,
+ const cubeb_stream_params * stream_params)
+{
+ AudioComponentDescription desc;
+ AudioComponent comp;
+ UInt32 enable;
+ AudioDeviceID devid;
+
+ desc.componentType = kAudioUnitType_Output;
+ desc.componentSubType = CUBEB_AUDIOUNIT_SUBTYPE;
+ desc.componentManufacturer = kAudioUnitManufacturer_Apple;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+ comp = AudioComponentFindNext(NULL, &desc);
+ if (comp == NULL)
+ return CUBEB_ERROR;
+
+ if (AudioComponentInstanceNew(comp, unit) != 0)
+ return CUBEB_ERROR;
+
+ enable = 1;
+ if (AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
+ is_input ? kAudioUnitScope_Input : kAudioUnitScope_Output,
+ is_input ? AU_IN_BUS : AU_OUT_BUS, &enable, sizeof(UInt32)) != noErr)
+ return CUBEB_ERROR;
+
+ enable = 0;
+ if (AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
+ is_input ? kAudioUnitScope_Output : kAudioUnitScope_Input,
+ is_input ? AU_OUT_BUS : AU_IN_BUS, &enable, sizeof(UInt32)) != noErr)
+ return CUBEB_ERROR;
- ss.mBytesPerFrame = (ss.mBitsPerChannel / 8) * ss.mChannelsPerFrame;
- ss.mFramesPerPacket = 1;
- ss.mBytesPerPacket = ss.mBytesPerFrame * ss.mFramesPerPacket;
+ devid = audiounit_get_default_device_id (is_input ? CUBEB_DEVICE_TYPE_INPUT
+ : CUBEB_DEVICE_TYPE_OUTPUT);
+ int err = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ is_input ? AU_IN_BUS : AU_OUT_BUS,
+ &devid, sizeof(AudioDeviceID));
+ if (err != noErr) {
+ return CUBEB_ERROR;
+ }
+
+ return CUBEB_OK;
+}
+
+static int
+audiounit_buflst_init_single_buffer (AudioBufferList * buflst,
+ const AudioStreamBasicDescription * desc, uint32_t frames)
+{
+ size_t size = desc->mBytesPerFrame * frames;
+
+ if ((buflst->mBuffers[0].mData = (float *)malloc(size)) == NULL)
+ return CUBEB_ERROR;
+
+ buflst->mBuffers[0].mNumberChannels = desc->mChannelsPerFrame;
+ buflst->mBuffers[0].mDataByteSize = size;
+ buflst->mNumberBuffers = 1;
+
+ return CUBEB_OK;
+}
+
+static int
+audiounit_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name,
+ cubeb_stream_params * input_stream_params,
+ cubeb_stream_params * output_stream_params,
+ unsigned int latency,
+ cubeb_data_callback data_callback, cubeb_state_callback state_callback,
+ void * user_ptr)
+{
+ cubeb_stream * stm;
+ AudioUnit input_unit;
+ AudioUnit output_unit;
+ int ret;
+ AURenderCallbackStruct aurcbs_in;
+ AURenderCallbackStruct aurcbs_out;
+ UInt32 size;
+#if 0
+ unsigned int buffer_size, default_buffer_size;
+ AudioValueRange latency_range;
+#endif
+
+ assert(context);
+ *stream = NULL;
pthread_mutex_lock(&context->mutex);
if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
pthread_mutex_unlock(&context->mutex);
return CUBEB_ERROR;
}
context->active_streams += 1;
pthread_mutex_unlock(&context->mutex);
- desc.componentType = kAudioUnitType_Output;
- desc.componentSubType =
-#if TARGET_OS_IPHONE
- kAudioUnitSubType_RemoteIO;
-#else
- kAudioUnitSubType_DefaultOutput;
-#endif
- desc.componentManufacturer = kAudioUnitManufacturer_Apple;
- desc.componentFlags = 0;
- desc.componentFlagsMask = 0;
-#if MACOSX_LESS_THAN_106
- comp = FindNextComponent(NULL, &desc);
-#else
- comp = AudioComponentFindNext(NULL, &desc);
-#endif
- assert(comp);
+ /* FIXME: Use device as arguments!? */
+ if (input_stream_params != NULL) {
+ if ((ret = audiounit_create_unit (&input_unit, true,
+ input_stream_params)) != CUBEB_OK)
+ return ret;
+ }
- stm = calloc(1, sizeof(*stm));
+ if (output_stream_params != NULL) {
+ if ((ret = audiounit_create_unit (&output_unit, false,
+ output_stream_params)) != CUBEB_OK)
+ return ret;
+ }
+
+ stm = calloc(1, sizeof(cubeb_stream));
assert(stm);
+ /* These could be different in the future if we have both
+ * fullduplex stream and different devices for input vs output */
+ stm->input_unit = (input_stream_params != NULL) ? input_unit : NULL;
+ stm->output_unit = (output_stream_params != NULL) ? output_unit : NULL;
stm->context = context;
stm->data_callback = data_callback;
stm->state_callback = state_callback;
stm->user_ptr = user_ptr;
stm->device_changed_callback = NULL;
- stm->sample_spec = ss;
-
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
- r = pthread_mutex_init(&stm->mutex, &attr);
+ pthread_mutex_init(&stm->mutex, &attr);
pthread_mutexattr_destroy(&attr);
- assert(r == 0);
+ stm->draining = false;
stm->frames_played = 0;
stm->frames_queued = 0;
stm->current_latency_frames = 0;
stm->hw_latency_frames = UINT64_MAX;
-#if MACOSX_LESS_THAN_106
- r = OpenAComponent(comp, &stm->unit);
-#else
- r = AudioComponentInstanceNew(comp, &stm->unit);
-#endif
- if (r != 0) {
- audiounit_stream_destroy(stm);
- return CUBEB_ERROR;
+ memset(&stm->input_buflst, 0, sizeof(AudioBufferList));
+
+ /* FIXME: Set FramesPerBuffer? */
+ if (input_stream_params != NULL) {
+ size = sizeof(UInt32);
+ if (AudioUnitGetProperty(stm->input_unit, kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Input, AU_IN_BUS, &stm->input_fpb, &size) != 0) {
+ audiounit_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
}
- input.inputProc = audiounit_output_callback;
- input.inputProcRefCon = stm;
- r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_SetRenderCallback,
- kAudioUnitScope_Global, 0, &input, sizeof(input));
- if (r != 0) {
- audiounit_stream_destroy(stm);
- return CUBEB_ERROR;
+ /* FIXME: Set Render quality? */
+
+ /* Set Stream Format! */
+ if (input_stream_params != NULL) {
+ AudioStreamBasicDescription src_desc, hw_desc;
+
+ size = sizeof(AudioStreamBasicDescription);
+ if (AudioUnitGetProperty(stm->input_unit, kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input, AU_IN_BUS, &hw_desc, &size)) {
+ audiounit_stream_destroy(stm);
+ return ret;
+ }
+ if ((ret = audio_stream_desc_init(&stm->input_desc,
+ input_stream_params)) != CUBEB_OK) {
+ audiounit_stream_destroy(stm);
+ return ret;
+ }
+
+ src_desc = stm->input_desc;
+ src_desc.mSampleRate = hw_desc.mSampleRate;
+
+ if (AudioUnitSetProperty(stm->input_unit, kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output, AU_IN_BUS,
+ &src_desc, sizeof(AudioStreamBasicDescription)) != 0) {
+ audiounit_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
+ if (AudioUnitSetProperty(stm->input_unit, kAudioUnitProperty_MaximumFramesPerSlice,
+ kAudioUnitScope_Output, AU_IN_BUS, &stm->input_fpb, sizeof(UInt32))) {
+ audiounit_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
+ if (src_desc.mSampleRate != stm->input_desc.mSampleRate) {
+ UInt32 value;
+ if (AudioConverterNew(&src_desc, &stm->input_desc,
+ &stm->input_converter)) {
+ audiounit_stream_destroy(stm);
+ return ret;
+ }
+
+ value = kAudioConverterQuality_High;
+ if (AudioConverterSetProperty(stm->input_converter,
+ kAudioConverterSampleRateConverterQuality, sizeof(UInt32), &value)) {
+ audiounit_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
+ }
+
+ if (audiounit_buflst_init_single_buffer(&stm->input_buflst,
+ &src_desc, stm->input_fpb) != CUBEB_OK) {
+ audiounit_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
+ }
+ if (output_stream_params != NULL) {
+ if ((ret = audio_stream_desc_init(&stm->output_desc, output_stream_params)) != CUBEB_OK) {
+ audiounit_stream_destroy(stm);
+ return ret;
+ }
+ if (AudioUnitSetProperty(stm->output_unit, kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input, AU_OUT_BUS,
+ &stm->output_desc, sizeof(AudioStreamBasicDescription)) != 0) {
+ audiounit_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
}
+ /* Set IO proc! */
+ aurcbs_in.inputProc = audiounit_input_callback;
+ aurcbs_in.inputProcRefCon = stm;
+ if (input_stream_params != NULL) {
+ assert(stm->input_unit != NULL);
+ if (AudioUnitSetProperty(stm->input_unit, kAudioOutputUnitProperty_SetInputCallback,
+ kAudioUnitScope_Global, AU_OUT_BUS, &aurcbs_in, sizeof(aurcbs_in)) != 0) {
+ audiounit_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
+ }
+ aurcbs_out.inputProc = audiounit_output_callback;
+ aurcbs_out.inputProcRefCon = stm;
+ if (output_stream_params != NULL) {
+ assert(stm->output_unit != NULL);
+ if (AudioUnitSetProperty(stm->output_unit, kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Global, AU_OUT_BUS, &aurcbs_out, sizeof(aurcbs_out)) != 0) {
+ audiounit_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
+ }
+
+ // Setting the latency doesn't work well for USB headsets (eg. plantronics).
+ // Keep the default latency for now.
+#if 0
buffer_size = latency / 1000.0 * ss.mSampleRate;
/* Get the range of latency this particular device can work with, and clamp
* the requested latency to this acceptable range. */
#if !TARGET_OS_IPHONE
if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
@@ -679,52 +895,44 @@ audiounit_stream_init(cubeb * context, c
buffer_size = (unsigned int) latency_range.mMaximum;
}
/**
* Get the default buffer size. If our latency request is below the default,
* set it. Otherwise, use the default latency.
**/
size = sizeof(default_buffer_size);
- r = AudioUnitGetProperty(stm->unit, kAudioDevicePropertyBufferFrameSize,
- kAudioUnitScope_Output, 0, &default_buffer_size, &size);
-
- if (r != 0) {
+ if (AudioUnitGetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Output, 0, &default_buffer_size, &size) != 0) {
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
+
+ if (buffer_size < default_buffer_size) {
+ /* Set the maximum number of frame that the render callback will ask for,
+ * effectively setting the latency of the stream. This is process-wide. */
+ if (AudioUnitSetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size)) != 0) {
+ audiounit_stream_destroy(stm);
+ return CUBEB_ERROR;
+ }
+ }
#else // TARGET_OS_IPHONE
//TODO: [[AVAudioSession sharedInstance] inputLatency]
// http://stackoverflow.com/questions/13157523/kaudiodevicepropertybufferframesize-replacement-for-ios
#endif
-
- // Setting the latency doesn't work well for USB headsets (eg. plantronics).
- // Keep the default latency for now.
-#if 0
- if (buffer_size < default_buffer_size) {
- /* Set the maximum number of frame that the render callback will ask for,
- * effectively setting the latency of the stream. This is process-wide. */
- r = AudioUnitSetProperty(stm->unit, kAudioDevicePropertyBufferFrameSize,
- kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size));
- if (r != 0) {
- audiounit_stream_destroy(stm);
- return CUBEB_ERROR;
- }
- }
#endif
- r = AudioUnitSetProperty(stm->unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input,
- 0, &ss, sizeof(ss));
- if (r != 0) {
+ if (stm->input_unit != NULL &&
+ AudioUnitInitialize(stm->input_unit) != 0) {
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
-
- r = AudioUnitInitialize(stm->unit);
- if (r != 0) {
+ if (stm->output_unit != NULL &&
+ AudioUnitInitialize(stm->output_unit) != 0) {
audiounit_stream_destroy(stm);
return CUBEB_ERROR;
}
*stream = stm;
#if !TARGET_OS_IPHONE
/* we dont' check the return value here, because we want to be able to play
@@ -737,24 +945,38 @@ audiounit_stream_init(cubeb * context, c
static void
audiounit_stream_destroy(cubeb_stream * stm)
{
int r;
stm->shutdown = 1;
- if (stm->unit) {
- AudioOutputUnitStop(stm->unit);
- AudioUnitUninitialize(stm->unit);
-#if MACOSX_LESS_THAN_106
- CloseComponent(stm->unit);
-#else
- AudioComponentInstanceDispose(stm->unit);
-#endif
+ if (stm->input_unit != NULL) {
+ if (stm->input_unit != stm->output_unit) {
+ AudioOutputUnitStop(stm->input_unit);
+ AudioUnitUninitialize(stm->input_unit);
+ AudioComponentInstanceDispose(stm->input_unit);
+ }
+ stm->input_unit = NULL;
+ }
+ if (stm->input_converter) {
+ AudioConverterDispose(stm->input_converter);
+ stm->input_converter = NULL;
+ }
+ if (stm->input_buflst.mBuffers[0].mData) {
+ free(stm->input_buflst.mBuffers[0].mData);
+ stm->input_buflst.mBuffers[0].mData = NULL;
+ }
+
+ if (stm->output_unit != NULL) {
+ AudioOutputUnitStop(stm->output_unit);
+ AudioUnitUninitialize(stm->output_unit);
+ AudioComponentInstanceDispose(stm->output_unit);
+ stm->output_unit = NULL;
}
#if !TARGET_OS_IPHONE
audiounit_uninstall_device_changed_callback(stm);
#endif
r = pthread_mutex_destroy(&stm->mutex);
assert(r == 0);
@@ -766,28 +988,40 @@ audiounit_stream_destroy(cubeb_stream *
free(stm);
}
static int
audiounit_stream_start(cubeb_stream * stm)
{
OSStatus r;
- r = AudioOutputUnitStart(stm->unit);
- assert(r == 0);
+ if (stm->input_unit != NULL) {
+ r = AudioOutputUnitStart(stm->input_unit);
+ assert(r == 0);
+ }
+ if (stm->output_unit != NULL && stm->input_unit != stm->output_unit) {
+ r = AudioOutputUnitStart(stm->output_unit);
+ assert(r == 0);
+ }
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
return CUBEB_OK;
}
static int
audiounit_stream_stop(cubeb_stream * stm)
{
OSStatus r;
- r = AudioOutputUnitStop(stm->unit);
- assert(r == 0);
+ if (stm->output_unit != NULL) {
+ r = AudioOutputUnitStop(stm->output_unit);
+ assert(r == 0);
+ }
+ if (stm->input_unit != NULL && stm->input_unit != stm->output_unit) {
+ r = AudioOutputUnitStop(stm->input_unit);
+ assert(r == 0);
+ }
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
return CUBEB_OK;
}
static int
audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
pthread_mutex_lock(&stm->mutex);
@@ -824,17 +1058,17 @@ audiounit_stream_get_latency(cubeb_strea
r = audiounit_get_output_device_id(&output_device_id);
if (r != noErr) {
pthread_mutex_unlock(&stm->mutex);
return CUBEB_ERROR;
}
size = sizeof(unit_latency_sec);
- r = AudioUnitGetProperty(stm->unit,
+ r = AudioUnitGetProperty(stm->output_unit,
kAudioUnitProperty_Latency,
kAudioUnitScope_Global,
0,
&unit_latency_sec,
&size);
if (r != noErr) {
pthread_mutex_unlock(&stm->mutex);
return CUBEB_ERROR;
@@ -861,46 +1095,46 @@ audiounit_stream_get_latency(cubeb_strea
&device_safety_offset);
if (r != noErr) {
pthread_mutex_unlock(&stm->mutex);
return CUBEB_ERROR;
}
/* This part is fixed and depend on the stream parameter and the hardware. */
stm->hw_latency_frames =
- (uint32_t)(unit_latency_sec * stm->sample_spec.mSampleRate)
+ (uint32_t)(unit_latency_sec * stm->output_desc.mSampleRate)
+ device_latency_frames
+ device_safety_offset;
}
*latency = stm->hw_latency_frames + stm->current_latency_frames;
pthread_mutex_unlock(&stm->mutex);
return CUBEB_OK;
#endif
}
int audiounit_stream_set_volume(cubeb_stream * stm, float volume)
{
OSStatus r;
- r = AudioUnitSetParameter(stm->unit,
+ r = AudioUnitSetParameter(stm->output_unit,
kHALOutputParam_Volume,
kAudioUnitScope_Global,
0, volume, 0);
if (r != noErr) {
return CUBEB_ERROR;
}
return CUBEB_OK;
}
int audiounit_stream_set_panning(cubeb_stream * stm, float panning)
{
- if (stm->sample_spec.mChannelsPerFrame > 2) {
+ if (stm->output_desc.mChannelsPerFrame > 2) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
pthread_mutex_lock(&stm->mutex);
stm->panning = panning;
pthread_mutex_unlock(&stm->mutex);
return CUBEB_OK;