summaryrefslogtreecommitdiffstats
path: root/media/libcubeb/osx-linearize-operations.patch
diff options
context:
space:
mode:
Diffstat (limited to 'media/libcubeb/osx-linearize-operations.patch')
-rw-r--r--media/libcubeb/osx-linearize-operations.patch968
1 files changed, 0 insertions, 968 deletions
diff --git a/media/libcubeb/osx-linearize-operations.patch b/media/libcubeb/osx-linearize-operations.patch
deleted file mode 100644
index 9f4f31bca..000000000
--- a/media/libcubeb/osx-linearize-operations.patch
+++ /dev/null
@@ -1,968 +0,0 @@
-From: Paul Adenot <paul@paul.cx>
-Subject: Linearize operations on AudioUnits to sidestep a deadlock.
-
----
-
-diff --git a/src/cubeb_audiounit.cpp b/src/cubeb_audiounit.cpp
---- a/src/cubeb_audiounit.cpp
-+++ b/src/cubeb_audiounit.cpp
-@@ -53,40 +53,45 @@ typedef UInt32 AudioFormatFlags;
-
- #define AU_OUT_BUS 0
- #define AU_IN_BUS 1
-
- #define PRINT_ERROR_CODE(str, r) do { \
- LOG("System call failed: %s (rv: %d)", str, r); \
- } while(0)
-
-+const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb";
-+
- /* Testing empirically, some headsets report a minimal latency that is very
- * low, but this does not work in practice. Lie and say the minimum is 256
- * frames. */
- const uint32_t SAFE_MIN_LATENCY_FRAMES = 256;
- const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
-
- void audiounit_stream_stop_internal(cubeb_stream * stm);
- void audiounit_stream_start_internal(cubeb_stream * stm);
--static void close_audiounit_stream(cubeb_stream * stm);
--static int setup_audiounit_stream(cubeb_stream * stm);
-+static void audiounit_close_stream(cubeb_stream *stm);
-+static int audiounit_setup_stream(cubeb_stream *stm);
-
- extern cubeb_ops const audiounit_ops;
-
- struct cubeb {
- cubeb_ops const * ops;
- owned_critical_section mutex;
- std::atomic<int> active_streams;
-+ uint32_t global_latency_frames = 0;
- int limit_streams;
- cubeb_device_collection_changed_callback collection_changed_callback;
- void * collection_changed_user_ptr;
- /* Differentiate input from output devices. */
- cubeb_device_type collection_changed_devtype;
- uint32_t devtype_device_count;
- AudioObjectID * devtype_device_array;
-+ // The queue is asynchronously deallocated once all references to it are released
-+ dispatch_queue_t serial_queue = dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL);
- };
-
- class auto_array_wrapper
- {
- public:
- explicit auto_array_wrapper(auto_array<float> * ar)
- : float_ar(ar)
- , short_ar(nullptr)
-@@ -205,16 +210,17 @@ struct cubeb_stream {
- cubeb_resampler * resampler;
- /* This is the number of output callback we got in a row. This is usually one,
- * but can be two when the input and output rate are different, and more when
- * a device has been plugged or unplugged, as there can be some time before
- * the device is ready. */
- std::atomic<int> output_callback_in_a_row;
- /* This is true if a device change callback is currently running. */
- std::atomic<bool> switching_device;
-+ std::atomic<bool> buffer_size_change_state{ false };
- };
-
- bool has_input(cubeb_stream * stm)
- {
- return stm->input_stream_params.rate != 0;
- }
-
- bool has_output(cubeb_stream * stm)
-@@ -256,16 +262,24 @@ audiotimestamp_to_latency(AudioTimeStamp
-
- uint64_t pres = AudioConvertHostTimeToNanos(tstamp->mHostTime);
- uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
-
- return ((pres - now) * stream->output_desc.mSampleRate) / 1000000000LL;
- }
-
- static void
-+audiounit_set_global_latency(cubeb_stream * stm, uint32_t latency_frames)
-+{
-+ stm->mutex.assert_current_thread_owns();
-+ assert(stm->context->active_streams == 1);
-+ stm->context->global_latency_frames = latency_frames;
-+}
-+
-+static void
- audiounit_make_silent(AudioBuffer * ioData)
- {
- assert(ioData);
- assert(ioData->mData);
- memset(ioData->mData, 0, ioData->mDataByteSize);
- }
-
- static OSStatus
-@@ -576,29 +590,54 @@ audiounit_get_input_device_id(AudioDevic
- device_id);
- if (r != noErr) {
- return CUBEB_ERROR;
- }
-
- return CUBEB_OK;
- }
-
-+static int
-+audiounit_reinit_stream(cubeb_stream * stm, bool is_started)
-+{
-+ if (is_started) {
-+ audiounit_stream_stop_internal(stm);
-+ }
-+
-+ {
-+ auto_lock lock(stm->mutex);
-+
-+ audiounit_close_stream(stm);
-+
-+ if (audiounit_setup_stream(stm) != CUBEB_OK) {
-+ LOG("(%p) Stream reinit failed.", stm);
-+ return CUBEB_ERROR;
-+ }
-+
-+ // Reset input frames to force new stream pre-buffer
-+ // silence if needed, check `is_extra_input_needed()`
-+ stm->frames_read = 0;
-+
-+ // If the stream was running, start it again.
-+ if (is_started) {
-+ audiounit_stream_start_internal(stm);
-+ }
-+ }
-+ return CUBEB_OK;
-+}
-+
- static OSStatus
- audiounit_property_listener_callback(AudioObjectID /* id */, UInt32 address_count,
- const AudioObjectPropertyAddress * addresses,
- void * user)
- {
- cubeb_stream * stm = (cubeb_stream*) user;
-- int rv;
-- bool was_running = false;
--
- stm->switching_device = true;
--
- // Note if the stream was running or not
-- was_running = !stm->shutdown;
-+ bool was_running = !stm->shutdown;
-
- LOG("(%p) Audio device changed, %d events.", stm, address_count);
- for (UInt32 i = 0; i < address_count; i++) {
- switch(addresses[i].mSelector) {
- case kAudioHardwarePropertyDefaultOutputDevice: {
- LOG("Event[%d] - mSelector == kAudioHardwarePropertyDefaultOutputDevice", i);
- // Allow restart to choose the new default
- stm->output_device = nullptr;
-@@ -639,38 +678,25 @@ audiounit_property_listener_callback(Aud
- if (stm->device_changed_callback) {
- stm->device_changed_callback(stm->user_ptr);
- }
- break;
- }
- }
- }
-
-- // This means the callback won't be called again.
-- audiounit_stream_stop_internal(stm);
--
-- {
-- auto_lock lock(stm->mutex);
-- close_audiounit_stream(stm);
-- rv = setup_audiounit_stream(stm);
-- if (rv != CUBEB_OK) {
-- LOG("(%p) Could not reopen a stream after switching.", stm);
-+ // Use a new thread, through the queue, to avoid deadlock when calling
-+ // Get/SetProperties method from inside notify callback
-+ dispatch_async(stm->context->serial_queue, ^() {
-+ if (audiounit_reinit_stream(stm, was_running) != CUBEB_OK) {
- stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
-- return noErr;
-+ LOG("(%p) Could not reopen the stream after switching.", stm);
- }
--
-- stm->frames_read = 0;
--
-- // If the stream was running, start it again.
-- if (was_running) {
-- audiounit_stream_start_internal(stm);
-- }
-- }
--
-- stm->switching_device = false;
-+ stm->switching_device = false;
-+ });
-
- return noErr;
- }
-
- OSStatus
- audiounit_add_listener(cubeb_stream * stm, AudioDeviceID id, AudioObjectPropertySelector selector,
- AudioObjectPropertyScope scope, AudioObjectPropertyListenerProc listener)
- {
-@@ -1155,18 +1181,17 @@ audiounit_init_input_linear_buffer(cubeb
-
- static void
- audiounit_destroy_input_linear_buffer(cubeb_stream * stream)
- {
- delete stream->input_linear_buffer;
- }
-
- static uint32_t
--audiounit_clamp_latency(cubeb_stream * stm,
-- uint32_t latency_frames)
-+audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
- {
- // For the 1st stream set anything within safe min-max
- assert(stm->context->active_streams > 0);
- if (stm->context->active_streams == 1) {
- return std::max(std::min<uint32_t>(latency_frames, SAFE_MAX_LATENCY_FRAMES),
- SAFE_MIN_LATENCY_FRAMES);
- }
-
-@@ -1219,26 +1244,374 @@ audiounit_clamp_latency(cubeb_stream * s
- } else {
- upper_latency_limit = SAFE_MAX_LATENCY_FRAMES;
- }
-
- return std::max(std::min<uint32_t>(latency_frames, upper_latency_limit),
- SAFE_MIN_LATENCY_FRAMES);
- }
-
-+/*
-+ * Change buffer size is prone to deadlock thus we change it
-+ * following the steps:
-+ * - register a listener for the buffer size property
-+ * - change the property
-+ * - wait until the listener is executed
-+ * - property has changed, remove the listener
-+ * */
-+static void
-+buffer_size_changed_callback(void * inClientData,
-+ AudioUnit inUnit,
-+ AudioUnitPropertyID inPropertyID,
-+ AudioUnitScope inScope,
-+ AudioUnitElement inElement)
-+{
-+ cubeb_stream * stm = (cubeb_stream *)inClientData;
-+
-+ AudioUnit au = inUnit;
-+ AudioUnitScope au_scope = kAudioUnitScope_Input;
-+ AudioUnitElement au_element = inElement;
-+ const char * au_type = "output";
-+
-+ if (au == stm->input_unit) {
-+ au_scope = kAudioUnitScope_Output;
-+ au_type = "input";
-+ }
-+
-+ switch (inPropertyID) {
-+
-+ case kAudioDevicePropertyBufferFrameSize: {
-+ if (inScope != au_scope) {
-+ break;
-+ }
-+ UInt32 new_buffer_size;
-+ UInt32 outSize = sizeof(UInt32);
-+ OSStatus r = AudioUnitGetProperty(au,
-+ kAudioDevicePropertyBufferFrameSize,
-+ au_scope,
-+ au_element,
-+ &new_buffer_size,
-+ &outSize);
-+ if (r != noErr) {
-+ LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: Cannot get current buffer size", stm);
-+ } else {
-+ LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: New %s buffer size = %d for scope %d", stm,
-+ au_type, new_buffer_size, inScope);
-+ }
-+ stm->buffer_size_change_state = true;
-+ break;
-+ }
-+ }
-+}
-+
-+enum set_buffer_size_side {
-+ INPUT,
-+ OUTPUT,
-+};
-+
- static int
--setup_audiounit_stream(cubeb_stream * stm)
-+audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames, set_buffer_size_side set_side)
-+{
-+ AudioUnit au = stm->output_unit;
-+ AudioUnitScope au_scope = kAudioUnitScope_Input;
-+ AudioUnitElement au_element = AU_OUT_BUS;
-+ const char * au_type = "output";
-+
-+ if (set_side == INPUT) {
-+ au = stm->input_unit;
-+ au_scope = kAudioUnitScope_Output;
-+ au_element = AU_IN_BUS;
-+ au_type = "input";
-+ }
-+
-+ uint32_t buffer_frames = 0;
-+ UInt32 size = sizeof(buffer_frames);
-+ int r = AudioUnitGetProperty(au,
-+ kAudioDevicePropertyBufferFrameSize,
-+ au_scope,
-+ au_element,
-+ &buffer_frames,
-+ &size);
-+ if (r != noErr) {
-+ if (set_side == INPUT) {
-+ PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
-+ } else {
-+ PRINT_ERROR_CODE("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
-+ }
-+ return CUBEB_ERROR;
-+ }
-+
-+ if (new_size_frames == buffer_frames) {
-+ LOG("(%p) No need to update %s buffer size already %u frames", stm, au_type, buffer_frames);
-+ return CUBEB_OK;
-+ }
-+
-+ r = AudioUnitAddPropertyListener(au,
-+ kAudioDevicePropertyBufferFrameSize,
-+ buffer_size_changed_callback,
-+ stm);
-+ if (r != noErr) {
-+ if (set_side == INPUT) {
-+ PRINT_ERROR_CODE("AudioUnitAddPropertyListener/input/kAudioDevicePropertyBufferFrameSize", r);
-+ } else {
-+ PRINT_ERROR_CODE("AudioUnitAddPropertyListener/output/kAudioDevicePropertyBufferFrameSize", r);
-+ }
-+ return CUBEB_ERROR;
-+ }
-+
-+ stm->buffer_size_change_state = false;
-+
-+ r = AudioUnitSetProperty(au,
-+ kAudioDevicePropertyBufferFrameSize,
-+ au_scope,
-+ au_element,
-+ &new_size_frames,
-+ sizeof(new_size_frames));
-+ if (r != noErr) {
-+ if (set_side == INPUT) {
-+ PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
-+ } else {
-+ PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
-+ }
-+
-+ r = AudioUnitRemovePropertyListenerWithUserData(au,
-+ kAudioDevicePropertyBufferFrameSize,
-+ buffer_size_changed_callback,
-+ stm);
-+ if (r != noErr) {
-+ if (set_side == INPUT) {
-+ PRINT_ERROR_CODE("AudioUnitAddPropertyListener/input/kAudioDevicePropertyBufferFrameSize", r);
-+ } else {
-+ PRINT_ERROR_CODE("AudioUnitAddPropertyListener/output/kAudioDevicePropertyBufferFrameSize", r);
-+ }
-+ }
-+
-+ return CUBEB_ERROR;
-+ }
-+
-+ int count = 0;
-+ while (!stm->buffer_size_change_state && count++ < 30) {
-+ struct timespec req, rem;
-+ req.tv_sec = 0;
-+ req.tv_nsec = 100000000L; // 0.1 sec
-+ if (nanosleep(&req , &rem) < 0 ) {
-+ LOG("(%p) Warning: nanosleep call failed or interrupted. Remaining time %ld nano secs \n", stm, rem.tv_nsec);
-+ }
-+ LOG("(%p) audiounit_set_buffer_size : wait count = %d", stm, count);
-+ }
-+
-+ r = AudioUnitRemovePropertyListenerWithUserData(au,
-+ kAudioDevicePropertyBufferFrameSize,
-+ buffer_size_changed_callback,
-+ stm);
-+ if (r != noErr) {
-+ return CUBEB_ERROR;
-+ if (set_side == INPUT) {
-+ PRINT_ERROR_CODE("AudioUnitAddPropertyListener/input/kAudioDevicePropertyBufferFrameSize", r);
-+ } else {
-+ PRINT_ERROR_CODE("AudioUnitAddPropertyListener/output/kAudioDevicePropertyBufferFrameSize", r);
-+ }
-+ }
-+
-+ if (!stm->buffer_size_change_state && count >= 30) {
-+ LOG("(%p) Error, did not get buffer size change callback ...", stm);
-+ return CUBEB_ERROR;
-+ }
-+
-+ LOG("(%p) %s buffer size changed to %u frames.", stm, au_type, new_size_frames);
-+ return CUBEB_OK;
-+}
-+
-+static int
-+audiounit_configure_input(cubeb_stream * stm)
-+{
-+ int r = 0;
-+ UInt32 size;
-+ AURenderCallbackStruct aurcbs_in;
-+
-+ LOG("(%p) Opening input side: rate %u, channels %u, format %d, latency in frames %u.",
-+ stm, stm->input_stream_params.rate, stm->input_stream_params.channels,
-+ stm->input_stream_params.format, stm->latency_frames);
-+
-+ /* Get input device sample rate. */
-+ AudioStreamBasicDescription input_hw_desc;
-+ size = sizeof(AudioStreamBasicDescription);
-+ r = AudioUnitGetProperty(stm->input_unit,
-+ kAudioUnitProperty_StreamFormat,
-+ kAudioUnitScope_Input,
-+ AU_IN_BUS,
-+ &input_hw_desc,
-+ &size);
-+ if (r != noErr) {
-+ PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat", r);
-+ return CUBEB_ERROR;
-+ }
-+ stm->input_hw_rate = input_hw_desc.mSampleRate;
-+ LOG("(%p) Input device sampling rate: %.2f", stm, stm->input_hw_rate);
-+
-+ /* Set format description according to the input params. */
-+ r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params);
-+ if (r != CUBEB_OK) {
-+ LOG("(%p) Setting format description for input failed.", stm);
-+ return r;
-+ }
-+
-+ // Use latency to set buffer size
-+ stm->input_buffer_frames = stm->latency_frames;
-+ r = audiounit_set_buffer_size(stm, stm->input_buffer_frames, INPUT);
-+ if (r != CUBEB_OK) {
-+ LOG("(%p) Error in change input buffer size.", 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. */
-+ src_desc.mSampleRate = stm->input_hw_rate;
-+
-+ r = AudioUnitSetProperty(stm->input_unit,
-+ kAudioUnitProperty_StreamFormat,
-+ kAudioUnitScope_Output,
-+ AU_IN_BUS,
-+ &src_desc,
-+ sizeof(AudioStreamBasicDescription));
-+ if (r != noErr) {
-+ PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat", r);
-+ return CUBEB_ERROR;
-+ }
-+
-+ /* Frames per buffer in the input callback. */
-+ r = AudioUnitSetProperty(stm->input_unit,
-+ kAudioUnitProperty_MaximumFramesPerSlice,
-+ kAudioUnitScope_Global,
-+ AU_IN_BUS,
-+ &stm->input_buffer_frames,
-+ sizeof(UInt32));
-+ if (r != noErr) {
-+ PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice", r);
-+ return CUBEB_ERROR;
-+ }
-+
-+ // Input only capacity
-+ unsigned int array_capacity = 1;
-+ if (has_output(stm)) {
-+ // Full-duplex increase capacity
-+ array_capacity = 8;
-+ }
-+ if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) {
-+ return CUBEB_ERROR;
-+ }
-+
-+ assert(stm->input_unit != NULL);
-+ aurcbs_in.inputProc = audiounit_input_callback;
-+ aurcbs_in.inputProcRefCon = stm;
-+
-+ r = AudioUnitSetProperty(stm->input_unit,
-+ kAudioOutputUnitProperty_SetInputCallback,
-+ kAudioUnitScope_Global,
-+ AU_OUT_BUS,
-+ &aurcbs_in,
-+ sizeof(aurcbs_in));
-+ if (r != noErr) {
-+ PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback", r);
-+ return CUBEB_ERROR;
-+ }
-+ LOG("(%p) Input audiounit init successfully.", stm);
-+
-+ return CUBEB_OK;
-+}
-+
-+static int
-+audiounit_configure_output(cubeb_stream * stm)
-+{
-+ int r;
-+ AURenderCallbackStruct aurcbs_out;
-+ UInt32 size;
-+
-+
-+ LOG("(%p) Opening output side: rate %u, channels %u, format %d, latency in frames %u.",
-+ stm, stm->output_stream_params.rate, stm->output_stream_params.channels,
-+ stm->output_stream_params.format, stm->latency_frames);
-+
-+ r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params);
-+ if (r != CUBEB_OK) {
-+ LOG("(%p) Could not initialize the audio stream description.", stm);
-+ return r;
-+ }
-+
-+ /* Get output device sample rate. */
-+ AudioStreamBasicDescription output_hw_desc;
-+ size = sizeof(AudioStreamBasicDescription);
-+ memset(&output_hw_desc, 0, size);
-+ r = AudioUnitGetProperty(stm->output_unit,
-+ kAudioUnitProperty_StreamFormat,
-+ kAudioUnitScope_Output,
-+ AU_OUT_BUS,
-+ &output_hw_desc,
-+ &size);
-+ if (r != noErr) {
-+ PRINT_ERROR_CODE("AudioUnitGetProperty/output/tkAudioUnitProperty_StreamFormat", r);
-+ return CUBEB_ERROR;
-+ }
-+ stm->output_hw_rate = output_hw_desc.mSampleRate;
-+ LOG("(%p) Output device sampling rate: %.2f", stm, output_hw_desc.mSampleRate);
-+
-+ r = AudioUnitSetProperty(stm->output_unit,
-+ kAudioUnitProperty_StreamFormat,
-+ kAudioUnitScope_Input,
-+ AU_OUT_BUS,
-+ &stm->output_desc,
-+ sizeof(AudioStreamBasicDescription));
-+ if (r != noErr) {
-+ PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat", r);
-+ return CUBEB_ERROR;
-+ }
-+
-+ r = audiounit_set_buffer_size(stm, stm->latency_frames, OUTPUT);
-+ if (r != CUBEB_OK) {
-+ LOG("(%p) Error in change output buffer size.", stm);
-+ return CUBEB_ERROR;
-+ }
-+
-+ /* Frames per buffer in the input callback. */
-+ r = AudioUnitSetProperty(stm->output_unit,
-+ kAudioUnitProperty_MaximumFramesPerSlice,
-+ kAudioUnitScope_Global,
-+ AU_OUT_BUS,
-+ &stm->latency_frames,
-+ sizeof(UInt32));
-+ if (r != noErr) {
-+ PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_MaximumFramesPerSlice", r);
-+ return CUBEB_ERROR;
-+ }
-+
-+ assert(stm->output_unit != NULL);
-+ aurcbs_out.inputProc = audiounit_output_callback;
-+ aurcbs_out.inputProcRefCon = stm;
-+ r = AudioUnitSetProperty(stm->output_unit,
-+ kAudioUnitProperty_SetRenderCallback,
-+ kAudioUnitScope_Global,
-+ AU_OUT_BUS,
-+ &aurcbs_out,
-+ sizeof(aurcbs_out));
-+ if (r != noErr) {
-+ PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback", r);
-+ return CUBEB_ERROR;
-+ }
-+
-+ LOG("(%p) Output audiounit init successfully.", stm);
-+ return CUBEB_OK;
-+}
-+
-+static int
-+audiounit_setup_stream(cubeb_stream * stm)
- {
- stm->mutex.assert_current_thread_owns();
-
-- int r;
-- AURenderCallbackStruct aurcbs_in;
-- AURenderCallbackStruct aurcbs_out;
-- UInt32 size;
--
-+ int r = 0;
- if (has_input(stm)) {
- r = audiounit_create_unit(&stm->input_unit, true,
- &stm->input_stream_params,
- stm->input_device);
- if (r != CUBEB_OK) {
- LOG("(%p) AudioUnit creation for input failed.", stm);
- return r;
- }
-@@ -1249,180 +1622,46 @@ setup_audiounit_stream(cubeb_stream * st
- &stm->output_stream_params,
- stm->output_device);
- if (r != CUBEB_OK) {
- LOG("(%p) AudioUnit creation for output failed.", stm);
- return r;
- }
- }
-
-+ /* Latency cannot change if another stream is operating in parallel. In this case
-+ * latecy is set to the other stream value. */
-+ if (stm->context->active_streams > 1) {
-+ LOG("(%p) More than one active stream, use global latency.", stm);
-+ stm->latency_frames = stm->context->global_latency_frames;
-+ } else {
-+ /* Silently clamp the latency down to the platform default, because we
-+ * synthetize the clock from the callbacks, and we want the clock to update
-+ * often. */
-+ stm->latency_frames = audiounit_clamp_latency(stm, stm->latency_frames);
-+ assert(stm->latency_frames); // Ungly error check
-+ audiounit_set_global_latency(stm, stm->latency_frames);
-+ }
-+
- /* Setup Input Stream! */
- if (has_input(stm)) {
-- LOG("(%p) Opening input side: rate %u, channels %u, format %d, latency in frames %u.",
-- stm, stm->input_stream_params.rate, stm->input_stream_params.channels,
-- stm->input_stream_params.format, stm->latency_frames);
-- /* Get input device sample rate. */
-- AudioStreamBasicDescription input_hw_desc;
-- size = sizeof(AudioStreamBasicDescription);
-- r = AudioUnitGetProperty(stm->input_unit,
-- kAudioUnitProperty_StreamFormat,
-- kAudioUnitScope_Input,
-- AU_IN_BUS,
-- &input_hw_desc,
-- &size);
-- if (r != noErr) {
-- PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat", r);
-- return CUBEB_ERROR;
-- }
-- stm->input_hw_rate = input_hw_desc.mSampleRate;
-- LOG("(%p) Input device sampling rate: %.2f", stm, stm->input_hw_rate);
--
-- /* Set format description according to the input params. */
-- r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params);
-+ r = audiounit_configure_input(stm);
- if (r != CUBEB_OK) {
-- LOG("(%p) Setting format description for input failed.", stm);
-+ LOG("(%p) Configure audiounit input failed.", stm);
- return r;
- }
--
-- // Use latency to set buffer size
-- stm->input_buffer_frames = stm->latency_frames;
-- LOG("(%p) Input buffer frame count %u.", stm, unsigned(stm->input_buffer_frames));
-- r = AudioUnitSetProperty(stm->input_unit,
-- kAudioDevicePropertyBufferFrameSize,
-- kAudioUnitScope_Output,
-- AU_IN_BUS,
-- &stm->input_buffer_frames,
-- sizeof(UInt32));
-- if (r != noErr) {
-- PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
-- 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. */
-- src_desc.mSampleRate = stm->input_hw_rate;
--
-- r = AudioUnitSetProperty(stm->input_unit,
-- kAudioUnitProperty_StreamFormat,
-- kAudioUnitScope_Output,
-- AU_IN_BUS,
-- &src_desc,
-- sizeof(AudioStreamBasicDescription));
-- if (r != noErr) {
-- PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat", r);
-- return CUBEB_ERROR;
-- }
--
-- /* Frames per buffer in the input callback. */
-- r = AudioUnitSetProperty(stm->input_unit,
-- kAudioUnitProperty_MaximumFramesPerSlice,
-- kAudioUnitScope_Output,
-- AU_IN_BUS,
-- &stm->input_buffer_frames,
-- sizeof(UInt32));
-- if (r != noErr) {
-- PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice", r);
-- return CUBEB_ERROR;
-- }
--
-- // Input only capacity
-- unsigned int array_capacity = 1;
-- if (has_output(stm)) {
-- // Full-duplex increase capacity
-- array_capacity = 8;
-- }
-- if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) {
-- return CUBEB_ERROR;
-- }
--
-- assert(stm->input_unit != NULL);
-- aurcbs_in.inputProc = audiounit_input_callback;
-- aurcbs_in.inputProcRefCon = stm;
--
-- r = AudioUnitSetProperty(stm->input_unit,
-- kAudioOutputUnitProperty_SetInputCallback,
-- kAudioUnitScope_Global,
-- AU_OUT_BUS,
-- &aurcbs_in,
-- sizeof(aurcbs_in));
-- if (r != noErr) {
-- PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback", r);
-- return CUBEB_ERROR;
-- }
-- LOG("(%p) Input audiounit init successfully.", stm);
- }
-
- /* Setup Output Stream! */
- if (has_output(stm)) {
-- LOG("(%p) Opening output side: rate %u, channels %u, format %d, latency in frames %u.",
-- stm, stm->output_stream_params.rate, stm->output_stream_params.channels,
-- stm->output_stream_params.format, stm->latency_frames);
-- r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params);
-+ r = audiounit_configure_output(stm);
- if (r != CUBEB_OK) {
-- LOG("(%p) Could not initialize the audio stream description.", stm);
-+ LOG("(%p) Configure audiounit output failed.", stm);
- return r;
- }
--
-- /* Get output device sample rate. */
-- AudioStreamBasicDescription output_hw_desc;
-- size = sizeof(AudioStreamBasicDescription);
-- memset(&output_hw_desc, 0, size);
-- r = AudioUnitGetProperty(stm->output_unit,
-- kAudioUnitProperty_StreamFormat,
-- kAudioUnitScope_Output,
-- AU_OUT_BUS,
-- &output_hw_desc,
-- &size);
-- if (r != noErr) {
-- PRINT_ERROR_CODE("AudioUnitGetProperty/output/tkAudioUnitProperty_StreamFormat", r);
-- return CUBEB_ERROR;
-- }
-- stm->output_hw_rate = output_hw_desc.mSampleRate;
-- LOG("(%p) Output device sampling rate: %.2f", stm, output_hw_desc.mSampleRate);
--
-- r = AudioUnitSetProperty(stm->output_unit,
-- kAudioUnitProperty_StreamFormat,
-- kAudioUnitScope_Input,
-- AU_OUT_BUS,
-- &stm->output_desc,
-- sizeof(AudioStreamBasicDescription));
-- if (r != noErr) {
-- PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat", r);
-- return CUBEB_ERROR;
-- }
--
-- // Use latency to calculate buffer size
-- uint32_t output_buffer_frames = stm->latency_frames;
-- LOG("(%p) Output buffer frame count %u.", stm, output_buffer_frames);
-- r = AudioUnitSetProperty(stm->output_unit,
-- kAudioDevicePropertyBufferFrameSize,
-- kAudioUnitScope_Input,
-- AU_OUT_BUS,
-- &output_buffer_frames,
-- sizeof(output_buffer_frames));
-- if (r != noErr) {
-- PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
-- return CUBEB_ERROR;
-- }
--
-- assert(stm->output_unit != NULL);
-- aurcbs_out.inputProc = audiounit_output_callback;
-- aurcbs_out.inputProcRefCon = stm;
-- r = AudioUnitSetProperty(stm->output_unit,
-- kAudioUnitProperty_SetRenderCallback,
-- kAudioUnitScope_Global,
-- AU_OUT_BUS,
-- &aurcbs_out,
-- sizeof(aurcbs_out));
-- if (r != noErr) {
-- PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback", r);
-- return CUBEB_ERROR;
-- }
-- LOG("(%p) Output audiounit init successfully.", stm);
- }
-
- // Setting the latency doesn't work well for USB headsets (eg. plantronics).
- // Keep the default latency for now.
- #if 0
- buffer_size = latency;
-
- /* Get the range of latency this particular device can work with, and clamp
-@@ -1535,63 +1774,60 @@ audiounit_stream_init(cubeb * context,
- void * user_ptr)
- {
- cubeb_stream * stm;
- int r;
-
- assert(context);
- *stream = NULL;
-
-+ assert(latency_frames > 0);
- if (context->limit_streams && context->active_streams >= CUBEB_STREAM_MAX) {
- LOG("Reached the stream limit of %d", CUBEB_STREAM_MAX);
- return CUBEB_ERROR;
- }
-- context->active_streams += 1;
-
- stm = (cubeb_stream *) calloc(1, sizeof(cubeb_stream));
- assert(stm);
- // Placement new to call the ctors of cubeb_stream members.
- new (stm) cubeb_stream();
-
- /* These could be different in the future if we have both
- * full-duplex stream and different devices for input vs output. */
- stm->context = context;
- stm->data_callback = data_callback;
- stm->state_callback = state_callback;
- stm->user_ptr = user_ptr;
-+ stm->latency_frames = latency_frames;
- stm->device_changed_callback = NULL;
- if (input_stream_params) {
- stm->input_stream_params = *input_stream_params;
- stm->input_device = input_device;
- stm->is_default_input = input_device == nullptr ||
- (audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT) ==
- reinterpret_cast<intptr_t>(input_device));
- }
- if (output_stream_params) {
- stm->output_stream_params = *output_stream_params;
- stm->output_device = output_device;
- }
-
- /* Init data members where necessary */
- stm->hw_latency_frames = UINT64_MAX;
-
-- /* Silently clamp the latency down to the platform default, because we
-- * synthetize the clock from the callbacks, and we want the clock to update
-- * often. */
-- stm->latency_frames = audiounit_clamp_latency(stm, latency_frames);
-- assert(latency_frames > 0);
--
- stm->switching_device = false;
-
-+ auto_lock context_lock(context->mutex);
- {
- // It's not critical to lock here, because no other thread has been started
- // yet, but it allows to assert that the lock has been taken in
-- // `setup_audiounit_stream`.
-+ // `audiounit_setup_stream`.
-+ context->active_streams += 1;
- auto_lock lock(stm->mutex);
-- r = setup_audiounit_stream(stm);
-+ r = audiounit_setup_stream(stm);
- }
-
- if (r != CUBEB_OK) {
- LOG("(%p) Could not setup the audiounit stream.", stm);
- audiounit_stream_destroy(stm);
- return r;
- }
-
-@@ -1602,17 +1838,17 @@ audiounit_stream_init(cubeb * context,
- }
-
- *stream = stm;
- LOG("Cubeb stream (%p) init successful.", stm);
- return CUBEB_OK;
- }
-
- static void
--close_audiounit_stream(cubeb_stream * stm)
-+audiounit_close_stream(cubeb_stream *stm)
- {
- stm->mutex.assert_current_thread_owns();
- if (stm->input_unit) {
- AudioUnitUninitialize(stm->input_unit);
- AudioComponentInstanceDispose(stm->input_unit);
- }
-
- audiounit_destroy_input_linear_buffer(stm);
-@@ -1625,33 +1861,36 @@ close_audiounit_stream(cubeb_stream * st
- cubeb_resampler_destroy(stm->resampler);
- }
-
- static void
- audiounit_stream_destroy(cubeb_stream * stm)
- {
- stm->shutdown = true;
-
-+ auto_lock context_locl(stm->context->mutex);
- audiounit_stream_stop_internal(stm);
-
- {
- auto_lock lock(stm->mutex);
-- close_audiounit_stream(stm);
-+ audiounit_close_stream(stm);
- }
-
- #if !TARGET_OS_IPHONE
- int r = audiounit_uninstall_device_changed_callback(stm);
- if (r != CUBEB_OK) {
- LOG("(%p) Could not uninstall the device changed callback", stm);
- }
- #endif
-
- assert(stm->context->active_streams >= 1);
- stm->context->active_streams -= 1;
-
-+ LOG("Cubeb stream (%p) destroyed successful.", stm);
-+
- stm->~cubeb_stream();
- free(stm);
- }
-
- void
- audiounit_stream_start_internal(cubeb_stream * stm)
- {
- OSStatus r;
-@@ -1666,16 +1905,17 @@ audiounit_stream_start_internal(cubeb_st
- }
-
- static int
- audiounit_stream_start(cubeb_stream * stm)
- {
- stm->shutdown = false;
- stm->draining = false;
-
-+ auto_lock context_locl(stm->context->mutex);
- audiounit_stream_start_internal(stm);
-
- stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
-
- LOG("Cubeb stream (%p) started successfully.", stm);
- return CUBEB_OK;
- }
-
-@@ -1693,16 +1933,17 @@ audiounit_stream_stop_internal(cubeb_str
- }
- }
-
- static int
- audiounit_stream_stop(cubeb_stream * stm)
- {
- stm->shutdown = true;
-
-+ auto_lock context_locl(stm->context->mutex);
- audiounit_stream_stop_internal(stm);
-
- stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
-
- LOG("Cubeb stream (%p) stopped successfully.", stm);
- return CUBEB_OK;
- }
-