|
|
|
@ -27,6 +27,7 @@
|
|
|
|
|
#endif
|
|
|
|
|
#include "cubeb_resampler.h"
|
|
|
|
|
#include "cubeb_ring_array.h"
|
|
|
|
|
#include "cubeb_utils.h"
|
|
|
|
|
|
|
|
|
|
#if !defined(kCFCoreFoundationVersionNumber10_7)
|
|
|
|
|
/* From CoreFoundation CFBase.h */
|
|
|
|
@ -56,7 +57,7 @@ typedef UInt32 AudioFormatFlags;
|
|
|
|
|
#define CUBEB_AUDIOUNIT_SUBTYPE kAudioUnitSubType_HALOutput
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
//#define LOGGING_ENABLED
|
|
|
|
|
// #define LOGGING_ENABLED
|
|
|
|
|
#ifdef LOGGING_ENABLED
|
|
|
|
|
#define LOG(...) do { \
|
|
|
|
|
fprintf(stderr, __VA_ARGS__); \
|
|
|
|
@ -65,10 +66,10 @@ typedef UInt32 AudioFormatFlags;
|
|
|
|
|
#define LOG(...)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static struct cubeb_ops const audiounit_ops;
|
|
|
|
|
extern cubeb_ops const audiounit_ops;
|
|
|
|
|
|
|
|
|
|
struct cubeb {
|
|
|
|
|
struct cubeb_ops const * ops;
|
|
|
|
|
cubeb_ops const * ops;
|
|
|
|
|
pthread_mutex_t mutex;
|
|
|
|
|
int active_streams;
|
|
|
|
|
int limit_streams;
|
|
|
|
@ -165,7 +166,7 @@ audiounit_input_callback(void * user_ptr,
|
|
|
|
|
UInt32 input_frames,
|
|
|
|
|
AudioBufferList * bufs)
|
|
|
|
|
{
|
|
|
|
|
cubeb_stream * stm = user_ptr;
|
|
|
|
|
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
|
|
|
|
|
long outframes, frames;
|
|
|
|
|
|
|
|
|
|
pthread_mutex_lock(&stm->mutex);
|
|
|
|
@ -253,7 +254,7 @@ audiounit_output_callback(void * user_ptr,
|
|
|
|
|
assert(AU_OUT_BUS == bus);
|
|
|
|
|
assert(outBufferList->mNumberBuffers == 1);
|
|
|
|
|
|
|
|
|
|
cubeb_stream * stm = user_ptr;
|
|
|
|
|
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
|
|
|
|
|
|
|
|
|
|
LOG("- output(%p): buffers %d, size %d, channels %d, frames %d\n", stm,
|
|
|
|
|
outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize,
|
|
|
|
@ -270,6 +271,10 @@ audiounit_output_callback(void * user_ptr,
|
|
|
|
|
return noErr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG("- output(%p): buffers %d, size %d, channels %d, frames %d\n", stm,
|
|
|
|
|
outBufferList->mNumberBuffers, outBufferList->mBuffers[0].mDataByteSize,
|
|
|
|
|
outBufferList->mBuffers[0].mNumberChannels, output_frames);
|
|
|
|
|
|
|
|
|
|
stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
|
|
|
|
|
if (stm->draining) {
|
|
|
|
|
OSStatus r = AudioOutputUnitStop(stm->output_unit);
|
|
|
|
@ -288,31 +293,28 @@ audiounit_output_callback(void * user_ptr,
|
|
|
|
|
/* If Full duplex get also input buffer */
|
|
|
|
|
AudioBuffer * input_aud_buf = NULL;
|
|
|
|
|
if (stm->input_unit != NULL) {
|
|
|
|
|
/* Output callback came first */
|
|
|
|
|
if (stm->frames_read == 0) {
|
|
|
|
|
LOG("Output callback came first send silent.\n");
|
|
|
|
|
audiounit_make_silent(&outBufferList->mBuffers[0]);
|
|
|
|
|
pthread_mutex_unlock(&stm->mutex);
|
|
|
|
|
return noErr;
|
|
|
|
|
}
|
|
|
|
|
/* Input samples stored previously in input callback. */
|
|
|
|
|
input_aud_buf = ring_array_get_data_buffer(&stm->input_buffer_array);
|
|
|
|
|
if (input_aud_buf == NULL) {
|
|
|
|
|
LOG("Requested more output than input. "
|
|
|
|
|
"This is either a hole or we are after a stream stop and input thread stopped before output\n");
|
|
|
|
|
/* Provide silent input. Other than that we could provide silent output and exit without
|
|
|
|
|
* calling user callback. I do not prefer it because the user loose the control of
|
|
|
|
|
* calling user callback. This is not preferred because the user loose the control of
|
|
|
|
|
* the output. Also resampler loose frame counting and produce less frame than
|
|
|
|
|
* expected at some point in the future breaking an assert. */
|
|
|
|
|
LOG("Input hole. Requested more input than existing.\n");
|
|
|
|
|
|
|
|
|
|
/* Avoid here to allocate new memory since we are inside callback. Use the existing
|
|
|
|
|
* allocated buffers since the ring array is empty and the buffer is not used. */
|
|
|
|
|
input_aud_buf = ring_array_get_dummy_buffer(&stm->input_buffer_array);
|
|
|
|
|
/* Output callback came first */
|
|
|
|
|
if (stm->frames_read == 0) {
|
|
|
|
|
LOG("Output callback came first send silent.\n");
|
|
|
|
|
}
|
|
|
|
|
audiounit_make_silent(input_aud_buf);
|
|
|
|
|
}
|
|
|
|
|
// The input buffer
|
|
|
|
|
input_buffer = input_aud_buf->mData;
|
|
|
|
|
// Number of input frames in the buffer
|
|
|
|
|
input_frames = stm->input_buffer_frames;
|
|
|
|
|
assert(stm->frames_read > 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Call user callback through resampler. */
|
|
|
|
@ -358,7 +360,8 @@ audiounit_output_callback(void * user_ptr,
|
|
|
|
|
return noErr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*static*/ int
|
|
|
|
|
extern "C" {
|
|
|
|
|
int
|
|
|
|
|
audiounit_init(cubeb ** context, char const * context_name)
|
|
|
|
|
{
|
|
|
|
|
cubeb * ctx;
|
|
|
|
@ -366,8 +369,9 @@ audiounit_init(cubeb ** context, char const * context_name)
|
|
|
|
|
|
|
|
|
|
*context = NULL;
|
|
|
|
|
|
|
|
|
|
ctx = calloc(1, sizeof(*ctx));
|
|
|
|
|
ctx = new cubeb;
|
|
|
|
|
assert(ctx);
|
|
|
|
|
PodZero(ctx, 1);
|
|
|
|
|
|
|
|
|
|
ctx->ops = &audiounit_ops;
|
|
|
|
|
|
|
|
|
@ -385,6 +389,7 @@ audiounit_init(cubeb ** context, char const * context_name)
|
|
|
|
|
|
|
|
|
|
return CUBEB_OK;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static char const *
|
|
|
|
|
audiounit_get_backend_id(cubeb * ctx)
|
|
|
|
@ -711,7 +716,7 @@ audiounit_get_min_latency(cubeb * ctx, cubeb_stream_params params, uint32_t * la
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*latency_ms = (latency_range.mMinimum * 1000 + params.rate - 1) / params.rate;
|
|
|
|
|
*latency_ms = ceil((latency_range.mMinimum * 1000 ) / params.rate);
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return CUBEB_OK;
|
|
|
|
@ -750,7 +755,7 @@ audiounit_get_preferred_sample_rate(cubeb * ctx, uint32_t * rate)
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*rate = (uint32_t)fsamplerate;
|
|
|
|
|
*rate = static_cast<uint32_t>(fsamplerate);
|
|
|
|
|
#endif
|
|
|
|
|
return CUBEB_OK;
|
|
|
|
|
}
|
|
|
|
@ -773,7 +778,7 @@ audiounit_destroy(cubeb * ctx)
|
|
|
|
|
int r = pthread_mutex_destroy(&ctx->mutex);
|
|
|
|
|
assert(r == 0);
|
|
|
|
|
|
|
|
|
|
free(ctx);
|
|
|
|
|
delete ctx;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void audiounit_stream_destroy(cubeb_stream * stm);
|
|
|
|
@ -862,7 +867,7 @@ audiounit_create_unit(AudioUnit * unit,
|
|
|
|
|
devid = audiounit_get_default_device_id(is_input ? CUBEB_DEVICE_TYPE_INPUT
|
|
|
|
|
: CUBEB_DEVICE_TYPE_OUTPUT);
|
|
|
|
|
} else {
|
|
|
|
|
devid = (AudioDeviceID)device;
|
|
|
|
|
devid = *(static_cast<AudioDeviceID*>(device));
|
|
|
|
|
}
|
|
|
|
|
int err = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice,
|
|
|
|
|
kAudioUnitScope_Global,
|
|
|
|
@ -945,8 +950,9 @@ audiounit_stream_init(cubeb * context,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
stm = calloc(1, sizeof(cubeb_stream));
|
|
|
|
|
stm = new cubeb_stream;
|
|
|
|
|
assert(stm);
|
|
|
|
|
PodZero(stm, 1);
|
|
|
|
|
|
|
|
|
|
/* These could be different in the future if we have both
|
|
|
|
|
* full-duplex stream and different devices for input vs output. */
|
|
|
|
@ -970,27 +976,6 @@ audiounit_stream_init(cubeb * context,
|
|
|
|
|
|
|
|
|
|
/* Setup Input Stream! */
|
|
|
|
|
if (input_stream_params != NULL) {
|
|
|
|
|
size = sizeof(UInt32);
|
|
|
|
|
if (AudioUnitGetProperty(stm->input_unit,
|
|
|
|
|
kAudioDevicePropertyBufferFrameSize,
|
|
|
|
|
kAudioUnitScope_Input,
|
|
|
|
|
AU_IN_BUS,
|
|
|
|
|
&stm->input_buffer_frames,
|
|
|
|
|
&size) != 0) {
|
|
|
|
|
audiounit_stream_destroy(stm);
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AudioUnitSetProperty(stm->input_unit,
|
|
|
|
|
kAudioDevicePropertyBufferFrameSize,
|
|
|
|
|
kAudioUnitScope_Output,
|
|
|
|
|
AU_IN_BUS,
|
|
|
|
|
&stm->input_buffer_frames,
|
|
|
|
|
size) != 0) {
|
|
|
|
|
audiounit_stream_destroy(stm);
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get input device sample rate. */
|
|
|
|
|
AudioStreamBasicDescription input_hw_desc;
|
|
|
|
|
size = sizeof(AudioStreamBasicDescription);
|
|
|
|
@ -1012,6 +997,19 @@ audiounit_stream_init(cubeb * context,
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use latency to calculate buffer size
|
|
|
|
|
stm->input_buffer_frames = (latency / 1000.0) * stm->input_hw_rate;
|
|
|
|
|
LOG("Calculated input number of frames %u for latency %u\n", stm->input_buffer_frames, latency);
|
|
|
|
|
if (AudioUnitSetProperty(stm->input_unit,
|
|
|
|
|
kAudioDevicePropertyBufferFrameSize,
|
|
|
|
|
kAudioUnitScope_Output,
|
|
|
|
|
AU_IN_BUS,
|
|
|
|
|
&stm->input_buffer_frames,
|
|
|
|
|
sizeof(UInt32)) != 0) {
|
|
|
|
|
audiounit_stream_destroy(stm);
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AudioStreamBasicDescription src_desc = stm->input_desc;
|
|
|
|
|
/* Input AudioUnit must be configured with device's sample rate.
|
|
|
|
|
we will resample inside input callback. */
|
|
|
|
@ -1072,6 +1070,20 @@ audiounit_stream_init(cubeb * context,
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Get output device sample rate. */
|
|
|
|
|
AudioStreamBasicDescription output_hw_desc;
|
|
|
|
|
size = sizeof(AudioStreamBasicDescription);
|
|
|
|
|
memset(&output_hw_desc, 0, size);
|
|
|
|
|
if (AudioUnitGetProperty(stm->output_unit,
|
|
|
|
|
kAudioUnitProperty_StreamFormat,
|
|
|
|
|
kAudioUnitScope_Output,
|
|
|
|
|
AU_OUT_BUS,
|
|
|
|
|
&output_hw_desc,
|
|
|
|
|
&size) != 0) {
|
|
|
|
|
audiounit_stream_destroy(stm);
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AudioUnitSetProperty(stm->output_unit,
|
|
|
|
|
kAudioUnitProperty_StreamFormat,
|
|
|
|
|
kAudioUnitScope_Input,
|
|
|
|
@ -1082,6 +1094,21 @@ audiounit_stream_init(cubeb * context,
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Use latency to set number of frames in out buffer. Use the
|
|
|
|
|
// device sampling rate, internal resampler of audiounit will
|
|
|
|
|
// calculate the expected number of frames.
|
|
|
|
|
uint32_t output_buffer_frames = (latency / 1000.0) * output_hw_desc.mSampleRate;
|
|
|
|
|
LOG("Calculated output number of frames %u for latency %u\n", output_buffer_frames, latency);
|
|
|
|
|
if (AudioUnitSetProperty(stm->output_unit,
|
|
|
|
|
kAudioDevicePropertyBufferFrameSize,
|
|
|
|
|
kAudioUnitScope_Input,
|
|
|
|
|
AU_OUT_BUS,
|
|
|
|
|
&output_buffer_frames,
|
|
|
|
|
sizeof(output_buffer_frames)) != 0) {
|
|
|
|
|
audiounit_stream_destroy(stm);
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
assert(stm->output_unit != NULL);
|
|
|
|
|
aurcbs_out.inputProc = audiounit_output_callback;
|
|
|
|
|
aurcbs_out.inputProcRefCon = stm;
|
|
|
|
@ -1225,7 +1252,7 @@ audiounit_stream_destroy(cubeb_stream * stm)
|
|
|
|
|
stm->context->active_streams -= 1;
|
|
|
|
|
pthread_mutex_unlock(&stm->context->mutex);
|
|
|
|
|
|
|
|
|
|
free(stm);
|
|
|
|
|
delete stm;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
@ -1347,7 +1374,7 @@ audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
|
|
|
|
|
|
|
|
|
|
/* This part is fixed and depend on the stream parameter and the hardware. */
|
|
|
|
|
stm->hw_latency_frames =
|
|
|
|
|
(uint32_t)(unit_latency_sec * stm->output_desc.mSampleRate)
|
|
|
|
|
static_cast<uint32_t>(unit_latency_sec * stm->output_desc.mSampleRate)
|
|
|
|
|
+ device_latency_frames
|
|
|
|
|
+ device_safety_offset;
|
|
|
|
|
}
|
|
|
|
@ -1419,10 +1446,11 @@ int audiounit_stream_get_current_device(cubeb_stream * stm,
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*device = malloc(sizeof(cubeb_device));
|
|
|
|
|
*device = new cubeb_device;
|
|
|
|
|
if (!*device) {
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
PodZero(*device, 1);
|
|
|
|
|
|
|
|
|
|
size = sizeof(UInt32);
|
|
|
|
|
/* This fails with some USB headset, so simply return an empty string. */
|
|
|
|
@ -1434,12 +1462,7 @@ int audiounit_stream_get_current_device(cubeb_stream * stm,
|
|
|
|
|
data = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(*device)->output_name = malloc(size + 1);
|
|
|
|
|
if (!(*device)->output_name) {
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(*device)->output_name = malloc(size + 1);
|
|
|
|
|
(*device)->output_name = new char[size + 1];
|
|
|
|
|
if (!(*device)->output_name) {
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
@ -1465,7 +1488,7 @@ int audiounit_stream_get_current_device(cubeb_stream * stm,
|
|
|
|
|
data = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(*device)->input_name = malloc(size + 1);
|
|
|
|
|
(*device)->input_name = new char[size + 1];
|
|
|
|
|
if (!(*device)->input_name) {
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
@ -1486,9 +1509,9 @@ int audiounit_stream_get_current_device(cubeb_stream * stm,
|
|
|
|
|
int audiounit_stream_device_destroy(cubeb_stream * stream,
|
|
|
|
|
cubeb_device * device)
|
|
|
|
|
{
|
|
|
|
|
free(device->output_name);
|
|
|
|
|
free(device->input_name);
|
|
|
|
|
free(device);
|
|
|
|
|
delete [] device->output_name;
|
|
|
|
|
delete [] device->input_name;
|
|
|
|
|
delete device;
|
|
|
|
|
return CUBEB_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1528,17 +1551,17 @@ audiounit_get_devices(AudioObjectID ** devices, uint32_t * count)
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*count = (uint32_t)(size / sizeof(AudioObjectID));
|
|
|
|
|
*count = static_cast<uint32_t>(size / sizeof(AudioObjectID));
|
|
|
|
|
if (size >= sizeof(AudioObjectID)) {
|
|
|
|
|
if (*devices != NULL) {
|
|
|
|
|
free(*devices);
|
|
|
|
|
delete [] (*devices);
|
|
|
|
|
}
|
|
|
|
|
*devices = malloc(size);
|
|
|
|
|
memset(*devices, 0, size);
|
|
|
|
|
*devices = new AudioObjectID[*count];
|
|
|
|
|
PodZero(*devices, *count);
|
|
|
|
|
|
|
|
|
|
ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, (void *)*devices);
|
|
|
|
|
if (ret != noErr) {
|
|
|
|
|
free(*devices);
|
|
|
|
|
delete [] (*devices);
|
|
|
|
|
*devices = NULL;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
@ -1560,10 +1583,10 @@ audiounit_strref_to_cstr_utf8(CFStringRef strref)
|
|
|
|
|
|
|
|
|
|
len = CFStringGetLength(strref);
|
|
|
|
|
size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
|
|
|
|
|
ret = malloc(size);
|
|
|
|
|
ret = new char[size];
|
|
|
|
|
|
|
|
|
|
if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) {
|
|
|
|
|
free(ret);
|
|
|
|
|
delete [] ret;
|
|
|
|
|
ret = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1580,7 +1603,7 @@ audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope)
|
|
|
|
|
adr.mSelector = kAudioDevicePropertyStreamConfiguration;
|
|
|
|
|
|
|
|
|
|
if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && size > 0) {
|
|
|
|
|
AudioBufferList * list = alloca(size);
|
|
|
|
|
AudioBufferList * list = static_cast<AudioBufferList *>(alloca(size));
|
|
|
|
|
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == noErr) {
|
|
|
|
|
for (i = 0; i < list->mNumberBuffers; i++)
|
|
|
|
|
ret += list->mBuffers[i].mNumberChannels;
|
|
|
|
@ -1611,7 +1634,7 @@ audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope
|
|
|
|
|
if (AudioObjectHasProperty(devid, &adr) &&
|
|
|
|
|
AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) {
|
|
|
|
|
uint32_t i, count = size / sizeof(AudioValueRange);
|
|
|
|
|
AudioValueRange * ranges = malloc(size);
|
|
|
|
|
AudioValueRange * ranges = new AudioValueRange[count];
|
|
|
|
|
range.mMinimum = 9999999999.0;
|
|
|
|
|
range.mMaximum = 0.0;
|
|
|
|
|
if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, ranges) == noErr) {
|
|
|
|
@ -1622,9 +1645,9 @@ audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope
|
|
|
|
|
range.mMinimum = ranges[i].mMinimum;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
free(ranges);
|
|
|
|
|
*max = (uint32_t)range.mMaximum;
|
|
|
|
|
*min = (uint32_t)range.mMinimum;
|
|
|
|
|
delete [] ranges;
|
|
|
|
|
*max = static_cast<uint32_t>(range.mMaximum);
|
|
|
|
|
*min = static_cast<uint32_t>(range.mMinimum);
|
|
|
|
|
} else {
|
|
|
|
|
*min = *max = 0;
|
|
|
|
|
}
|
|
|
|
@ -1683,7 +1706,8 @@ audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type)
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = calloc(1, sizeof(cubeb_device_info));
|
|
|
|
|
ret = new cubeb_device_info;
|
|
|
|
|
PodZero(ret, 1);
|
|
|
|
|
|
|
|
|
|
size = sizeof(CFStringRef);
|
|
|
|
|
adr.mSelector = kAudioDevicePropertyDeviceUID;
|
|
|
|
@ -1733,7 +1757,7 @@ audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type)
|
|
|
|
|
CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
|
|
|
|
|
|
|
|
|
|
ret->max_channels = ch;
|
|
|
|
|
ret->format = CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
|
|
|
|
|
ret->format = (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
|
|
|
|
|
/* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */
|
|
|
|
|
ret->default_format = CUBEB_DEVICE_FMT_F32NE;
|
|
|
|
|
audiounit_get_available_samplerate(devid, adr.mScope,
|
|
|
|
@ -1766,8 +1790,8 @@ audiounit_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|
|
|
|
return CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*collection = malloc(sizeof(cubeb_device_collection) +
|
|
|
|
|
sizeof(cubeb_device_info*) * (hwdevcount > 0 ? hwdevcount - 1 : 0));
|
|
|
|
|
*collection = static_cast<cubeb_device_collection *>(malloc(sizeof(cubeb_device_collection) +
|
|
|
|
|
sizeof(cubeb_device_info*) * (hwdevcount > 0 ? hwdevcount - 1 : 0)));
|
|
|
|
|
(*collection)->count = 0;
|
|
|
|
|
|
|
|
|
|
if (hwdevcount > 0) {
|
|
|
|
@ -1788,7 +1812,7 @@ audiounit_enumerate_devices(cubeb * context, cubeb_device_type type,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
free(hwdevs);
|
|
|
|
|
delete [] hwdevs;
|
|
|
|
|
|
|
|
|
|
return CUBEB_OK;
|
|
|
|
|
}
|
|
|
|
@ -1825,7 +1849,7 @@ audiounit_get_devices_of_type(cubeb_device_type devtype, AudioObjectID ** devid_
|
|
|
|
|
|
|
|
|
|
if (devtype == (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) {
|
|
|
|
|
if (devid_array) {
|
|
|
|
|
*devid_array = calloc(count, sizeof(AudioObjectID));
|
|
|
|
|
*devid_array = new AudioObjectID[count];
|
|
|
|
|
assert(*devid_array);
|
|
|
|
|
memcpy(*devid_array, &devices, count * sizeof(AudioObjectID));
|
|
|
|
|
}
|
|
|
|
@ -1847,7 +1871,7 @@ audiounit_get_devices_of_type(cubeb_device_type devtype, AudioObjectID ** devid_
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (devid_array && dev_count > 0) {
|
|
|
|
|
*devid_array = calloc(dev_count, sizeof(AudioObjectID));
|
|
|
|
|
*devid_array = static_cast<AudioObjectID *>(calloc(dev_count, sizeof(AudioObjectID)));
|
|
|
|
|
assert(*devid_array);
|
|
|
|
|
memcpy(*devid_array, &devices_in_scope, dev_count * sizeof(AudioObjectID));
|
|
|
|
|
}
|
|
|
|
@ -1872,7 +1896,7 @@ audiounit_collection_changed_callback(AudioObjectID inObjectID,
|
|
|
|
|
const AudioObjectPropertyAddress * inAddresses,
|
|
|
|
|
void * inClientData)
|
|
|
|
|
{
|
|
|
|
|
cubeb * context = inClientData;
|
|
|
|
|
cubeb * context = static_cast<cubeb *>(inClientData);
|
|
|
|
|
pthread_mutex_lock(&context->mutex);
|
|
|
|
|
if (context->collection_changed_callback == NULL) {
|
|
|
|
|
/* Listener removed while waiting in mutex, abort. */
|
|
|
|
@ -1889,14 +1913,14 @@ audiounit_collection_changed_callback(AudioObjectID inObjectID,
|
|
|
|
|
if (context->devtype_device_count == new_number_of_devices &&
|
|
|
|
|
audiounit_equal_arrays(devices, context->devtype_device_array, new_number_of_devices)) {
|
|
|
|
|
/* Device changed for the other scope, ignore. */
|
|
|
|
|
free(devices);
|
|
|
|
|
delete [] devices;
|
|
|
|
|
pthread_mutex_unlock(&context->mutex);
|
|
|
|
|
return noErr;
|
|
|
|
|
}
|
|
|
|
|
/* Device on desired scope changed, reset counter and array. */
|
|
|
|
|
context->devtype_device_count = new_number_of_devices;
|
|
|
|
|
/* Free the old array before replace. */
|
|
|
|
|
free(context->devtype_device_array);
|
|
|
|
|
delete [] context->devtype_device_array;
|
|
|
|
|
context->devtype_device_array = devices;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -1957,12 +1981,12 @@ audiounit_remove_device_listener(cubeb * context)
|
|
|
|
|
context);
|
|
|
|
|
if (ret == noErr) {
|
|
|
|
|
/* Reset all values. */
|
|
|
|
|
context->collection_changed_devtype = 0;
|
|
|
|
|
context->collection_changed_devtype = CUBEB_DEVICE_TYPE_UNKNOWN;
|
|
|
|
|
context->collection_changed_callback = NULL;
|
|
|
|
|
context->collection_changed_user_ptr = NULL;
|
|
|
|
|
context->devtype_device_count = 0;
|
|
|
|
|
if (context->devtype_device_array) {
|
|
|
|
|
free(context->devtype_device_array);
|
|
|
|
|
delete [] context->devtype_device_array;
|
|
|
|
|
context->devtype_device_array = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1987,24 +2011,24 @@ int audiounit_register_device_collection_changed(cubeb * context,
|
|
|
|
|
return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct cubeb_ops const audiounit_ops = {
|
|
|
|
|
.init = audiounit_init,
|
|
|
|
|
.get_backend_id = audiounit_get_backend_id,
|
|
|
|
|
.get_max_channel_count = audiounit_get_max_channel_count,
|
|
|
|
|
.get_min_latency = audiounit_get_min_latency,
|
|
|
|
|
.get_preferred_sample_rate = audiounit_get_preferred_sample_rate,
|
|
|
|
|
.enumerate_devices = audiounit_enumerate_devices,
|
|
|
|
|
.destroy = audiounit_destroy,
|
|
|
|
|
.stream_init = audiounit_stream_init,
|
|
|
|
|
.stream_destroy = audiounit_stream_destroy,
|
|
|
|
|
.stream_start = audiounit_stream_start,
|
|
|
|
|
.stream_stop = audiounit_stream_stop,
|
|
|
|
|
.stream_get_position = audiounit_stream_get_position,
|
|
|
|
|
.stream_get_latency = audiounit_stream_get_latency,
|
|
|
|
|
.stream_set_volume = audiounit_stream_set_volume,
|
|
|
|
|
.stream_set_panning = audiounit_stream_set_panning,
|
|
|
|
|
.stream_get_current_device = audiounit_stream_get_current_device,
|
|
|
|
|
.stream_device_destroy = audiounit_stream_device_destroy,
|
|
|
|
|
.stream_register_device_changed_callback = audiounit_stream_register_device_changed_callback,
|
|
|
|
|
.register_device_collection_changed = audiounit_register_device_collection_changed
|
|
|
|
|
cubeb_ops const audiounit_ops = {
|
|
|
|
|
/*.init =*/ audiounit_init,
|
|
|
|
|
/*.get_backend_id =*/ audiounit_get_backend_id,
|
|
|
|
|
/*.get_max_channel_count =*/ audiounit_get_max_channel_count,
|
|
|
|
|
/*.get_min_latency =*/ audiounit_get_min_latency,
|
|
|
|
|
/*.get_preferred_sample_rate =*/ audiounit_get_preferred_sample_rate,
|
|
|
|
|
/*.enumerate_devices =*/ audiounit_enumerate_devices,
|
|
|
|
|
/*.destroy =*/ audiounit_destroy,
|
|
|
|
|
/*.stream_init =*/ audiounit_stream_init,
|
|
|
|
|
/*.stream_destroy =*/ audiounit_stream_destroy,
|
|
|
|
|
/*.stream_start =*/ audiounit_stream_start,
|
|
|
|
|
/*.stream_stop =*/ audiounit_stream_stop,
|
|
|
|
|
/*.stream_get_position =*/ audiounit_stream_get_position,
|
|
|
|
|
/*.stream_get_latency =*/ audiounit_stream_get_latency,
|
|
|
|
|
/*.stream_set_volume =*/ audiounit_stream_set_volume,
|
|
|
|
|
/*.stream_set_panning =*/ audiounit_stream_set_panning,
|
|
|
|
|
/*.stream_get_current_device =*/ audiounit_stream_get_current_device,
|
|
|
|
|
/*.stream_device_destroy =*/ audiounit_stream_device_destroy,
|
|
|
|
|
/*.stream_register_device_changed_callback =*/ audiounit_stream_register_device_changed_callback,
|
|
|
|
|
/*.register_device_collection_changed =*/ audiounit_register_device_collection_changed
|
|
|
|
|
};
|