summaryrefslogtreecommitdiff
path: root/media/libcubeb/src/cubeb_audiounit.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libcubeb/src/cubeb_audiounit.cpp')
-rw-r--r--media/libcubeb/src/cubeb_audiounit.cpp3642
1 files changed, 1353 insertions, 2289 deletions
diff --git a/media/libcubeb/src/cubeb_audiounit.cpp b/media/libcubeb/src/cubeb_audiounit.cpp
index 02cd134697..9483c2795e 100644
--- a/media/libcubeb/src/cubeb_audiounit.cpp
+++ b/media/libcubeb/src/cubeb_audiounit.cpp
@@ -6,387 +6,236 @@
*/
#undef NDEBUG
-#include <AudioUnit/AudioUnit.h>
#include <TargetConditionals.h>
#include <assert.h>
#include <mach/mach_time.h>
#include <pthread.h>
#include <stdlib.h>
+#include <AudioUnit/AudioUnit.h>
#if !TARGET_OS_IPHONE
#include <AvailabilityMacros.h>
#include <CoreAudio/AudioHardware.h>
#include <CoreAudio/HostTime.h>
#include <CoreFoundation/CoreFoundation.h>
#endif
-#include "cubeb-internal.h"
-#include "cubeb/cubeb.h"
-#include "cubeb_mixer.h"
-#include <AudioToolbox/AudioToolbox.h>
#include <CoreAudio/CoreAudioTypes.h>
+#include <AudioToolbox/AudioToolbox.h>
+#include "cubeb/cubeb.h"
+#include "cubeb-internal.h"
+#include "cubeb_panner.h"
#if !TARGET_OS_IPHONE
#include "cubeb_osx_run_loop.h"
#endif
#include "cubeb_resampler.h"
#include "cubeb_ring_array.h"
+#include "cubeb_utils.h"
#include <algorithm>
#include <atomic>
-#include <set>
-#include <string>
-#include <sys/time.h>
-#include <vector>
-using namespace std;
+#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 AudioComponent Component
+#define AudioComponentDescription ComponentDescription
+#define AudioComponentFindNext FindNextComponent
+#define AudioComponentInstanceNew OpenAComponent
+#define AudioComponentInstanceDispose CloseComponent
+#endif
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
-typedef UInt32 AudioFormatFlags;
+typedef UInt32 AudioFormatFlags;
#endif
-#define AU_OUT_BUS 0
-#define AU_IN_BUS 1
+#define CUBEB_STREAM_MAX 8
-const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb";
-const char * PRIVATE_AGGREGATE_DEVICE_NAME = "CubebAggregateDevice";
+#define AU_OUT_BUS 0
+#define AU_IN_BUS 1
-#ifdef ALOGV
-#undef ALOGV
-#endif
-#define ALOGV(msg, ...) \
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), \
- ^{ \
- LOGV(msg, ##__VA_ARGS__); \
- })
-
-#ifdef ALOG
-#undef ALOG
-#endif
-#define ALOG(msg, ...) \
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), \
- ^{ \
- LOG(msg, ##__VA_ARGS__); \
- })
+#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 = 128;
+const uint32_t SAFE_MIN_LATENCY_FRAMES = 256;
const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
-const AudioObjectPropertyAddress DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS = {
- kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
-
-const AudioObjectPropertyAddress DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS = {
- kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
-
-const AudioObjectPropertyAddress DEVICE_IS_ALIVE_PROPERTY_ADDRESS = {
- kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
-
-const AudioObjectPropertyAddress DEVICES_PROPERTY_ADDRESS = {
- kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
-
-const AudioObjectPropertyAddress INPUT_DATA_SOURCE_PROPERTY_ADDRESS = {
- kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeInput,
- kAudioObjectPropertyElementMaster};
-
-const AudioObjectPropertyAddress OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS = {
- kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput,
- kAudioObjectPropertyElementMaster};
-
-typedef uint32_t device_flags_value;
-
-enum device_flags {
- DEV_UNKNOWN = 0x00, /* Unknown */
- DEV_INPUT = 0x01, /* Record device like mic */
- DEV_OUTPUT = 0x02, /* Playback device like speakers */
- DEV_SYSTEM_DEFAULT = 0x04, /* System default device */
- DEV_SELECTED_DEFAULT =
- 0x08, /* User selected to use the system default device */
-};
-
-void
-audiounit_stream_stop_internal(cubeb_stream * stm);
-static int
-audiounit_stream_start_internal(cubeb_stream * stm);
-static void
-audiounit_close_stream(cubeb_stream * stm);
-static int
-audiounit_setup_stream(cubeb_stream * stm);
-static vector<AudioObjectID>
-audiounit_get_devices_of_type(cubeb_device_type devtype);
-static UInt32
-audiounit_get_device_presentation_latency(AudioObjectID devid,
- AudioObjectPropertyScope scope);
-
-#if !TARGET_OS_IPHONE
-static AudioObjectID
-audiounit_get_default_device_id(cubeb_device_type type);
-static int
-audiounit_uninstall_device_changed_callback(cubeb_stream * stm);
-static int
-audiounit_uninstall_system_changed_callback(cubeb_stream * stm);
-static void
-audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags);
-#endif
+void audiounit_stream_stop_internal(cubeb_stream * stm);
+void audiounit_stream_start_internal(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 = &audiounit_ops;
+ cubeb_ops const * ops;
owned_critical_section mutex;
- int active_streams = 0;
+ std::atomic<int> active_streams;
uint32_t global_latency_frames = 0;
- cubeb_device_collection_changed_callback input_collection_changed_callback =
- nullptr;
- void * input_collection_changed_user_ptr = nullptr;
- cubeb_device_collection_changed_callback output_collection_changed_callback =
- nullptr;
- void * output_collection_changed_user_ptr = nullptr;
- // Store list of devices to detect changes
- vector<AudioObjectID> input_device_array;
- vector<AudioObjectID> output_device_array;
- // The queue should be released when it’s no longer needed.
- dispatch_queue_t serial_queue =
- dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL);
- // Current used channel layout
- atomic<cubeb_channel_layout> layout{CUBEB_LAYOUT_UNDEFINED};
- uint32_t channels = 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);
};
-static unique_ptr<AudioChannelLayout, decltype(&free)>
-make_sized_audio_channel_layout(size_t sz)
+class auto_array_wrapper
{
- assert(sz >= sizeof(AudioChannelLayout));
- AudioChannelLayout * acl =
- reinterpret_cast<AudioChannelLayout *>(calloc(1, sz));
- assert(acl); // Assert the allocation works.
- return unique_ptr<AudioChannelLayout, decltype(&free)>(acl, free);
-}
+public:
+ explicit auto_array_wrapper(auto_array<float> * ar)
+ : float_ar(ar)
+ , short_ar(nullptr)
+ {assert((float_ar && !short_ar) || (!float_ar && short_ar));}
-enum class io_side {
- INPUT,
- OUTPUT,
-};
+ explicit auto_array_wrapper(auto_array<short> * ar)
+ : float_ar(nullptr)
+ , short_ar(ar)
+ {assert((float_ar && !short_ar) || (!float_ar && short_ar));}
-static char const *
-to_string(io_side side)
-{
- switch (side) {
- case io_side::INPUT:
- return "input";
- case io_side::OUTPUT:
- return "output";
+ ~auto_array_wrapper() {
+ auto_lock l(lock);
+ assert((float_ar && !short_ar) || (!float_ar && short_ar));
+ delete float_ar;
+ delete short_ar;
}
-}
-struct device_info {
- AudioDeviceID id = kAudioObjectUnknown;
- device_flags_value flags = DEV_UNKNOWN;
-};
+ void push(void * elements, size_t length){
+ assert((float_ar && !short_ar) || (!float_ar && short_ar));
+ auto_lock l(lock);
+ if (float_ar)
+ return float_ar->push(static_cast<float*>(elements), length);
+ return short_ar->push(static_cast<short*>(elements), length);
+ }
-struct property_listener {
- AudioDeviceID device_id;
- const AudioObjectPropertyAddress * property_address;
- AudioObjectPropertyListenerProc callback;
- cubeb_stream * stream;
+ size_t length() {
+ assert((float_ar && !short_ar) || (!float_ar && short_ar));
+ auto_lock l(lock);
+ if (float_ar)
+ return float_ar->length();
+ return short_ar->length();
+ }
- property_listener(AudioDeviceID id,
- const AudioObjectPropertyAddress * address,
- AudioObjectPropertyListenerProc proc, cubeb_stream * stm)
- : device_id(id), property_address(address), callback(proc), stream(stm)
- {
+ void push_silence(size_t length) {
+ assert((float_ar && !short_ar) || (!float_ar && short_ar));
+ auto_lock l(lock);
+ if (float_ar)
+ return float_ar->push_silence(length);
+ return short_ar->push_silence(length);
+ }
+
+ bool pop(void * elements, size_t length) {
+ assert((float_ar && !short_ar) || (!float_ar && short_ar));
+ auto_lock l(lock);
+ if (float_ar)
+ return float_ar->pop(static_cast<float*>(elements), length);
+ return short_ar->pop(static_cast<short*>(elements), length);
}
+
+ void * data() {
+ assert((float_ar && !short_ar) || (!float_ar && short_ar));
+ auto_lock l(lock);
+ if (float_ar)
+ return float_ar->data();
+ return short_ar->data();
+ }
+
+ void clear() {
+ assert((float_ar && !short_ar) || (!float_ar && short_ar));
+ auto_lock l(lock);
+ if (float_ar) {
+ float_ar->clear();
+ } else {
+ short_ar->clear();
+ }
+ }
+
+private:
+ auto_array<float> * float_ar;
+ auto_array<short> * short_ar;
+ owned_critical_section lock;
};
struct cubeb_stream {
- explicit cubeb_stream(cubeb * context);
-
- /* Note: Must match cubeb_stream layout in cubeb.c. */
cubeb * context;
- void * user_ptr = nullptr;
- /**/
-
- cubeb_data_callback data_callback = nullptr;
- cubeb_state_callback state_callback = nullptr;
- cubeb_device_changed_callback device_changed_callback = nullptr;
- owned_critical_section device_changed_callback_lock;
+ cubeb_data_callback data_callback;
+ cubeb_state_callback state_callback;
+ cubeb_device_changed_callback device_changed_callback;
/* Stream creation parameters */
- cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
- CUBEB_LAYOUT_UNDEFINED,
- CUBEB_STREAM_PREF_NONE};
- cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
- CUBEB_LAYOUT_UNDEFINED,
- CUBEB_STREAM_PREF_NONE};
- device_info input_device;
- device_info output_device;
+ cubeb_stream_params input_stream_params;
+ cubeb_stream_params output_stream_params;
+ cubeb_devid input_device;
+ bool is_default_input;
+ cubeb_devid output_device;
+ /* User pointer of data_callback */
+ void * user_ptr;
/* Format descriptions */
AudioStreamBasicDescription input_desc;
AudioStreamBasicDescription output_desc;
/* I/O AudioUnits */
- AudioUnit input_unit = nullptr;
- AudioUnit output_unit = nullptr;
+ AudioUnit input_unit;
+ AudioUnit output_unit;
/* I/O device sample rate */
- Float64 input_hw_rate = 0;
- Float64 output_hw_rate = 0;
+ Float64 input_hw_rate;
+ Float64 output_hw_rate;
/* Expected I/O thread interleave,
* calculated from I/O hw rate. */
- int expected_output_callbacks_in_a_row = 0;
+ int expected_output_callbacks_in_a_row;
owned_critical_section mutex;
- // Hold the input samples in every input callback iteration.
- // Only accessed on input/output callback thread and during initial configure.
- unique_ptr<auto_array_wrapper> input_linear_buffer;
+ /* Hold the input samples in every
+ * input callback iteration */
+ auto_array_wrapper * input_linear_buffer;
+ /* Frames on input buffer */
+ std::atomic<uint32_t> input_buffer_frames;
/* Frame counters */
- atomic<uint64_t> frames_played{0};
- uint64_t frames_queued = 0;
- // How many frames got read from the input since the stream started (includes
- // padded silence)
- atomic<int64_t> frames_read{0};
- // How many frames got written to the output device since the stream started
- atomic<int64_t> frames_written{0};
- atomic<bool> shutdown{true};
- atomic<bool> draining{false};
- atomic<bool> reinit_pending{false};
- atomic<bool> destroy_pending{false};
+ uint64_t frames_played;
+ uint64_t frames_queued;
+ std::atomic<int64_t> frames_read;
+ std::atomic<bool> shutdown;
+ std::atomic<bool> draining;
/* Latency requested by the user. */
- uint32_t latency_frames = 0;
- atomic<uint32_t> current_latency_frames{0};
- atomic<uint32_t> total_output_latency_frames{0};
- unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler;
+ uint32_t latency_frames;
+ std::atomic<uint64_t> current_latency_frames;
+ uint64_t hw_latency_frames;
+ std::atomic<float> panning;
+ 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. */
- atomic<bool> switching_device{false};
- atomic<bool> buffer_size_change_state{false};
- AudioDeviceID aggregate_device_id =
- kAudioObjectUnknown; // the aggregate device id
- AudioObjectID plugin_id =
- kAudioObjectUnknown; // used to create aggregate device
- /* Mixer interface */
- unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> mixer;
- /* Buffer where remixing/resampling will occur when upmixing is required */
- /* Only accessed from callback thread */
- unique_ptr<uint8_t[]> temp_buffer;
- size_t temp_buffer_size = 0; // size in bytes.
- /* Listeners indicating what system events are monitored. */
- unique_ptr<property_listener> default_input_listener;
- unique_ptr<property_listener> default_output_listener;
- unique_ptr<property_listener> input_alive_listener;
- unique_ptr<property_listener> input_source_listener;
- unique_ptr<property_listener> output_source_listener;
+ std::atomic<bool> switching_device;
+ std::atomic<bool> buffer_size_change_state{ false };
};
-bool
-has_input(cubeb_stream * stm)
+bool has_input(cubeb_stream * stm)
{
return stm->input_stream_params.rate != 0;
}
-bool
-has_output(cubeb_stream * stm)
+bool has_output(cubeb_stream * stm)
{
return stm->output_stream_params.rate != 0;
}
-cubeb_channel
-channel_label_to_cubeb_channel(UInt32 label)
-{
- switch (label) {
- case kAudioChannelLabel_Left:
- return CHANNEL_FRONT_LEFT;
- case kAudioChannelLabel_Right:
- return CHANNEL_FRONT_RIGHT;
- case kAudioChannelLabel_Center:
- return CHANNEL_FRONT_CENTER;
- case kAudioChannelLabel_LFEScreen:
- return CHANNEL_LOW_FREQUENCY;
- case kAudioChannelLabel_LeftSurround:
- return CHANNEL_BACK_LEFT;
- case kAudioChannelLabel_RightSurround:
- return CHANNEL_BACK_RIGHT;
- case kAudioChannelLabel_LeftCenter:
- return CHANNEL_FRONT_LEFT_OF_CENTER;
- case kAudioChannelLabel_RightCenter:
- return CHANNEL_FRONT_RIGHT_OF_CENTER;
- case kAudioChannelLabel_CenterSurround:
- return CHANNEL_BACK_CENTER;
- case kAudioChannelLabel_LeftSurroundDirect:
- return CHANNEL_SIDE_LEFT;
- case kAudioChannelLabel_RightSurroundDirect:
- return CHANNEL_SIDE_RIGHT;
- case kAudioChannelLabel_TopCenterSurround:
- return CHANNEL_TOP_CENTER;
- case kAudioChannelLabel_VerticalHeightLeft:
- return CHANNEL_TOP_FRONT_LEFT;
- case kAudioChannelLabel_VerticalHeightCenter:
- return CHANNEL_TOP_FRONT_CENTER;
- case kAudioChannelLabel_VerticalHeightRight:
- return CHANNEL_TOP_FRONT_RIGHT;
- case kAudioChannelLabel_TopBackLeft:
- return CHANNEL_TOP_BACK_LEFT;
- case kAudioChannelLabel_TopBackCenter:
- return CHANNEL_TOP_BACK_CENTER;
- case kAudioChannelLabel_TopBackRight:
- return CHANNEL_TOP_BACK_RIGHT;
- default:
- return CHANNEL_UNKNOWN;
- }
-}
-
-AudioChannelLabel
-cubeb_channel_to_channel_label(cubeb_channel channel)
-{
- switch (channel) {
- case CHANNEL_FRONT_LEFT:
- return kAudioChannelLabel_Left;
- case CHANNEL_FRONT_RIGHT:
- return kAudioChannelLabel_Right;
- case CHANNEL_FRONT_CENTER:
- return kAudioChannelLabel_Center;
- case CHANNEL_LOW_FREQUENCY:
- return kAudioChannelLabel_LFEScreen;
- case CHANNEL_BACK_LEFT:
- return kAudioChannelLabel_LeftSurround;
- case CHANNEL_BACK_RIGHT:
- return kAudioChannelLabel_RightSurround;
- case CHANNEL_FRONT_LEFT_OF_CENTER:
- return kAudioChannelLabel_LeftCenter;
- case CHANNEL_FRONT_RIGHT_OF_CENTER:
- return kAudioChannelLabel_RightCenter;
- case CHANNEL_BACK_CENTER:
- return kAudioChannelLabel_CenterSurround;
- case CHANNEL_SIDE_LEFT:
- return kAudioChannelLabel_LeftSurroundDirect;
- case CHANNEL_SIDE_RIGHT:
- return kAudioChannelLabel_RightSurroundDirect;
- case CHANNEL_TOP_CENTER:
- return kAudioChannelLabel_TopCenterSurround;
- case CHANNEL_TOP_FRONT_LEFT:
- return kAudioChannelLabel_VerticalHeightLeft;
- case CHANNEL_TOP_FRONT_CENTER:
- return kAudioChannelLabel_VerticalHeightCenter;
- case CHANNEL_TOP_FRONT_RIGHT:
- return kAudioChannelLabel_VerticalHeightRight;
- case CHANNEL_TOP_BACK_LEFT:
- return kAudioChannelLabel_TopBackLeft;
- case CHANNEL_TOP_BACK_CENTER:
- return kAudioChannelLabel_TopBackCenter;
- case CHANNEL_TOP_BACK_RIGHT:
- return kAudioChannelLabel_TopBackRight;
- default:
- return kAudioChannelLabel_Unknown;
- }
-}
-
#if TARGET_OS_IPHONE
typedef UInt32 AudioDeviceID;
typedef UInt32 AudioObjectID;
#define AudioGetCurrentHostTime mach_absolute_time
-#endif
-
uint64_t
-ConvertHostTimeToNanos(uint64_t host_time)
+AudioConvertHostTimeToNanos(uint64_t host_time)
{
static struct mach_timebase_info timebase_info;
static bool initialized = false;
@@ -402,34 +251,27 @@ ConvertHostTimeToNanos(uint64_t host_time)
}
return (uint64_t)answer;
}
+#endif
-static void
-audiounit_increment_active_streams(cubeb * ctx)
+static int64_t
+audiotimestamp_to_latency(AudioTimeStamp const * tstamp, cubeb_stream * stream)
{
- ctx->mutex.assert_current_thread_owns();
- ctx->active_streams += 1;
-}
+ if (!(tstamp->mFlags & kAudioTimeStampHostTimeValid)) {
+ return 0;
+ }
-static void
-audiounit_decrement_active_streams(cubeb * ctx)
-{
- ctx->mutex.assert_current_thread_owns();
- ctx->active_streams -= 1;
-}
+ uint64_t pres = AudioConvertHostTimeToNanos(tstamp->mHostTime);
+ uint64_t now = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
-static int
-audiounit_active_streams(cubeb * ctx)
-{
- ctx->mutex.assert_current_thread_owns();
- return ctx->active_streams;
+ return ((pres - now) * stream->output_desc.mSampleRate) / 1000000000LL;
}
static void
-audiounit_set_global_latency(cubeb * ctx, uint32_t latency_frames)
+audiounit_set_global_latency(cubeb_stream * stm, uint32_t latency_frames)
{
- ctx->mutex.assert_current_thread_owns();
- assert(audiounit_active_streams(ctx) == 1);
- ctx->global_latency_frames = latency_frames;
+ stm->mutex.assert_current_thread_owns();
+ assert(stm->context->active_streams == 1);
+ stm->context->global_latency_frames = latency_frames;
}
static void
@@ -441,8 +283,10 @@ audiounit_make_silent(AudioBuffer * ioData)
}
static OSStatus
-audiounit_render_input(cubeb_stream * stm, AudioUnitRenderActionFlags * flags,
- AudioTimeStamp const * tstamp, UInt32 bus,
+audiounit_render_input(cubeb_stream * stm,
+ AudioUnitRenderActionFlags * flags,
+ AudioTimeStamp const * tstamp,
+ UInt32 bus,
UInt32 input_frames)
{
/* Create the AudioBufferList to store input. */
@@ -450,76 +294,66 @@ audiounit_render_input(cubeb_stream * stm, AudioUnitRenderActionFlags * flags,
input_buffer_list.mBuffers[0].mDataByteSize =
stm->input_desc.mBytesPerFrame * input_frames;
input_buffer_list.mBuffers[0].mData = nullptr;
- input_buffer_list.mBuffers[0].mNumberChannels =
- stm->input_desc.mChannelsPerFrame;
+ input_buffer_list.mBuffers[0].mNumberChannels = stm->input_desc.mChannelsPerFrame;
input_buffer_list.mNumberBuffers = 1;
/* Render input samples */
- OSStatus r = AudioUnitRender(stm->input_unit, flags, tstamp, bus,
- input_frames, &input_buffer_list);
+ OSStatus r = AudioUnitRender(stm->input_unit,
+ flags,
+ tstamp,
+ bus,
+ input_frames,
+ &input_buffer_list);
if (r != noErr) {
- LOG("AudioUnitRender rv=%d", r);
- if (r != kAudioUnitErr_CannotDoInCurrentContext) {
- return r;
- }
- if (stm->output_unit) {
- // kAudioUnitErr_CannotDoInCurrentContext is returned when using a BT
- // headset and the profile is changed from A2DP to HFP/HSP. The previous
- // output device is no longer valid and must be reset.
- audiounit_reinit_stream_async(stm, DEV_INPUT | DEV_OUTPUT);
- }
- // For now state that no error occurred and feed silence, stream will be
- // resumed once reinit has completed.
- ALOGV("(%p) input: reinit pending feeding silence instead", stm);
- stm->input_linear_buffer->push_silence(input_frames *
- stm->input_desc.mChannelsPerFrame);
- } else {
- /* Copy input data in linear buffer. */
- stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
- input_frames *
- stm->input_desc.mChannelsPerFrame);
+ PRINT_ERROR_CODE("AudioUnitRender", r);
+ return r;
}
+ /* Copy input data in linear buffer. */
+ stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
+ input_frames * stm->input_desc.mChannelsPerFrame);
+
+ LOGV("(%p) input: buffers %d, size %d, channels %d, frames %d.",
+ stm, input_buffer_list.mNumberBuffers,
+ input_buffer_list.mBuffers[0].mDataByteSize,
+ input_buffer_list.mBuffers[0].mNumberChannels,
+ input_frames);
+
/* Advance input frame counter. */
assert(input_frames > 0);
stm->frames_read += input_frames;
- ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, "
- "total frames %lu.",
- stm, (unsigned int)input_buffer_list.mNumberBuffers,
- (unsigned int)input_buffer_list.mBuffers[0].mDataByteSize,
- (unsigned int)input_buffer_list.mBuffers[0].mNumberChannels,
- (unsigned int)input_frames,
- stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
-
return noErr;
}
static OSStatus
-audiounit_input_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
- AudioTimeStamp const * tstamp, UInt32 bus,
- UInt32 input_frames, AudioBufferList * /* bufs */)
+audiounit_input_callback(void * user_ptr,
+ AudioUnitRenderActionFlags * flags,
+ AudioTimeStamp const * tstamp,
+ UInt32 bus,
+ UInt32 input_frames,
+ AudioBufferList * /* bufs */)
{
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
+ long outframes;
assert(stm->input_unit != NULL);
assert(AU_IN_BUS == bus);
if (stm->shutdown) {
- ALOG("(%p) input shutdown", stm);
+ LOG("(%p) input shutdown", stm);
return noErr;
}
- if (stm->draining) {
- OSStatus r = AudioOutputUnitStop(stm->input_unit);
- assert(r == 0);
- // Only fire state callback in input-only stream. For duplex stream,
- // the state callback will be fired in output callback.
- if (stm->output_unit == NULL) {
- stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
- }
- return noErr;
+ // This happens when we're finally getting a new input callback after having
+ // switched device, we can clear the input buffer now, only keeping the data
+ // we just got.
+ if (stm->output_callback_in_a_row > stm->expected_output_callbacks_in_a_row) {
+ stm->input_linear_buffer->pop(
+ nullptr,
+ stm->input_linear_buffer->length() -
+ input_frames * stm->input_stream_params.channels);
}
OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames);
@@ -529,199 +363,147 @@ audiounit_input_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
// Full Duplex. We'll call data_callback in the AudioUnit output callback.
if (stm->output_unit != NULL) {
+ stm->output_callback_in_a_row = 0;
return noErr;
}
/* Input only. Call the user callback through resampler.
Resampler will deliver input buffer in the correct rate. */
- assert(input_frames <= stm->input_linear_buffer->length() /
- stm->input_desc.mChannelsPerFrame);
- long total_input_frames =
- stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
- long outframes = cubeb_resampler_fill(stm->resampler.get(),
- stm->input_linear_buffer->data(),
- &total_input_frames, NULL, 0);
- stm->draining = outframes < total_input_frames;
-
+ assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
+ long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
+ outframes = cubeb_resampler_fill(stm->resampler,
+ stm->input_linear_buffer->data(),
+ &total_input_frames,
+ NULL,
+ 0);
// Reset input buffer
stm->input_linear_buffer->clear();
+ if (outframes < 0 || outframes != input_frames) {
+ stm->shutdown = true;
+ return noErr;
+ }
+
return noErr;
}
-static void
-audiounit_mix_output_buffer(cubeb_stream * stm, size_t output_frames,
- void * input_buffer, size_t input_buffer_size,
- void * output_buffer, size_t output_buffer_size)
+static bool
+is_extra_input_needed(cubeb_stream * stm)
{
- assert(input_buffer_size >=
- cubeb_sample_size(stm->output_stream_params.format) *
- stm->output_stream_params.channels * output_frames);
- assert(output_buffer_size >= stm->output_desc.mBytesPerFrame * output_frames);
+ /* If the output callback came first and this is a duplex stream, we need to
+ * fill in some additional silence in the resampler.
+ * Otherwise, if we had more than expected callbacks in a row, or we're currently
+ * switching, we add some silence as well to compensate for the fact that
+ * we're lacking some input data. */
- int r = cubeb_mixer_mix(stm->mixer.get(), output_frames, input_buffer,
- input_buffer_size, output_buffer, output_buffer_size);
- if (r != 0) {
- LOG("Remix error = %d", r);
- }
-}
-
-// Return how many input frames (sampled at input_hw_rate) are needed to provide
-// output_frames (sampled at output_stream_params.rate)
-static int64_t
-minimum_resampling_input_frames(cubeb_stream * stm, uint32_t output_frames)
-{
- if (stm->input_hw_rate == stm->output_stream_params.rate) {
- // Fast path.
- return output_frames;
- }
- return ceil(stm->input_hw_rate * output_frames /
- stm->output_stream_params.rate);
+ /* If resampling is taking place after every output callback
+ * the input buffer expected to be empty. Any frame left over
+ * from resampling is stored inside the resampler available to
+ * be used in next iteration as needed.
+ * BUT when noop_resampler is operating we have left over
+ * frames since it does not store anything internally. */
+ return stm->frames_read == 0 ||
+ (stm->input_linear_buffer->length() == 0 &&
+ (stm->output_callback_in_a_row > stm->expected_output_callbacks_in_a_row ||
+ stm->switching_device));
}
static OSStatus
audiounit_output_callback(void * user_ptr,
AudioUnitRenderActionFlags * /* flags */,
- AudioTimeStamp const * tstamp, UInt32 bus,
- UInt32 output_frames, AudioBufferList * outBufferList)
+ AudioTimeStamp const * tstamp,
+ UInt32 bus,
+ UInt32 output_frames,
+ AudioBufferList * outBufferList)
{
assert(AU_OUT_BUS == bus);
assert(outBufferList->mNumberBuffers == 1);
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
- uint64_t now = ConvertHostTimeToNanos(mach_absolute_time());
- uint64_t audio_output_time = ConvertHostTimeToNanos(tstamp->mHostTime);
- uint64_t output_latency_ns = audio_output_time - now;
-
- const int ns2s = 1e9;
- // The total output latency is the timestamp difference + the stream latency +
- // the hardware latency.
- stm->total_output_latency_frames =
- output_latency_ns * stm->output_hw_rate / ns2s +
- stm->current_latency_frames;
-
- ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input "
- "frames %lu.",
- stm, (unsigned int)outBufferList->mNumberBuffers,
- (unsigned int)outBufferList->mBuffers[0].mDataByteSize,
- (unsigned int)outBufferList->mBuffers[0].mNumberChannels,
- (unsigned int)output_frames,
- has_input(stm) ? stm->input_linear_buffer->length() /
- stm->input_desc.mChannelsPerFrame
- : 0);
-
- long input_frames = 0;
- void *output_buffer = NULL, *input_buffer = NULL;
+ stm->output_callback_in_a_row++;
+
+ LOGV("(%p) output: buffers %d, size %d, channels %d, frames %d.",
+ stm, outBufferList->mNumberBuffers,
+ outBufferList->mBuffers[0].mDataByteSize,
+ outBufferList->mBuffers[0].mNumberChannels, output_frames);
+
+ long outframes = 0, input_frames = 0;
+ void * output_buffer = NULL, * input_buffer = NULL;
if (stm->shutdown) {
- ALOG("(%p) output shutdown.", stm);
+ LOG("(%p) output shutdown.", stm);
audiounit_make_silent(&outBufferList->mBuffers[0]);
return noErr;
}
+ stm->current_latency_frames = audiotimestamp_to_latency(tstamp, stm);
if (stm->draining) {
OSStatus r = AudioOutputUnitStop(stm->output_unit);
assert(r == 0);
+ if (stm->input_unit) {
+ r = AudioOutputUnitStop(stm->input_unit);
+ assert(r == 0);
+ }
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
audiounit_make_silent(&outBufferList->mBuffers[0]);
return noErr;
}
-
/* Get output buffer. */
- if (stm->mixer) {
- // If remixing needs to occur, we can't directly work in our final
- // destination buffer as data may be overwritten or too small to start with.
- size_t size_needed = output_frames * stm->output_stream_params.channels *
- cubeb_sample_size(stm->output_stream_params.format);
- if (stm->temp_buffer_size < size_needed) {
- stm->temp_buffer.reset(new uint8_t[size_needed]);
- stm->temp_buffer_size = size_needed;
- }
- output_buffer = stm->temp_buffer.get();
- } else {
- output_buffer = outBufferList->mBuffers[0].mData;
- }
-
- stm->frames_written += output_frames;
-
+ output_buffer = outBufferList->mBuffers[0].mData;
/* If Full duplex get also input buffer */
if (stm->input_unit != NULL) {
- /* If the output callback came first and this is a duplex stream, we need to
- * fill in some additional silence in the resampler.
- * Otherwise, if we had more than expected callbacks in a row, or we're
- * currently switching, we add some silence as well to compensate for the
- * fact that we're lacking some input data. */
- uint32_t input_frames_needed =
- minimum_resampling_input_frames(stm, stm->frames_written);
- long missing_frames = input_frames_needed - stm->frames_read;
- if (missing_frames > 0) {
- stm->input_linear_buffer->push_silence(missing_frames *
- stm->input_desc.mChannelsPerFrame);
- stm->frames_read = input_frames_needed;
-
- ALOG("(%p) %s pushed %ld frames of input silence.", stm,
- stm->frames_read == 0 ? "Input hasn't started,"
- : stm->switching_device ? "Device switching,"
- : "Drop out,",
- missing_frames);
- }
+ if (is_extra_input_needed(stm)) {
+ uint32_t min_input_frames_required = ceilf(stm->input_hw_rate / stm->output_hw_rate *
+ stm->input_buffer_frames);
+ stm->input_linear_buffer->push_silence(min_input_frames_required * stm->input_desc.mChannelsPerFrame);
+ LOG("(%p) %s pushed %u frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," :
+ stm->switching_device ? "Device switching," : "Drop out,", min_input_frames_required);
+ }
+ // The input buffer
input_buffer = stm->input_linear_buffer->data();
- // Number of input frames in the buffer. It will change to actually used
- // frames inside fill
- input_frames =
- stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
+ // Number of input frames in the buffer
+ input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
}
/* Call user callback through resampler. */
- long outframes = cubeb_resampler_fill(stm->resampler.get(), input_buffer,
- input_buffer ? &input_frames : NULL,
- output_buffer, output_frames);
+ outframes = cubeb_resampler_fill(stm->resampler,
+ input_buffer,
+ input_buffer ? &input_frames : NULL,
+ output_buffer,
+ output_frames);
if (input_buffer) {
- // Pop from the buffer the frames used by the the resampler.
- stm->input_linear_buffer->pop(input_frames *
- stm->input_desc.mChannelsPerFrame);
+ stm->input_linear_buffer->pop(nullptr, input_frames * stm->input_desc.mChannelsPerFrame);
}
- if (outframes < 0 || outframes > output_frames) {
+ if (outframes < 0) {
stm->shutdown = true;
- OSStatus r = AudioOutputUnitStop(stm->output_unit);
- assert(r == 0);
- if (stm->input_unit) {
- r = AudioOutputUnitStop(stm->input_unit);
- assert(r == 0);
- }
- stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
- audiounit_make_silent(&outBufferList->mBuffers[0]);
return noErr;
}
- stm->draining = (UInt32)outframes < output_frames;
+ size_t outbpf = stm->output_desc.mBytesPerFrame;
+ stm->draining = outframes < output_frames;
stm->frames_played = stm->frames_queued;
stm->frames_queued += outframes;
+ AudioFormatFlags outaff = stm->output_desc.mFormatFlags;
+ float panning = (stm->output_desc.mChannelsPerFrame == 2) ?
+ stm->panning.load(std::memory_order_relaxed) : 0.0f;
+
/* Post process output samples. */
if (stm->draining) {
/* Clear missing frames (silence) */
- size_t channels = stm->output_stream_params.channels;
- size_t missing_samples = (output_frames - outframes) * channels;
- size_t size_sample = cubeb_sample_size(stm->output_stream_params.format);
- /* number of bytes that have been filled with valid audio by the callback.
- */
- size_t audio_byte_count = outframes * channels * size_sample;
- PodZero((uint8_t *)output_buffer + audio_byte_count,
- missing_samples * size_sample);
+ memset((uint8_t*)output_buffer + outframes * outbpf, 0, (output_frames - outframes) * outbpf);
}
-
- /* Mixing */
- if (stm->mixer) {
- audiounit_mix_output_buffer(stm, output_frames, output_buffer,
- stm->temp_buffer_size,
- outBufferList->mBuffers[0].mData,
- outBufferList->mBuffers[0].mDataByteSize);
+ /* Pan stereo. */
+ if (panning != 0.0f) {
+ if (outaff & kAudioFormatFlagIsFloat) {
+ cubeb_pan_stereo_buffer_float((float*)output_buffer, outframes, panning);
+ } else if (outaff & kAudioFormatFlagIsSignedInteger) {
+ cubeb_pan_stereo_buffer_int((short*)output_buffer, outframes, panning);
+ }
}
-
return noErr;
}
@@ -729,11 +511,25 @@ extern "C" {
int
audiounit_init(cubeb ** context, char const * /* context_name */)
{
+ cubeb * ctx;
+
+ *context = NULL;
+
+ ctx = (cubeb *)calloc(1, sizeof(cubeb));
+ assert(ctx);
+ // Placement new to call the ctors of cubeb members.
+ new (ctx) cubeb();
+
+ ctx->ops = &audiounit_ops;
+
+ ctx->active_streams = 0;
+
+ ctx->limit_streams = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber10_7;
#if !TARGET_OS_IPHONE
cubeb_set_coreaudio_notification_runloop();
#endif
- *context = new cubeb;
+ *context = ctx;
return CUBEB_OK;
}
@@ -746,338 +542,255 @@ audiounit_get_backend_id(cubeb * /* ctx */)
}
#if !TARGET_OS_IPHONE
-
-static int
-audiounit_stream_get_volume(cubeb_stream * stm, float * volume);
static int
-audiounit_stream_set_volume(cubeb_stream * stm, float volume);
-
-static int
-audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side)
+audiounit_get_output_device_id(AudioDeviceID * device_id)
{
- assert(stm);
+ UInt32 size;
+ OSStatus r;
+ AudioObjectPropertyAddress output_device_address = {
+ kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
- device_info * info = nullptr;
- cubeb_device_type type = CUBEB_DEVICE_TYPE_UNKNOWN;
+ size = sizeof(*device_id);
- if (side == io_side::INPUT) {
- info = &stm->input_device;
- type = CUBEB_DEVICE_TYPE_INPUT;
- } else if (side == io_side::OUTPUT) {
- info = &stm->output_device;
- type = CUBEB_DEVICE_TYPE_OUTPUT;
+ r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+ &output_device_address,
+ 0,
+ NULL,
+ &size,
+ device_id);
+ if (r != noErr) {
+ PRINT_ERROR_CODE("output_device_id", r);
+ return CUBEB_ERROR;
}
- memset(info, 0, sizeof(device_info));
- info->id = id;
- if (side == io_side::INPUT) {
- info->flags |= DEV_INPUT;
- } else if (side == io_side::OUTPUT) {
- info->flags |= DEV_OUTPUT;
- }
+ return CUBEB_OK;
+}
- AudioDeviceID default_device_id = audiounit_get_default_device_id(type);
- if (default_device_id == kAudioObjectUnknown) {
- return CUBEB_ERROR;
- }
- if (id == kAudioObjectUnknown) {
- info->id = default_device_id;
- info->flags |= DEV_SELECTED_DEFAULT;
- }
+static int
+audiounit_get_input_device_id(AudioDeviceID * device_id)
+{
+ UInt32 size;
+ OSStatus r;
+ AudioObjectPropertyAddress input_device_address = {
+ kAudioHardwarePropertyDefaultInputDevice,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
- if (info->id == default_device_id) {
- info->flags |= DEV_SYSTEM_DEFAULT;
- }
+ size = sizeof(*device_id);
- assert(info->id);
- assert(info->flags & DEV_INPUT && !(info->flags & DEV_OUTPUT) ||
- !(info->flags & DEV_INPUT) && info->flags & DEV_OUTPUT);
+ r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
+ &input_device_address,
+ 0,
+ NULL,
+ &size,
+ device_id);
+ if (r != noErr) {
+ return CUBEB_ERROR;
+ }
return CUBEB_OK;
}
+static int audiounit_stream_get_volume(cubeb_stream * stm, float * volume);
+static int audiounit_stream_set_volume(cubeb_stream * stm, float volume);
+static int audiounit_uninstall_device_changed_callback(cubeb_stream * stm);
+
static int
-audiounit_reinit_stream(cubeb_stream * stm, device_flags_value flags)
+audiounit_reinit_stream(cubeb_stream * stm)
{
auto_lock context_lock(stm->context->mutex);
- assert((flags & DEV_INPUT && stm->input_unit) ||
- (flags & DEV_OUTPUT && stm->output_unit));
if (!stm->shutdown) {
audiounit_stream_stop_internal(stm);
}
int r = audiounit_uninstall_device_changed_callback(stm);
if (r != CUBEB_OK) {
- LOG("(%p) Could not uninstall all device change listeners.", stm);
+ LOG("(%p) Could not uninstall the device changed callback", stm);
}
{
auto_lock lock(stm->mutex);
float volume = 0.0;
- int vol_rv = CUBEB_ERROR;
- if (stm->output_unit) {
- vol_rv = audiounit_stream_get_volume(stm, &volume);
- }
+ int vol_rv = audiounit_stream_get_volume(stm, &volume);
audiounit_close_stream(stm);
- /* Reinit occurs in one of the following case:
- * - When the device is not alive any more
- * - When the default system device change.
- * - The bluetooth device changed from A2DP to/from HFP/HSP profile
- * We first attempt to re-use the same device id, should that fail we will
- * default to the (potentially new) default device. */
- AudioDeviceID input_device =
- flags & DEV_INPUT ? stm->input_device.id : kAudioObjectUnknown;
- if (flags & DEV_INPUT) {
- r = audiounit_set_device_info(stm, input_device, io_side::INPUT);
- if (r != CUBEB_OK) {
- LOG("(%p) Set input device info failed. This can happen when last "
- "media device is unplugged",
- stm);
- return CUBEB_ERROR;
- }
- }
-
- /* Always use the default output on reinit. This is not correct in every
- * case but it is sufficient for Firefox and prevent reinit from reporting
- * failures. It will change soon when reinit mechanism will be updated. */
- r = audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::OUTPUT);
- if (r != CUBEB_OK) {
- LOG("(%p) Set output device info failed. This can happen when last media "
- "device is unplugged",
- stm);
- return CUBEB_ERROR;
- }
-
if (audiounit_setup_stream(stm) != CUBEB_OK) {
LOG("(%p) Stream reinit failed.", stm);
- if (flags & DEV_INPUT && input_device != kAudioObjectUnknown) {
- // Attempt to re-use the same device-id failed, so attempt again with
- // default input device.
- audiounit_close_stream(stm);
- if (audiounit_set_device_info(stm, kAudioObjectUnknown,
- io_side::INPUT) != CUBEB_OK ||
- audiounit_setup_stream(stm) != CUBEB_OK) {
- LOG("(%p) Second stream reinit failed.", stm);
- return CUBEB_ERROR;
- }
- }
+ return CUBEB_ERROR;
}
if (vol_rv == CUBEB_OK) {
audiounit_stream_set_volume(stm, volume);
}
+ // 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 (!stm->shutdown) {
- r = audiounit_stream_start_internal(stm);
- if (r != CUBEB_OK) {
- return CUBEB_ERROR;
- }
+ audiounit_stream_start_internal(stm);
}
}
return CUBEB_OK;
}
-static void
-audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags)
-{
- if (std::atomic_exchange(&stm->reinit_pending, true)) {
- // A reinit task is already pending, nothing more to do.
- ALOG("(%p) re-init stream task already pending, cancelling request", stm);
- return;
- }
-
- // 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 (stm->destroy_pending) {
- ALOG("(%p) stream pending destroy, cancelling reinit task", stm);
- return;
- }
-
- if (audiounit_reinit_stream(stm, flags) != CUBEB_OK) {
- if (audiounit_uninstall_system_changed_callback(stm) != CUBEB_OK) {
- LOG("(%p) Could not uninstall system changed callback", stm);
- }
- stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
- LOG("(%p) Could not reopen the stream after switching.", stm);
- }
- stm->switching_device = false;
- stm->reinit_pending = false;
- });
-}
-
-static char const *
-event_addr_to_string(AudioObjectPropertySelector selector)
-{
- switch (selector) {
- case kAudioHardwarePropertyDefaultOutputDevice:
- return "kAudioHardwarePropertyDefaultOutputDevice";
- case kAudioHardwarePropertyDefaultInputDevice:
- return "kAudioHardwarePropertyDefaultInputDevice";
- case kAudioDevicePropertyDeviceIsAlive:
- return "kAudioDevicePropertyDeviceIsAlive";
- case kAudioDevicePropertyDataSource:
- return "kAudioDevicePropertyDataSource";
- default:
- return "Unknown";
- }
-}
-
static OSStatus
-audiounit_property_listener_callback(
- AudioObjectID id, UInt32 address_count,
- const AudioObjectPropertyAddress * addresses, void * user)
-{
- cubeb_stream * stm = (cubeb_stream *)user;
- if (stm->switching_device) {
- LOG("Switching is already taking place. Skip Event %s for id=%d",
- event_addr_to_string(addresses[0].mSelector), id);
- return noErr;
- }
+audiounit_property_listener_callback(AudioObjectID /* id */, UInt32 address_count,
+ const AudioObjectPropertyAddress * addresses,
+ void * user)
+{
+ cubeb_stream * stm = (cubeb_stream*) user;
stm->switching_device = true;
- LOG("(%p) Audio device changed, %u events.", stm,
- (unsigned int)address_count);
+ 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[%u] - mSelector == kAudioHardwarePropertyDefaultOutputDevice "
- "for id=%d",
- (unsigned int)i, id);
- } break;
- case kAudioHardwarePropertyDefaultInputDevice: {
- LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultInputDevice "
- "for id=%d",
- (unsigned int)i, id);
- } break;
- case kAudioDevicePropertyDeviceIsAlive: {
- LOG("Event[%u] - mSelector == kAudioDevicePropertyDeviceIsAlive for "
- "id=%d",
- (unsigned int)i, id);
- // If this is the default input device ignore the event,
- // kAudioHardwarePropertyDefaultInputDevice will take care of the switch
- if (stm->input_device.flags & DEV_SYSTEM_DEFAULT) {
- LOG("It's the default input device, ignore the event");
- stm->switching_device = false;
- return noErr;
- }
- } break;
- case kAudioDevicePropertyDataSource: {
- LOG("Event[%u] - mSelector == kAudioDevicePropertyDataSource for id=%d",
- (unsigned int)i, id);
- } break;
- default:
- LOG("Event[%u] - mSelector == Unexpected Event id %d, return",
- (unsigned int)i, addresses[i].mSelector);
- stm->switching_device = false;
- return noErr;
+ switch(addresses[i].mSelector) {
+ case kAudioHardwarePropertyDefaultOutputDevice: {
+ LOG("Event[%d] - mSelector == kAudioHardwarePropertyDefaultOutputDevice", i);
+ // Allow restart to choose the new default
+ stm->output_device = nullptr;
+ }
+ break;
+ case kAudioHardwarePropertyDefaultInputDevice: {
+ LOG("Event[%d] - mSelector == kAudioHardwarePropertyDefaultInputDevice", i);
+ // Allow restart to choose the new default
+ stm->input_device = nullptr;
+ }
+ break;
+ case kAudioDevicePropertyDeviceIsAlive: {
+ LOG("Event[%d] - mSelector == kAudioDevicePropertyDeviceIsAlive", i);
+ // If this is the default input device ignore the event,
+ // kAudioHardwarePropertyDefaultInputDevice will take care of the switch
+ if (stm->is_default_input) {
+ LOG("It's the default input device, ignore the event");
+ return noErr;
+ }
+ // Allow restart to choose the new default. Event register only for input.
+ stm->input_device = nullptr;
+ }
+ break;
+ case kAudioDevicePropertyDataSource: {
+ LOG("Event[%d] - mSelector == kAudioHardwarePropertyDataSource", i);
+ return noErr;
+ }
}
}
- // Allow restart to choose the new default
- device_flags_value switch_side = DEV_UNKNOWN;
- if (has_input(stm)) {
- switch_side |= DEV_INPUT;
- }
- if (has_output(stm)) {
- switch_side |= DEV_OUTPUT;
- }
-
for (UInt32 i = 0; i < address_count; i++) {
- switch (addresses[i].mSelector) {
+ switch(addresses[i].mSelector) {
case kAudioHardwarePropertyDefaultOutputDevice:
case kAudioHardwarePropertyDefaultInputDevice:
case kAudioDevicePropertyDeviceIsAlive:
/* fall through */
case kAudioDevicePropertyDataSource: {
- auto_lock dev_cb_lock(stm->device_changed_callback_lock);
- if (stm->device_changed_callback) {
- stm->device_changed_callback(stm->user_ptr);
+ auto_lock lock(stm->mutex);
+ if (stm->device_changed_callback) {
+ stm->device_changed_callback(stm->user_ptr);
+ }
+ break;
}
- break;
- }
}
}
- audiounit_reinit_stream_async(stm, switch_side);
+ // 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) != CUBEB_OK) {
+ stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
+ LOG("(%p) Could not reopen the stream after switching.", stm);
+ }
+ stm->switching_device = false;
+ });
return noErr;
}
OSStatus
-audiounit_add_listener(const property_listener * listener)
+audiounit_add_listener(cubeb_stream * stm, AudioDeviceID id, AudioObjectPropertySelector selector,
+ AudioObjectPropertyScope scope, AudioObjectPropertyListenerProc listener)
{
- assert(listener);
- return AudioObjectAddPropertyListener(listener->device_id,
- listener->property_address,
- listener->callback, listener->stream);
+ AudioObjectPropertyAddress address = {
+ selector,
+ scope,
+ kAudioObjectPropertyElementMaster
+ };
+
+ return AudioObjectAddPropertyListener(id, &address, listener, stm);
}
OSStatus
-audiounit_remove_listener(const property_listener * listener)
+audiounit_remove_listener(cubeb_stream * stm, AudioDeviceID id,
+ AudioObjectPropertySelector selector,
+ AudioObjectPropertyScope scope,
+ AudioObjectPropertyListenerProc listener)
{
- assert(listener);
- return AudioObjectRemovePropertyListener(
- listener->device_id, listener->property_address, listener->callback,
- listener->stream);
+ AudioObjectPropertyAddress address = {
+ selector,
+ scope,
+ kAudioObjectPropertyElementMaster
+ };
+
+ return AudioObjectRemovePropertyListener(id, &address, listener, stm);
}
+static AudioObjectID audiounit_get_default_device_id(cubeb_device_type type);
+
static int
audiounit_install_device_changed_callback(cubeb_stream * stm)
{
- OSStatus rv;
- int r = CUBEB_OK;
+ OSStatus r;
if (stm->output_unit) {
- /* This event will notify us when the data source on the same device
- * changes, for example when the user plugs in a normal (non-usb) headset in
- * the headphone jack. */
- stm->output_source_listener.reset(new property_listener(
- stm->output_device.id, &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS,
- &audiounit_property_listener_callback, stm));
- rv = audiounit_add_listener(stm->output_source_listener.get());
- if (rv != noErr) {
- stm->output_source_listener.reset();
- LOG("AudioObjectAddPropertyListener/output/"
- "kAudioDevicePropertyDataSource rv=%d, device id=%d",
- rv, stm->output_device.id);
- r = CUBEB_ERROR;
+ /* This event will notify us when the data source on the same device changes,
+ * for example when the user plugs in a normal (non-usb) headset in the
+ * headphone jack. */
+ AudioDeviceID output_dev_id;
+ r = audiounit_get_output_device_id(&output_dev_id);
+ if (r != noErr) {
+ return CUBEB_ERROR;
+ }
+
+ r = audiounit_add_listener(stm, output_dev_id, kAudioDevicePropertyDataSource,
+ kAudioDevicePropertyScopeOutput, &audiounit_property_listener_callback);
+ if (r != noErr) {
+ PRINT_ERROR_CODE("AudioObjectAddPropertyListener/output/kAudioDevicePropertyDataSource", r);
+ return CUBEB_ERROR;
}
}
if (stm->input_unit) {
- /* This event will notify us when the data source on the input device
- * changes. */
- stm->input_source_listener.reset(new property_listener(
- stm->input_device.id, &INPUT_DATA_SOURCE_PROPERTY_ADDRESS,
- &audiounit_property_listener_callback, stm));
- rv = audiounit_add_listener(stm->input_source_listener.get());
- if (rv != noErr) {
- stm->input_source_listener.reset();
- LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDataSource "
- "rv=%d, device id=%d",
- rv, stm->input_device.id);
- r = CUBEB_ERROR;
+ /* This event will notify us when the data source on the input device changes. */
+ AudioDeviceID input_dev_id;
+ r = audiounit_get_input_device_id(&input_dev_id);
+ if (r != noErr) {
+ return CUBEB_ERROR;
+ }
+
+ r = audiounit_add_listener(stm, input_dev_id, kAudioDevicePropertyDataSource,
+ kAudioDevicePropertyScopeInput, &audiounit_property_listener_callback);
+ if (r != noErr) {
+ PRINT_ERROR_CODE("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDataSource", r);
+ return CUBEB_ERROR;
}
/* Event to notify when the input is going away. */
- stm->input_alive_listener.reset(new property_listener(
- stm->input_device.id, &DEVICE_IS_ALIVE_PROPERTY_ADDRESS,
- &audiounit_property_listener_callback, stm));
- rv = audiounit_add_listener(stm->input_alive_listener.get());
- if (rv != noErr) {
- stm->input_alive_listener.reset();
- LOG("AudioObjectAddPropertyListener/input/"
- "kAudioDevicePropertyDeviceIsAlive rv=%d, device id =%d",
- rv, stm->input_device.id);
- r = CUBEB_ERROR;
+ AudioDeviceID dev = stm->input_device ? reinterpret_cast<intptr_t>(stm->input_device) :
+ audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT);
+ r = audiounit_add_listener(stm, dev, kAudioDevicePropertyDeviceIsAlive,
+ kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
+ if (r != noErr) {
+ PRINT_ERROR_CODE("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDeviceIsAlive", r);
+ return CUBEB_ERROR;
}
}
- return r;
+ return CUBEB_OK;
}
static int
@@ -1087,33 +800,23 @@ audiounit_install_system_changed_callback(cubeb_stream * stm)
if (stm->output_unit) {
/* This event will notify us when the default audio device changes,
- * for example when the user plugs in a USB headset and the system chooses
- * it automatically as the default, or when another device is chosen in the
+ * for example when the user plugs in a USB headset and the system chooses it
+ * automatically as the default, or when another device is chosen in the
* dropdown list. */
- stm->default_output_listener.reset(new property_listener(
- kAudioObjectSystemObject, &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS,
- &audiounit_property_listener_callback, stm));
- r = audiounit_add_listener(stm->default_output_listener.get());
+ r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
- stm->default_output_listener.reset();
- LOG("AudioObjectAddPropertyListener/output/"
- "kAudioHardwarePropertyDefaultOutputDevice rv=%d",
- r);
+ LOG("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice rv=%d", r);
return CUBEB_ERROR;
}
}
if (stm->input_unit) {
/* This event will notify us when the default input device changes. */
- stm->default_input_listener.reset(new property_listener(
- kAudioObjectSystemObject, &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS,
- &audiounit_property_listener_callback, stm));
- r = audiounit_add_listener(stm->default_input_listener.get());
+ r = audiounit_add_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice,
+ kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
- stm->default_input_listener.reset();
- LOG("AudioObjectAddPropertyListener/input/"
- "kAudioHardwarePropertyDefaultInputDevice rv=%d",
- r);
+ LOG("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice rv=%d", r);
return CUBEB_ERROR;
}
}
@@ -1124,44 +827,36 @@ audiounit_install_system_changed_callback(cubeb_stream * stm)
static int
audiounit_uninstall_device_changed_callback(cubeb_stream * stm)
{
- OSStatus rv;
- // Failing to uninstall listeners is not a fatal error.
- int r = CUBEB_OK;
+ OSStatus r;
- if (stm->output_source_listener) {
- rv = audiounit_remove_listener(stm->output_source_listener.get());
- if (rv != noErr) {
- LOG("AudioObjectRemovePropertyListener/output/"
- "kAudioDevicePropertyDataSource rv=%d, device id=%d",
- rv, stm->output_device.id);
- r = CUBEB_ERROR;
+ if (stm->output_unit) {
+ AudioDeviceID output_dev_id;
+ r = audiounit_get_output_device_id(&output_dev_id);
+ if (r != noErr) {
+ return CUBEB_ERROR;
}
- stm->output_source_listener.reset();
- }
- if (stm->input_source_listener) {
- rv = audiounit_remove_listener(stm->input_source_listener.get());
- if (rv != noErr) {
- LOG("AudioObjectRemovePropertyListener/input/"
- "kAudioDevicePropertyDataSource rv=%d, device id=%d",
- rv, stm->input_device.id);
- r = CUBEB_ERROR;
+ r = audiounit_remove_listener(stm, output_dev_id, kAudioDevicePropertyDataSource,
+ kAudioDevicePropertyScopeOutput, &audiounit_property_listener_callback);
+ if (r != noErr) {
+ return CUBEB_ERROR;
}
- stm->input_source_listener.reset();
}
- if (stm->input_alive_listener) {
- rv = audiounit_remove_listener(stm->input_alive_listener.get());
- if (rv != noErr) {
- LOG("AudioObjectRemovePropertyListener/input/"
- "kAudioDevicePropertyDeviceIsAlive rv=%d, device id=%d",
- rv, stm->input_device.id);
- r = CUBEB_ERROR;
+ if (stm->input_unit) {
+ AudioDeviceID input_dev_id;
+ r = audiounit_get_input_device_id(&input_dev_id);
+ if (r != noErr) {
+ return CUBEB_ERROR;
}
- stm->input_alive_listener.reset();
- }
- return r;
+ r = audiounit_remove_listener(stm, input_dev_id, kAudioDevicePropertyDataSource,
+ kAudioDevicePropertyScopeInput, &audiounit_property_listener_callback);
+ if (r != noErr) {
+ return CUBEB_ERROR;
+ }
+ }
+ return CUBEB_OK;
}
static int
@@ -1169,20 +864,20 @@ audiounit_uninstall_system_changed_callback(cubeb_stream * stm)
{
OSStatus r;
- if (stm->default_output_listener) {
- r = audiounit_remove_listener(stm->default_output_listener.get());
+ if (stm->output_unit) {
+ r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultOutputDevice,
+ kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
return CUBEB_ERROR;
}
- stm->default_output_listener.reset();
}
- if (stm->default_input_listener) {
- r = audiounit_remove_listener(stm->default_input_listener.get());
+ if (stm->input_unit) {
+ r = audiounit_remove_listener(stm, kAudioObjectSystemObject, kAudioHardwarePropertyDefaultInputDevice,
+ kAudioObjectPropertyScopeGlobal, &audiounit_property_listener_callback);
if (r != noErr) {
return CUBEB_ERROR;
}
- stm->default_input_listener.reset();
}
return CUBEB_OK;
}
@@ -1195,11 +890,12 @@ audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
OSStatus r;
AudioDeviceID output_device_id;
AudioObjectPropertyAddress output_device_buffer_size_range = {
- kAudioDevicePropertyBufferFrameSizeRange, kAudioDevicePropertyScopeOutput,
- kAudioObjectPropertyElementMaster};
+ kAudioDevicePropertyBufferFrameSizeRange,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
- output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
- if (output_device_id == kAudioObjectUnknown) {
+ if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
LOG("Could not get default output device id.");
return CUBEB_ERROR;
}
@@ -1208,10 +904,13 @@ audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
size = sizeof(*latency_range);
r = AudioObjectGetPropertyData(output_device_id,
- &output_device_buffer_size_range, 0, NULL,
- &size, latency_range);
+ &output_device_buffer_size_range,
+ 0,
+ NULL,
+ &size,
+ latency_range);
if (r != noErr) {
- LOG("AudioObjectGetPropertyData/buffer size range rv=%d", r);
+ PRINT_ERROR_CODE("AudioObjectGetPropertyData/buffer size range", r);
return CUBEB_ERROR;
}
@@ -1222,19 +921,20 @@ audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
static AudioObjectID
audiounit_get_default_device_id(cubeb_device_type type)
{
- const AudioObjectPropertyAddress * adr;
+ AudioObjectPropertyAddress adr = { 0, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
+ AudioDeviceID devid;
+ UInt32 size;
+
if (type == CUBEB_DEVICE_TYPE_OUTPUT) {
- adr = &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS;
+ adr.mSelector = kAudioHardwarePropertyDefaultOutputDevice;
} else if (type == CUBEB_DEVICE_TYPE_INPUT) {
- adr = &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS;
+ adr.mSelector = kAudioHardwarePropertyDefaultInputDevice;
} else {
return kAudioObjectUnknown;
}
- AudioDeviceID devid;
- UInt32 size = sizeof(AudioDeviceID);
- if (AudioObjectGetPropertyData(kAudioObjectSystemObject, adr, 0, NULL, &size,
- &devid) != noErr) {
+ size = sizeof(AudioDeviceID);
+ if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, &devid) != noErr) {
return kAudioObjectUnknown;
}
@@ -1245,7 +945,7 @@ int
audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
{
#if TARGET_OS_IPHONE
- // TODO: [[AVAudioSession sharedInstance] maximumOutputNumberOfChannels]
+ //TODO: [[AVAudioSession sharedInstance] maximumOutputNumberOfChannels]
*max_channels = 2;
#else
UInt32 size;
@@ -1253,22 +953,27 @@ audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
AudioDeviceID output_device_id;
AudioStreamBasicDescription stream_format;
AudioObjectPropertyAddress stream_format_address = {
- kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput,
- kAudioObjectPropertyElementMaster};
+ kAudioDevicePropertyStreamFormat,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
assert(ctx && max_channels);
- output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
- if (output_device_id == kAudioObjectUnknown) {
+ if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
return CUBEB_ERROR;
}
size = sizeof(stream_format);
- r = AudioObjectGetPropertyData(output_device_id, &stream_format_address, 0,
- NULL, &size, &stream_format);
+ r = AudioObjectGetPropertyData(output_device_id,
+ &stream_format_address,
+ 0,
+ NULL,
+ &size,
+ &stream_format);
if (r != noErr) {
- LOG("AudioObjectPropertyAddress/StreamFormat rv=%d", r);
+ PRINT_ERROR_CODE("AudioObjectPropertyAddress/StreamFormat", r);
return CUBEB_ERROR;
}
@@ -1278,11 +983,12 @@ audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
}
static int
-audiounit_get_min_latency(cubeb * /* ctx */, cubeb_stream_params /* params */,
+audiounit_get_min_latency(cubeb * /* ctx */,
+ cubeb_stream_params /* params */,
uint32_t * latency_frames)
{
#if TARGET_OS_IPHONE
- // TODO: [[AVAudioSession sharedInstance] inputLatency]
+ //TODO: [[AVAudioSession sharedInstance] inputLatency]
return CUBEB_ERROR_NOT_SUPPORTED;
#else
AudioValueRange latency_range;
@@ -1291,8 +997,8 @@ audiounit_get_min_latency(cubeb * /* ctx */, cubeb_stream_params /* params */,
return CUBEB_ERROR;
}
- *latency_frames =
- max<uint32_t>(latency_range.mMinimum, SAFE_MIN_LATENCY_FRAMES);
+ *latency_frames = std::max<uint32_t>(latency_range.mMinimum,
+ SAFE_MIN_LATENCY_FRAMES);
#endif
return CUBEB_OK;
@@ -1302,7 +1008,7 @@ static int
audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate)
{
#if TARGET_OS_IPHONE
- // TODO
+ //TODO
return CUBEB_ERROR_NOT_SUPPORTED;
#else
UInt32 size;
@@ -1310,17 +1016,22 @@ audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate)
Float64 fsamplerate;
AudioDeviceID output_device_id;
AudioObjectPropertyAddress samplerate_address = {
- kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
+ kAudioDevicePropertyNominalSampleRate,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster
+ };
- output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
- if (output_device_id == kAudioObjectUnknown) {
+ if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
return CUBEB_ERROR;
}
size = sizeof(fsamplerate);
- r = AudioObjectGetPropertyData(output_device_id, &samplerate_address, 0, NULL,
- &size, &fsamplerate);
+ r = AudioObjectGetPropertyData(output_device_id,
+ &samplerate_address,
+ 0,
+ NULL,
+ &size,
+ &fsamplerate);
if (r != noErr) {
return CUBEB_ERROR;
@@ -1331,133 +1042,27 @@ audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate)
return CUBEB_OK;
}
-static cubeb_channel_layout
-audiounit_convert_channel_layout(AudioChannelLayout * layout)
-{
- // When having one or two channel, force mono or stereo. Some devices (namely,
- // Bose QC35, mark 1 and 2), expose a single channel mapped to the right for
- // some reason.
- if (layout->mNumberChannelDescriptions == 1) {
- return CUBEB_LAYOUT_MONO;
- } else if (layout->mNumberChannelDescriptions == 2) {
- return CUBEB_LAYOUT_STEREO;
- }
-
- if (layout->mChannelLayoutTag !=
- kAudioChannelLayoutTag_UseChannelDescriptions) {
- // kAudioChannelLayoutTag_UseChannelBitmap
- // kAudioChannelLayoutTag_Mono
- // kAudioChannelLayoutTag_Stereo
- // ....
- LOG("Only handle UseChannelDescriptions for now.\n");
- return CUBEB_LAYOUT_UNDEFINED;
- }
-
- cubeb_channel_layout cl = 0;
- for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) {
- cubeb_channel cc = channel_label_to_cubeb_channel(
- layout->mChannelDescriptions[i].mChannelLabel);
- if (cc == CHANNEL_UNKNOWN) {
- return CUBEB_LAYOUT_UNDEFINED;
- }
- cl |= cc;
- }
-
- return cl;
-}
-
-static cubeb_channel_layout
-audiounit_get_preferred_channel_layout(AudioUnit output_unit)
-{
- OSStatus rv = noErr;
- UInt32 size = 0;
- rv = AudioUnitGetPropertyInfo(
- output_unit, kAudioDevicePropertyPreferredChannelLayout,
- kAudioUnitScope_Output, AU_OUT_BUS, &size, nullptr);
- if (rv != noErr) {
- LOG("AudioUnitGetPropertyInfo/kAudioDevicePropertyPreferredChannelLayout "
- "rv=%d",
- rv);
- return CUBEB_LAYOUT_UNDEFINED;
- }
- assert(size > 0);
-
- auto layout = make_sized_audio_channel_layout(size);
- rv = AudioUnitGetProperty(
- output_unit, kAudioDevicePropertyPreferredChannelLayout,
- kAudioUnitScope_Output, AU_OUT_BUS, layout.get(), &size);
- if (rv != noErr) {
- LOG("AudioUnitGetProperty/kAudioDevicePropertyPreferredChannelLayout rv=%d",
- rv);
- return CUBEB_LAYOUT_UNDEFINED;
- }
-
- return audiounit_convert_channel_layout(layout.get());
-}
-
-static cubeb_channel_layout
-audiounit_get_current_channel_layout(AudioUnit output_unit)
-{
- OSStatus rv = noErr;
- UInt32 size = 0;
- rv = AudioUnitGetPropertyInfo(
- output_unit, kAudioUnitProperty_AudioChannelLayout,
- kAudioUnitScope_Output, AU_OUT_BUS, &size, nullptr);
- if (rv != noErr) {
- LOG("AudioUnitGetPropertyInfo/kAudioUnitProperty_AudioChannelLayout rv=%d",
- rv);
- // This property isn't known before macOS 10.12, attempt another method.
- return audiounit_get_preferred_channel_layout(output_unit);
- }
- assert(size > 0);
-
- auto layout = make_sized_audio_channel_layout(size);
- rv = AudioUnitGetProperty(output_unit, kAudioUnitProperty_AudioChannelLayout,
- kAudioUnitScope_Output, AU_OUT_BUS, layout.get(),
- &size);
- if (rv != noErr) {
- LOG("AudioUnitGetProperty/kAudioUnitProperty_AudioChannelLayout rv=%d", rv);
- return CUBEB_LAYOUT_UNDEFINED;
- }
-
- return audiounit_convert_channel_layout(layout.get());
-}
-
-static int
-audiounit_create_unit(AudioUnit * unit, device_info * device);
-
-static OSStatus
-audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype);
+static OSStatus audiounit_remove_device_listener(cubeb * context);
static void
audiounit_destroy(cubeb * ctx)
{
+ // Disabling this assert for bug 1083664 -- we seem to leak a stream
+ // assert(ctx->active_streams == 0);
+
{
auto_lock lock(ctx->mutex);
-
- // Disabling this assert for bug 1083664 -- we seem to leak a stream
- // assert(ctx->active_streams == 0);
- if (audiounit_active_streams(ctx) > 0) {
- LOG("(%p) API misuse, %d streams active when context destroyed!", ctx,
- audiounit_active_streams(ctx));
- }
-
/* Unregister the callback if necessary. */
- if (ctx->input_collection_changed_callback) {
- audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_INPUT);
- }
- if (ctx->output_collection_changed_callback) {
- audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_OUTPUT);
+ if(ctx->collection_changed_callback) {
+ audiounit_remove_device_listener(ctx);
}
}
- dispatch_release(ctx->serial_queue);
-
- delete ctx;
+ ctx->~cubeb();
+ free(ctx);
}
-static void
-audiounit_stream_destroy(cubeb_stream * stm);
+static void audiounit_stream_destroy(cubeb_stream * stm);
static int
audio_stream_desc_init(AudioStreamBasicDescription * ss,
@@ -1470,8 +1075,8 @@ audio_stream_desc_init(AudioStreamBasicDescription * ss,
break;
case CUBEB_SAMPLE_S16BE:
ss->mBitsPerChannel = 16;
- ss->mFormatFlags =
- kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian;
+ ss->mFormatFlags = kAudioFormatFlagIsSignedInteger |
+ kAudioFormatFlagIsBigEndian;
break;
case CUBEB_SAMPLE_FLOAT32LE:
ss->mBitsPerChannel = 32;
@@ -1479,7 +1084,8 @@ audio_stream_desc_init(AudioStreamBasicDescription * ss,
break;
case CUBEB_SAMPLE_FLOAT32BE:
ss->mBitsPerChannel = 32;
- ss->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsBigEndian;
+ ss->mFormatFlags = kAudioFormatFlagIsFloat |
+ kAudioFormatFlagIsBigEndian;
break;
default:
return CUBEB_ERROR_INVALID_FORMAT;
@@ -1499,543 +1105,29 @@ audio_stream_desc_init(AudioStreamBasicDescription * ss,
return CUBEB_OK;
}
-void
-audiounit_init_mixer(cubeb_stream * stm)
-{
- // We can't rely on macOS' AudioUnit to properly downmix (or upmix) the audio
- // data, it silently drop the channels so we need to remix the
- // audio data by ourselves to keep all the information.
- stm->mixer.reset(cubeb_mixer_create(
- stm->output_stream_params.format, stm->output_stream_params.channels,
- stm->output_stream_params.layout, stm->context->channels,
- stm->context->layout));
- assert(stm->mixer);
-}
-
-static int
-audiounit_set_channel_layout(AudioUnit unit, io_side side,
- cubeb_channel_layout layout)
-{
- if (side != io_side::OUTPUT) {
- return CUBEB_ERROR;
- }
-
- if (layout == CUBEB_LAYOUT_UNDEFINED) {
- // We leave everything as-is...
- return CUBEB_OK;
- }
-
- OSStatus r;
- uint32_t nb_channels = cubeb_channel_layout_nb_channels(layout);
-
- // We do not use CoreAudio standard layout for lack of documentation on what
- // the actual channel orders are. So we set a custom layout.
- size_t size = offsetof(AudioChannelLayout, mChannelDescriptions[nb_channels]);
- auto au_layout = make_sized_audio_channel_layout(size);
- au_layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
- au_layout->mNumberChannelDescriptions = nb_channels;
-
- uint32_t channels = 0;
- cubeb_channel_layout channelMap = layout;
- for (uint32_t i = 0; channelMap != 0; ++i) {
- XASSERT(channels < nb_channels);
- uint32_t channel = (channelMap & 1) << i;
- if (channel != 0) {
- au_layout->mChannelDescriptions[channels].mChannelLabel =
- cubeb_channel_to_channel_label(static_cast<cubeb_channel>(channel));
- au_layout->mChannelDescriptions[channels].mChannelFlags =
- kAudioChannelFlags_AllOff;
- channels++;
- }
- channelMap = channelMap >> 1;
- }
-
- r = AudioUnitSetProperty(unit, kAudioUnitProperty_AudioChannelLayout,
- kAudioUnitScope_Input, AU_OUT_BUS, au_layout.get(),
- size);
- if (r != noErr) {
- LOG("AudioUnitSetProperty/%s/kAudioUnitProperty_AudioChannelLayout rv=%d",
- to_string(side), r);
- return CUBEB_ERROR;
- }
-
- return CUBEB_OK;
-}
-
-void
-audiounit_layout_init(cubeb_stream * stm, io_side side)
-{
- // We currently don't support the input layout setting.
- if (side == io_side::INPUT) {
- return;
- }
-
- stm->context->layout = audiounit_get_current_channel_layout(stm->output_unit);
-
- audiounit_set_channel_layout(stm->output_unit, io_side::OUTPUT,
- stm->context->layout);
-}
-
-static vector<AudioObjectID>
-audiounit_get_sub_devices(AudioDeviceID device_id)
-{
- vector<AudioDeviceID> sub_devices;
- AudioObjectPropertyAddress property_address = {
- kAudioAggregateDevicePropertyActiveSubDeviceList,
- kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
- UInt32 size = 0;
- OSStatus rv = AudioObjectGetPropertyDataSize(device_id, &property_address, 0,
- nullptr, &size);
-
- if (rv != noErr) {
- sub_devices.push_back(device_id);
- return sub_devices;
- }
-
- uint32_t count = static_cast<uint32_t>(size / sizeof(AudioObjectID));
- sub_devices.resize(count);
- rv = AudioObjectGetPropertyData(device_id, &property_address, 0, nullptr,
- &size, sub_devices.data());
- if (rv != noErr) {
- sub_devices.clear();
- sub_devices.push_back(device_id);
- } else {
- LOG("Found %u sub-devices", count);
- }
- return sub_devices;
-}
-
-static int
-audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id,
- AudioDeviceID * aggregate_device_id)
-{
- AudioObjectPropertyAddress address_plugin_bundle_id = {
- kAudioHardwarePropertyPlugInForBundleID, kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
- UInt32 size = 0;
- OSStatus r = AudioObjectGetPropertyDataSize(
- kAudioObjectSystemObject, &address_plugin_bundle_id, 0, NULL, &size);
- if (r != noErr) {
- LOG("AudioObjectGetPropertyDataSize/"
- "kAudioHardwarePropertyPlugInForBundleID, rv=%d",
- r);
- return CUBEB_ERROR;
- }
-
- AudioValueTranslation translation_value;
- CFStringRef in_bundle_ref = CFSTR("com.apple.audio.CoreAudio");
- translation_value.mInputData = &in_bundle_ref;
- translation_value.mInputDataSize = sizeof(in_bundle_ref);
- translation_value.mOutputData = plugin_id;
- translation_value.mOutputDataSize = sizeof(*plugin_id);
-
- r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
- &address_plugin_bundle_id, 0, nullptr, &size,
- &translation_value);
- if (r != noErr) {
- LOG("AudioObjectGetPropertyData/kAudioHardwarePropertyPlugInForBundleID, "
- "rv=%d",
- r);
- return CUBEB_ERROR;
- }
-
- AudioObjectPropertyAddress create_aggregate_device_address = {
- kAudioPlugInCreateAggregateDevice, kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
- r = AudioObjectGetPropertyDataSize(
- *plugin_id, &create_aggregate_device_address, 0, nullptr, &size);
- if (r != noErr) {
- LOG("AudioObjectGetPropertyDataSize/kAudioPlugInCreateAggregateDevice, "
- "rv=%d",
- r);
- return CUBEB_ERROR;
- }
-
- CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable(
- kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
- &kCFTypeDictionaryValueCallBacks);
- struct timeval timestamp;
- gettimeofday(&timestamp, NULL);
- long long int time_id = timestamp.tv_sec * 1000000LL + timestamp.tv_usec;
- CFStringRef aggregate_device_name = CFStringCreateWithFormat(
- NULL, NULL, CFSTR("%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
- CFDictionaryAddValue(aggregate_device_dict,
- CFSTR(kAudioAggregateDeviceNameKey),
- aggregate_device_name);
- CFRelease(aggregate_device_name);
-
- CFStringRef aggregate_device_UID =
- CFStringCreateWithFormat(NULL, NULL, CFSTR("org.mozilla.%s_%llx"),
- PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
- CFDictionaryAddValue(aggregate_device_dict,
- CFSTR(kAudioAggregateDeviceUIDKey),
- aggregate_device_UID);
- CFRelease(aggregate_device_UID);
-
- int private_value = 1;
- CFNumberRef aggregate_device_private_key =
- CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &private_value);
- CFDictionaryAddValue(aggregate_device_dict,
- CFSTR(kAudioAggregateDeviceIsPrivateKey),
- aggregate_device_private_key);
- CFRelease(aggregate_device_private_key);
-
- int stacked_value = 0;
- CFNumberRef aggregate_device_stacked_key =
- CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &stacked_value);
- CFDictionaryAddValue(aggregate_device_dict,
- CFSTR(kAudioAggregateDeviceIsStackedKey),
- aggregate_device_stacked_key);
- CFRelease(aggregate_device_stacked_key);
-
- r = AudioObjectGetPropertyData(*plugin_id, &create_aggregate_device_address,
- sizeof(aggregate_device_dict),
- &aggregate_device_dict, &size,
- aggregate_device_id);
- CFRelease(aggregate_device_dict);
- if (r != noErr) {
- LOG("AudioObjectGetPropertyData/kAudioPlugInCreateAggregateDevice, rv=%d",
- r);
- return CUBEB_ERROR;
- }
- LOG("New aggregate device %u", *aggregate_device_id);
-
- return CUBEB_OK;
-}
-
-// The returned CFStringRef object needs to be released (via CFRelease)
-// if it's not NULL, since the reference count of the returned CFStringRef
-// object is increased.
-static CFStringRef
-get_device_name(AudioDeviceID id)
-{
- UInt32 size = sizeof(CFStringRef);
- CFStringRef UIname = nullptr;
- AudioObjectPropertyAddress address_uuid = {kAudioDevicePropertyDeviceUID,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
- OSStatus err =
- AudioObjectGetPropertyData(id, &address_uuid, 0, nullptr, &size, &UIname);
- return (err == noErr) ? UIname : NULL;
-}
-
-static int
-audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id,
- AudioDeviceID input_device_id,
- AudioDeviceID output_device_id)
-{
- LOG("Add devices input %u and output %u into aggregate device %u",
- input_device_id, output_device_id, aggregate_device_id);
- const vector<AudioDeviceID> output_sub_devices =
- audiounit_get_sub_devices(output_device_id);
- const vector<AudioDeviceID> input_sub_devices =
- audiounit_get_sub_devices(input_device_id);
-
- CFMutableArrayRef aggregate_sub_devices_array =
- CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
- /* The order of the items in the array is significant and is used to determine
- the order of the streams of the AudioAggregateDevice. */
- for (UInt32 i = 0; i < output_sub_devices.size(); i++) {
- CFStringRef ref = get_device_name(output_sub_devices[i]);
- if (ref == NULL) {
- CFRelease(aggregate_sub_devices_array);
- return CUBEB_ERROR;
- }
- CFArrayAppendValue(aggregate_sub_devices_array, ref);
- CFRelease(ref);
- }
- for (UInt32 i = 0; i < input_sub_devices.size(); i++) {
- CFStringRef ref = get_device_name(input_sub_devices[i]);
- if (ref == NULL) {
- CFRelease(aggregate_sub_devices_array);
- return CUBEB_ERROR;
- }
- CFArrayAppendValue(aggregate_sub_devices_array, ref);
- CFRelease(ref);
- }
-
- AudioObjectPropertyAddress aggregate_sub_device_list = {
- kAudioAggregateDevicePropertyFullSubDeviceList,
- kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
- UInt32 size = sizeof(CFMutableArrayRef);
- OSStatus rv = AudioObjectSetPropertyData(
- aggregate_device_id, &aggregate_sub_device_list, 0, nullptr, size,
- &aggregate_sub_devices_array);
- CFRelease(aggregate_sub_devices_array);
- if (rv != noErr) {
- LOG("AudioObjectSetPropertyData/"
- "kAudioAggregateDevicePropertyFullSubDeviceList, rv=%d",
- rv);
- return CUBEB_ERROR;
- }
-
- return CUBEB_OK;
-}
-
-static int
-audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id)
-{
- assert(aggregate_device_id != kAudioObjectUnknown);
- AudioObjectPropertyAddress master_aggregate_sub_device = {
- kAudioAggregateDevicePropertyMasterSubDevice,
- kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
-
- // Master become the 1st output sub device
- AudioDeviceID output_device_id =
- audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
- const vector<AudioDeviceID> output_sub_devices =
- audiounit_get_sub_devices(output_device_id);
- CFStringRef master_sub_device = get_device_name(output_sub_devices[0]);
-
- UInt32 size = sizeof(CFStringRef);
- OSStatus rv = AudioObjectSetPropertyData(aggregate_device_id,
- &master_aggregate_sub_device, 0,
- NULL, size, &master_sub_device);
- if (master_sub_device) {
- CFRelease(master_sub_device);
- }
- if (rv != noErr) {
- LOG("AudioObjectSetPropertyData/"
- "kAudioAggregateDevicePropertyMasterSubDevice, rv=%d",
- rv);
- return CUBEB_ERROR;
- }
-
- return CUBEB_OK;
-}
-
-static int
-audiounit_activate_clock_drift_compensation(
- const AudioDeviceID aggregate_device_id)
-{
- assert(aggregate_device_id != kAudioObjectUnknown);
- AudioObjectPropertyAddress address_owned = {
- kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
-
- UInt32 qualifier_data_size = sizeof(AudioObjectID);
- AudioClassID class_id = kAudioSubDeviceClassID;
- void * qualifier_data = &class_id;
- UInt32 size = 0;
- OSStatus rv = AudioObjectGetPropertyDataSize(
- aggregate_device_id, &address_owned, qualifier_data_size, qualifier_data,
- &size);
- if (rv != noErr) {
- LOG("AudioObjectGetPropertyDataSize/kAudioObjectPropertyOwnedObjects, "
- "rv=%d",
- rv);
- return CUBEB_ERROR;
- }
-
- UInt32 subdevices_num = 0;
- subdevices_num = size / sizeof(AudioObjectID);
- AudioObjectID sub_devices[subdevices_num];
- size = sizeof(sub_devices);
-
- rv = AudioObjectGetPropertyData(aggregate_device_id, &address_owned,
- qualifier_data_size, qualifier_data, &size,
- sub_devices);
- if (rv != noErr) {
- LOG("AudioObjectGetPropertyData/kAudioObjectPropertyOwnedObjects, rv=%d",
- rv);
- return CUBEB_ERROR;
- }
-
- AudioObjectPropertyAddress address_drift = {
- kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
-
- // Start from the second device since the first is the master clock
- for (UInt32 i = 1; i < subdevices_num; ++i) {
- UInt32 drift_compensation_value = 1;
- rv = AudioObjectSetPropertyData(sub_devices[i], &address_drift, 0, nullptr,
- sizeof(UInt32), &drift_compensation_value);
- if (rv != noErr) {
- LOG("AudioObjectSetPropertyData/"
- "kAudioSubDevicePropertyDriftCompensation, rv=%d",
- rv);
- return CUBEB_OK;
- }
- }
- return CUBEB_OK;
-}
-
-static int
-audiounit_destroy_aggregate_device(AudioObjectID plugin_id,
- AudioDeviceID * aggregate_device_id);
-static void
-audiounit_get_available_samplerate(AudioObjectID devid,
- AudioObjectPropertyScope scope,
- uint32_t * min, uint32_t * max,
- uint32_t * def);
-static int
-audiounit_create_device_from_hwdev(cubeb_device_info * dev_info,
- AudioObjectID devid, cubeb_device_type type);
-static void
-audiounit_device_destroy(cubeb_device_info * device);
-
-static void
-audiounit_workaround_for_airpod(cubeb_stream * stm)
-{
- cubeb_device_info input_device_info;
- audiounit_create_device_from_hwdev(&input_device_info, stm->input_device.id,
- CUBEB_DEVICE_TYPE_INPUT);
-
- cubeb_device_info output_device_info;
- audiounit_create_device_from_hwdev(&output_device_info, stm->output_device.id,
- CUBEB_DEVICE_TYPE_OUTPUT);
-
- std::string input_name_str(input_device_info.friendly_name);
- std::string output_name_str(output_device_info.friendly_name);
-
- if (input_name_str.find("AirPods") != std::string::npos &&
- output_name_str.find("AirPods") != std::string::npos) {
- uint32_t input_min_rate = 0;
- uint32_t input_max_rate = 0;
- uint32_t input_nominal_rate = 0;
- audiounit_get_available_samplerate(
- stm->input_device.id, kAudioObjectPropertyScopeGlobal, &input_min_rate,
- &input_max_rate, &input_nominal_rate);
- LOG("(%p) Input device %u, name: %s, min: %u, max: %u, nominal rate: %u",
- stm, stm->input_device.id, input_device_info.friendly_name,
- input_min_rate, input_max_rate, input_nominal_rate);
- uint32_t output_min_rate = 0;
- uint32_t output_max_rate = 0;
- uint32_t output_nominal_rate = 0;
- audiounit_get_available_samplerate(
- stm->output_device.id, kAudioObjectPropertyScopeGlobal,
- &output_min_rate, &output_max_rate, &output_nominal_rate);
- LOG("(%p) Output device %u, name: %s, min: %u, max: %u, nominal rate: %u",
- stm, stm->output_device.id, output_device_info.friendly_name,
- output_min_rate, output_max_rate, output_nominal_rate);
-
- Float64 rate = input_nominal_rate;
- AudioObjectPropertyAddress addr = {kAudioDevicePropertyNominalSampleRate,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
-
- OSStatus rv = AudioObjectSetPropertyData(stm->aggregate_device_id, &addr, 0,
- nullptr, sizeof(Float64), &rate);
- if (rv != noErr) {
- LOG("Non fatal error, "
- "AudioObjectSetPropertyData/kAudioDevicePropertyNominalSampleRate, "
- "rv=%d",
- rv);
- }
- }
- audiounit_device_destroy(&input_device_info);
- audiounit_device_destroy(&output_device_info);
-}
-
-/*
- * Aggregate Device is a virtual audio interface which utilizes inputs and
- * outputs of one or more physical audio interfaces. It is possible to use the
- * clock of one of the devices as a master clock for all the combined devices
- * and enable drift compensation for the devices that are not designated clock
- * master.
- *
- * Creating a new aggregate device programmatically requires [0][1]:
- * 1. Locate the base plug-in ("com.apple.audio.CoreAudio")
- * 2. Create a dictionary that describes the aggregate device
- * (don't add sub-devices in that step, prone to fail [0])
- * 3. Ask the base plug-in to create the aggregate device (blank)
- * 4. Add the array of sub-devices.
- * 5. Set the master device (1st output device in our case)
- * 6. Enable drift compensation for the non-master devices
- *
- * [0] https://lists.apple.com/archives/coreaudio-api/2006/Apr/msg00092.html
- * [1] https://lists.apple.com/archives/coreaudio-api/2005/Jul/msg00150.html
- * [2] CoreAudio.framework/Headers/AudioHardware.h
- * */
-static int
-audiounit_create_aggregate_device(cubeb_stream * stm)
-{
- int r = audiounit_create_blank_aggregate_device(&stm->plugin_id,
- &stm->aggregate_device_id);
- if (r != CUBEB_OK) {
- LOG("(%p) Failed to create blank aggregate device", stm);
- return CUBEB_ERROR;
- }
-
- r = audiounit_set_aggregate_sub_device_list(
- stm->aggregate_device_id, stm->input_device.id, stm->output_device.id);
- if (r != CUBEB_OK) {
- LOG("(%p) Failed to set aggregate sub-device list", stm);
- audiounit_destroy_aggregate_device(stm->plugin_id,
- &stm->aggregate_device_id);
- return CUBEB_ERROR;
- }
-
- r = audiounit_set_master_aggregate_device(stm->aggregate_device_id);
- if (r != CUBEB_OK) {
- LOG("(%p) Failed to set master sub-device for aggregate device", stm);
- audiounit_destroy_aggregate_device(stm->plugin_id,
- &stm->aggregate_device_id);
- return CUBEB_ERROR;
- }
-
- r = audiounit_activate_clock_drift_compensation(stm->aggregate_device_id);
- if (r != CUBEB_OK) {
- LOG("(%p) Failed to activate clock drift compensation for aggregate device",
- stm);
- audiounit_destroy_aggregate_device(stm->plugin_id,
- &stm->aggregate_device_id);
- return CUBEB_ERROR;
- }
-
- audiounit_workaround_for_airpod(stm);
-
- return CUBEB_OK;
-}
-
-static int
-audiounit_destroy_aggregate_device(AudioObjectID plugin_id,
- AudioDeviceID * aggregate_device_id)
-{
- assert(aggregate_device_id && *aggregate_device_id != kAudioDeviceUnknown &&
- plugin_id != kAudioObjectUnknown);
- AudioObjectPropertyAddress destroy_aggregate_device_addr = {
- kAudioPlugInDestroyAggregateDevice, kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMaster};
- UInt32 size;
- OSStatus rv = AudioObjectGetPropertyDataSize(
- plugin_id, &destroy_aggregate_device_addr, 0, NULL, &size);
- if (rv != noErr) {
- LOG("AudioObjectGetPropertyDataSize/kAudioPlugInDestroyAggregateDevice, "
- "rv=%d",
- rv);
- return CUBEB_ERROR;
- }
-
- rv = AudioObjectGetPropertyData(plugin_id, &destroy_aggregate_device_addr, 0,
- NULL, &size, aggregate_device_id);
- if (rv != noErr) {
- LOG("AudioObjectGetPropertyData/kAudioPlugInDestroyAggregateDevice, rv=%d",
- rv);
- return CUBEB_ERROR;
- }
-
- LOG("Destroyed aggregate device %d", *aggregate_device_id);
- *aggregate_device_id = kAudioObjectUnknown;
- return CUBEB_OK;
-}
-
static int
-audiounit_new_unit_instance(AudioUnit * unit, device_info * device)
+audiounit_create_unit(AudioUnit * unit,
+ bool is_input,
+ const cubeb_stream_params * /* stream_params */,
+ cubeb_devid device)
{
AudioComponentDescription desc;
AudioComponent comp;
+ UInt32 enable;
+ AudioDeviceID devid;
OSStatus rv;
desc.componentType = kAudioUnitType_Output;
#if TARGET_OS_IPHONE
+ bool use_default_output = false;
desc.componentSubType = kAudioUnitSubType_RemoteIO;
#else
// Use the DefaultOutputUnit for output when no device is specified
// so we retain automatic output device switching when the default
// changes. Once we have complete support for device notifications
// and switching, we can use the AUHAL for everything.
- if ((device->flags & DEV_SYSTEM_DEFAULT) && (device->flags & DEV_OUTPUT)) {
+ bool use_default_output = device == NULL && !is_input;
+ if (use_default_output) {
desc.componentSubType = kAudioUnitSubType_DefaultOutput;
} else {
desc.componentSubType = kAudioUnitSubType_HALOutput;
@@ -2052,86 +1144,43 @@ audiounit_new_unit_instance(AudioUnit * unit, device_info * device)
rv = AudioComponentInstanceNew(comp, unit);
if (rv != noErr) {
- LOG("AudioComponentInstanceNew rv=%d", rv);
+ PRINT_ERROR_CODE("AudioComponentInstanceNew", rv);
return CUBEB_ERROR;
}
- return CUBEB_OK;
-}
-enum enable_state {
- DISABLE,
- ENABLE,
-};
-
-static int
-audiounit_enable_unit_scope(AudioUnit * unit, io_side side, enable_state state)
-{
- OSStatus rv;
- UInt32 enable = state;
- rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
- (side == io_side::INPUT) ? kAudioUnitScope_Input
- : kAudioUnitScope_Output,
- (side == io_side::INPUT) ? AU_IN_BUS : AU_OUT_BUS,
- &enable, sizeof(UInt32));
- if (rv != noErr) {
- LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO rv=%d", rv);
- return CUBEB_ERROR;
- }
- return CUBEB_OK;
-}
-
-static int
-audiounit_create_unit(AudioUnit * unit, device_info * device)
-{
- assert(*unit == nullptr);
- assert(device);
-
- OSStatus rv;
- int r;
-
- r = audiounit_new_unit_instance(unit, device);
- if (r != CUBEB_OK) {
- return r;
- }
- assert(*unit);
-
- if ((device->flags & DEV_SYSTEM_DEFAULT) && (device->flags & DEV_OUTPUT)) {
- return CUBEB_OK;
- }
-
- if (device->flags & DEV_INPUT) {
- r = audiounit_enable_unit_scope(unit, io_side::INPUT, ENABLE);
- if (r != CUBEB_OK) {
- LOG("Failed to enable audiounit input scope");
- return r;
+ if (!use_default_output) {
+ enable = 1;
+ rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
+ is_input ? kAudioUnitScope_Input : kAudioUnitScope_Output,
+ is_input ? AU_IN_BUS : AU_OUT_BUS, &enable, sizeof(UInt32));
+ if (rv != noErr) {
+ PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO", rv);
+ return CUBEB_ERROR;
}
- r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, DISABLE);
- if (r != CUBEB_OK) {
- LOG("Failed to disable audiounit output scope");
- return r;
+
+ enable = 0;
+ rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
+ is_input ? kAudioUnitScope_Output : kAudioUnitScope_Input,
+ is_input ? AU_OUT_BUS : AU_IN_BUS, &enable, sizeof(UInt32));
+ if (rv != noErr) {
+ PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO", rv);
+ return CUBEB_ERROR;
}
- } else if (device->flags & DEV_OUTPUT) {
- r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, ENABLE);
- if (r != CUBEB_OK) {
- LOG("Failed to enable audiounit output scope");
- return r;
+
+ if (device == NULL) {
+ assert(is_input);
+ devid = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_INPUT);
+ } else {
+ devid = reinterpret_cast<intptr_t>(device);
}
- r = audiounit_enable_unit_scope(unit, io_side::INPUT, DISABLE);
- if (r != CUBEB_OK) {
- LOG("Failed to disable audiounit input scope");
- return r;
+ rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice,
+ kAudioUnitScope_Global,
+ is_input ? AU_IN_BUS : AU_OUT_BUS,
+ &devid, sizeof(AudioDeviceID));
+ if (rv != noErr) {
+ PRINT_ERROR_CODE("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice", rv);
+ return CUBEB_ERROR;
}
- } else {
- assert(false);
- }
-
- rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice,
- kAudioUnitScope_Global, 0, &device->id,
- sizeof(AudioDeviceID));
- if (rv != noErr) {
- LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice rv=%d",
- rv);
- return CUBEB_ERROR;
}
return CUBEB_OK;
@@ -2140,28 +1189,51 @@ audiounit_create_unit(AudioUnit * unit, device_info * device)
static int
audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity)
{
- uint32_t size =
- capacity * stream->latency_frames * stream->input_desc.mChannelsPerFrame;
if (stream->input_desc.mFormatFlags & kAudioFormatFlagIsSignedInteger) {
- stream->input_linear_buffer.reset(new auto_array_wrapper_impl<short>(size));
+ stream->input_linear_buffer = new auto_array_wrapper(
+ new auto_array<short>(capacity *
+ stream->input_buffer_frames *
+ stream->input_desc.mChannelsPerFrame) );
} else {
- stream->input_linear_buffer.reset(new auto_array_wrapper_impl<float>(size));
+ stream->input_linear_buffer = new auto_array_wrapper(
+ new auto_array<float>(capacity *
+ stream->input_buffer_frames *
+ stream->input_desc.mChannelsPerFrame) );
}
+
+ if (!stream->input_linear_buffer) {
+ return CUBEB_ERROR;
+ }
+
assert(stream->input_linear_buffer->length() == 0);
+ // Pre-buffer silence if needed
+ if (capacity != 1) {
+ size_t silence_size = stream->input_buffer_frames *
+ stream->input_desc.mChannelsPerFrame;
+ stream->input_linear_buffer->push_silence(silence_size);
+
+ assert(stream->input_linear_buffer->length() == silence_size);
+ }
+
return CUBEB_OK;
}
+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)
{
// For the 1st stream set anything within safe min-max
- assert(audiounit_active_streams(stm->context) > 0);
- if (audiounit_active_streams(stm->context) == 1) {
- return max(min<uint32_t>(latency_frames, SAFE_MAX_LATENCY_FRAMES),
- SAFE_MIN_LATENCY_FRAMES);
+ 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);
}
- assert(stm->output_unit);
// If more than one stream operates in parallel
// allow only lower values of latency
@@ -2169,42 +1241,42 @@ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
UInt32 output_buffer_size = 0;
UInt32 size = sizeof(output_buffer_size);
if (stm->output_unit) {
- r = AudioUnitGetProperty(
- stm->output_unit, kAudioDevicePropertyBufferFrameSize,
- kAudioUnitScope_Output, AU_OUT_BUS, &output_buffer_size, &size);
+ r = AudioUnitGetProperty(stm->output_unit,
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Output,
+ AU_OUT_BUS,
+ &output_buffer_size,
+ &size);
if (r != noErr) {
- LOG("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize "
- "rv=%d",
- r);
+ PRINT_ERROR_CODE("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize", r);
return 0;
}
- output_buffer_size =
- max(min<uint32_t>(output_buffer_size, SAFE_MAX_LATENCY_FRAMES),
- SAFE_MIN_LATENCY_FRAMES);
+ output_buffer_size = std::max(std::min<uint32_t>(output_buffer_size, SAFE_MAX_LATENCY_FRAMES),
+ SAFE_MIN_LATENCY_FRAMES);
}
UInt32 input_buffer_size = 0;
if (stm->input_unit) {
- r = AudioUnitGetProperty(
- stm->input_unit, kAudioDevicePropertyBufferFrameSize,
- kAudioUnitScope_Input, AU_IN_BUS, &input_buffer_size, &size);
+ r = AudioUnitGetProperty(stm->input_unit,
+ kAudioDevicePropertyBufferFrameSize,
+ kAudioUnitScope_Input,
+ AU_IN_BUS,
+ &input_buffer_size,
+ &size);
if (r != noErr) {
- LOG("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize "
- "rv=%d",
- r);
+ PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize", r);
return 0;
}
- input_buffer_size =
- max(min<uint32_t>(input_buffer_size, SAFE_MAX_LATENCY_FRAMES),
- SAFE_MIN_LATENCY_FRAMES);
+ input_buffer_size = std::max(std::min<uint32_t>(input_buffer_size, SAFE_MAX_LATENCY_FRAMES),
+ SAFE_MIN_LATENCY_FRAMES);
}
// Every following active streams can only set smaller latency
UInt32 upper_latency_limit = 0;
if (input_buffer_size != 0 && output_buffer_size != 0) {
- upper_latency_limit = min<uint32_t>(input_buffer_size, output_buffer_size);
+ upper_latency_limit = std::min<uint32_t>(input_buffer_size, output_buffer_size);
} else if (input_buffer_size != 0) {
upper_latency_limit = input_buffer_size;
} else if (output_buffer_size != 0) {
@@ -2213,8 +1285,8 @@ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
upper_latency_limit = SAFE_MAX_LATENCY_FRAMES;
}
- return max(min<uint32_t>(latency_frames, upper_latency_limit),
- SAFE_MIN_LATENCY_FRAMES);
+ return std::max(std::min<uint32_t>(latency_frames, upper_latency_limit),
+ SAFE_MIN_LATENCY_FRAMES);
}
/*
@@ -2226,103 +1298,130 @@ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
* - property has changed, remove the listener
* */
static void
-buffer_size_changed_callback(void * inClientData, AudioUnit inUnit,
- AudioUnitPropertyID inPropertyID,
- AudioUnitScope inScope, AudioUnitElement inElement)
+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;
- char const * au_type = "output";
+ const char * au_type = "output";
- if (AU_IN_BUS == inElement) {
+ if (au == stm->input_unit) {
au_scope = kAudioUnitScope_Output;
au_type = "input";
}
switch (inPropertyID) {
- case kAudioDevicePropertyBufferFrameSize: {
- if (inScope != au_scope) {
+ 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;
}
- 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
-audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames,
- io_side side)
+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 (side == io_side::INPUT) {
+ 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);
+ int r = AudioUnitGetProperty(au,
+ kAudioDevicePropertyBufferFrameSize,
+ au_scope,
+ au_element,
+ &buffer_frames,
+ &size);
if (r != noErr) {
- LOG("AudioUnitGetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d",
- to_string(side), r);
+ 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,
- to_string(side), 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);
+ r = AudioUnitAddPropertyListener(au,
+ kAudioDevicePropertyBufferFrameSize,
+ buffer_size_changed_callback,
+ stm);
if (r != noErr) {
- LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize "
- "rv=%d",
- to_string(side), r);
+ 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,
+ r = AudioUnitSetProperty(au,
+ kAudioDevicePropertyBufferFrameSize,
+ au_scope,
+ au_element,
+ &new_size_frames,
sizeof(new_size_frames));
if (r != noErr) {
- LOG("AudioUnitSetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d",
- to_string(side), r);
+ 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);
+ r = AudioUnitRemovePropertyListenerWithUserData(au,
+ kAudioDevicePropertyBufferFrameSize,
+ buffer_size_changed_callback,
+ stm);
if (r != noErr) {
- LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize "
- "rv=%d",
- to_string(side), r);
+ if (set_side == INPUT) {
+ PRINT_ERROR_CODE("AudioUnitAddPropertyListener/input/kAudioDevicePropertyBufferFrameSize", r);
+ } else {
+ PRINT_ERROR_CODE("AudioUnitAddPropertyListener/output/kAudioDevicePropertyBufferFrameSize", r);
+ }
}
return CUBEB_ERROR;
@@ -2333,21 +1432,22 @@ audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames,
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);
+ 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);
+ r = AudioUnitRemovePropertyListenerWithUserData(au,
+ kAudioDevicePropertyBufferFrameSize,
+ buffer_size_changed_callback,
+ stm);
if (r != noErr) {
- LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize "
- "rv=%d",
- to_string(side), r);
+ if (set_side == INPUT) {
+ PRINT_ERROR_CODE("AudioUnitAddPropertyListener/input/kAudioDevicePropertyBufferFrameSize", r);
+ } else {
+ PRINT_ERROR_CODE("AudioUnitAddPropertyListener/output/kAudioDevicePropertyBufferFrameSize", r);
+ }
return CUBEB_ERROR;
}
@@ -2356,33 +1456,32 @@ audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames,
return CUBEB_ERROR;
}
- LOG("(%p) %s buffer size changed to %u frames.", stm, to_string(side),
- new_size_frames);
+ 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)
{
- assert(stm && stm->input_unit);
-
int r = 0;
UInt32 size;
AURenderCallbackStruct aurcbs_in;
- LOG("(%p) Opening input side: rate %u, channels %u, format %d, latency in "
- "frames %u.",
+ 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,
+ r = AudioUnitGetProperty(stm->input_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ AU_IN_BUS,
+ &input_hw_desc,
&size);
if (r != noErr) {
- LOG("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r);
+ PRINT_ERROR_CODE("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat", r);
return CUBEB_ERROR;
}
stm->input_hw_rate = input_hw_desc.mSampleRate;
@@ -2396,7 +1495,8 @@ audiounit_configure_input(cubeb_stream * stm)
}
// Use latency to set buffer size
- r = audiounit_set_buffer_size(stm, stm->latency_frames, io_side::INPUT);
+ 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;
@@ -2407,22 +1507,26 @@ audiounit_configure_input(cubeb_stream * stm)
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,
+ r = AudioUnitSetProperty(stm->input_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output,
+ AU_IN_BUS,
+ &src_desc,
sizeof(AudioStreamBasicDescription));
if (r != noErr) {
- LOG("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r);
+ 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->latency_frames, sizeof(UInt32));
+ r = AudioUnitSetProperty(stm->input_unit,
+ kAudioUnitProperty_MaximumFramesPerSlice,
+ kAudioUnitScope_Global,
+ AU_IN_BUS,
+ &stm->input_buffer_frames,
+ sizeof(UInt32));
if (r != noErr) {
- LOG("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice "
- "rv=%d",
- r);
+ PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice", r);
return CUBEB_ERROR;
}
@@ -2436,21 +1540,20 @@ audiounit_configure_input(cubeb_stream * stm)
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));
+ r = AudioUnitSetProperty(stm->input_unit,
+ kAudioOutputUnitProperty_SetInputCallback,
+ kAudioUnitScope_Global,
+ AU_OUT_BUS,
+ &aurcbs_in,
+ sizeof(aurcbs_in));
if (r != noErr) {
- LOG("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback "
- "rv=%d",
- r);
+ PRINT_ERROR_CODE("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback", r);
return CUBEB_ERROR;
}
-
- stm->frames_read = 0;
-
LOG("(%p) Input audiounit init successfully.", stm);
return CUBEB_OK;
@@ -2459,14 +1562,12 @@ audiounit_configure_input(cubeb_stream * stm)
static int
audiounit_configure_output(cubeb_stream * stm)
{
- assert(stm && stm->output_unit);
-
int r;
AURenderCallbackStruct aurcbs_out;
UInt32 size;
- LOG("(%p) Opening output side: rate %u, channels %u, format %d, latency in "
- "frames %u.",
+
+ 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);
@@ -2480,75 +1581,62 @@ audiounit_configure_output(cubeb_stream * stm)
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,
+ r = AudioUnitGetProperty(stm->output_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output,
+ AU_OUT_BUS,
+ &output_hw_desc,
&size);
if (r != noErr) {
- LOG("AudioUnitGetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r);
+ 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);
- stm->context->channels = output_hw_desc.mChannelsPerFrame;
-
- // Set the input layout to match the output device layout.
- audiounit_layout_init(stm, io_side::OUTPUT);
- if (stm->context->channels != stm->output_stream_params.channels ||
- stm->context->layout != stm->output_stream_params.layout) {
- LOG("Incompatible channel layouts detected, setting up remixer");
- audiounit_init_mixer(stm);
- // We will be remixing the data before it reaches the output device.
- // We need to adjust the number of channels and other
- // AudioStreamDescription details.
- stm->output_desc.mChannelsPerFrame = stm->context->channels;
- stm->output_desc.mBytesPerFrame = (stm->output_desc.mBitsPerChannel / 8) *
- stm->output_desc.mChannelsPerFrame;
- stm->output_desc.mBytesPerPacket =
- stm->output_desc.mBytesPerFrame * stm->output_desc.mFramesPerPacket;
- } else {
- stm->mixer = nullptr;
- }
+ 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,
+ r = AudioUnitSetProperty(stm->output_unit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ AU_OUT_BUS,
+ &stm->output_desc,
sizeof(AudioStreamBasicDescription));
if (r != noErr) {
- LOG("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r);
+ PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat", r);
return CUBEB_ERROR;
}
- r = audiounit_set_buffer_size(stm, stm->latency_frames, io_side::OUTPUT);
+ 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));
+ r = AudioUnitSetProperty(stm->output_unit,
+ kAudioUnitProperty_MaximumFramesPerSlice,
+ kAudioUnitScope_Global,
+ AU_OUT_BUS,
+ &stm->latency_frames,
+ sizeof(UInt32));
if (r != noErr) {
- LOG("AudioUnitSetProperty/output/kAudioUnitProperty_MaximumFramesPerSlice "
- "rv=%d",
- r);
+ 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));
+ r = AudioUnitSetProperty(stm->output_unit,
+ kAudioUnitProperty_SetRenderCallback,
+ kAudioUnitScope_Global,
+ AU_OUT_BUS,
+ &aurcbs_out,
+ sizeof(aurcbs_out));
if (r != noErr) {
- LOG("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback "
- "rv=%d",
- r);
+ PRINT_ERROR_CODE("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback", r);
return CUBEB_ERROR;
}
- stm->frames_written = 0;
-
LOG("(%p) Output audiounit init successfully.", stm);
return CUBEB_OK;
}
@@ -2558,37 +1646,11 @@ audiounit_setup_stream(cubeb_stream * stm)
{
stm->mutex.assert_current_thread_owns();
- if ((stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) ||
- (stm->output_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK)) {
- LOG("(%p) Loopback not supported for audiounit.", stm);
- return CUBEB_ERROR_NOT_SUPPORTED;
- }
-
int r = 0;
-
- device_info in_dev_info = stm->input_device;
- device_info out_dev_info = stm->output_device;
-
- if (has_input(stm) && has_output(stm) &&
- stm->input_device.id != stm->output_device.id) {
- r = audiounit_create_aggregate_device(stm);
- if (r != CUBEB_OK) {
- stm->aggregate_device_id = kAudioObjectUnknown;
- LOG("(%p) Create aggregate devices failed.", stm);
- // !!!NOTE: It is not necessary to return here. If it does not
- // return it will fallback to the old implementation. The intention
- // is to investigate how often it fails. I plan to remove
- // it after a couple of weeks.
- return r;
- } else {
- in_dev_info.id = out_dev_info.id = stm->aggregate_device_id;
- in_dev_info.flags = DEV_INPUT;
- out_dev_info.flags = DEV_OUTPUT;
- }
- }
-
if (has_input(stm)) {
- r = audiounit_create_unit(&stm->input_unit, &in_dev_info);
+ 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;
@@ -2596,28 +1658,30 @@ audiounit_setup_stream(cubeb_stream * stm)
}
if (has_output(stm)) {
- r = audiounit_create_unit(&stm->output_unit, &out_dev_info);
+ r = audiounit_create_unit(&stm->output_unit, false,
+ &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 latency is set to the other stream value. */
- if (audiounit_active_streams(stm->context) > 1) {
+ /* 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. */
+ * 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); // Ugly error check
- audiounit_set_global_latency(stm->context, stm->latency_frames);
+ assert(stm->latency_frames); // Ungly error check
+ audiounit_set_global_latency(stm, stm->latency_frames);
}
- /* Configure I/O stream */
+ /* Setup Input Stream! */
if (has_input(stm)) {
r = audiounit_configure_input(stm);
if (r != CUBEB_OK) {
@@ -2626,6 +1690,7 @@ audiounit_setup_stream(cubeb_stream * stm)
}
}
+ /* Setup Output Stream! */
if (has_output(stm)) {
r = audiounit_configure_output(stm);
if (r != CUBEB_OK) {
@@ -2670,7 +1735,7 @@ audiounit_setup_stream(cubeb_stream * stm)
return CUBEB_ERROR;
}
}
-#else // TARGET_OS_IPHONE
+#else // TARGET_OS_IPHONE
//TODO: [[AVAudioSession sharedInstance] inputLatency]
// http://stackoverflow.com/questions/13157523/kaudiodevicepropertybufferframesize-replacement-for-ios
#endif
@@ -2697,10 +1762,13 @@ audiounit_setup_stream(cubeb_stream * stm)
/* Create resampler. Output params are unchanged
* because we do not need conversion on the output. */
- stm->resampler.reset(cubeb_resampler_create(
- stm, has_input(stm) ? &input_unconverted_params : NULL,
- has_output(stm) ? &stm->output_stream_params : NULL, target_sample_rate,
- stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP));
+ stm->resampler = cubeb_resampler_create(stm,
+ has_input(stm) ? &input_unconverted_params : NULL,
+ has_output(stm) ? &stm->output_stream_params : NULL,
+ target_sample_rate,
+ stm->data_callback,
+ stm->user_ptr,
+ CUBEB_RESAMPLER_QUALITY_DESKTOP);
if (!stm->resampler) {
LOG("(%p) Could not create resampler.", stm);
return CUBEB_ERROR;
@@ -2709,7 +1777,7 @@ audiounit_setup_stream(cubeb_stream * stm)
if (stm->input_unit != NULL) {
r = AudioUnitInitialize(stm->input_unit);
if (r != noErr) {
- LOG("AudioUnitInitialize/input rv=%d", r);
+ PRINT_ERROR_CODE("AudioUnitInitialize/input", r);
return CUBEB_ERROR;
}
}
@@ -2717,221 +1785,175 @@ audiounit_setup_stream(cubeb_stream * stm)
if (stm->output_unit != NULL) {
r = AudioUnitInitialize(stm->output_unit);
if (r != noErr) {
- LOG("AudioUnitInitialize/output rv=%d", r);
+ PRINT_ERROR_CODE("AudioUnitInitialize/output", r);
return CUBEB_ERROR;
}
-
- stm->current_latency_frames = audiounit_get_device_presentation_latency(
- stm->output_device.id, kAudioDevicePropertyScopeOutput);
-
- Float64 unit_s;
- UInt32 size = sizeof(unit_s);
- if (AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_Latency,
- kAudioUnitScope_Global, 0, &unit_s,
- &size) == noErr) {
- stm->current_latency_frames +=
- static_cast<uint32_t>(unit_s * stm->output_desc.mSampleRate);
- }
}
if (stm->input_unit && stm->output_unit) {
- // According to the I/O hardware rate it is expected a specific pattern of
- // callbacks for example is input is 44100 and output is 48000 we expected
- // no more than 2 out callback in a row.
- stm->expected_output_callbacks_in_a_row =
- ceilf(stm->output_hw_rate / stm->input_hw_rate);
+ // According to the I/O hardware rate it is expected a specific pattern of callbacks
+ // for example is input is 44100 and output is 48000 we expected no more than 2
+ // out callback in a row.
+ stm->expected_output_callbacks_in_a_row = ceilf(stm->output_hw_rate / stm->input_hw_rate);
}
r = audiounit_install_device_changed_callback(stm);
if (r != CUBEB_OK) {
- LOG("(%p) Could not install all device change callback.", stm);
+ LOG("(%p) Could not install the device change callback.", stm);
+ return r;
}
return CUBEB_OK;
}
-cubeb_stream::cubeb_stream(cubeb * context)
- : context(context), resampler(nullptr, cubeb_resampler_destroy),
- mixer(nullptr, cubeb_mixer_destroy)
-{
- PodZero(&input_desc, 1);
- PodZero(&output_desc, 1);
-}
-
-static void
-audiounit_stream_destroy_internal(cubeb_stream * stm);
-
static int
-audiounit_stream_init(cubeb * context, cubeb_stream ** stream,
- char const * /* stream_name */, cubeb_devid input_device,
+audiounit_stream_init(cubeb * context,
+ cubeb_stream ** stream,
+ char const * /* stream_name */,
+ cubeb_devid input_device,
cubeb_stream_params * input_stream_params,
cubeb_devid output_device,
cubeb_stream_params * output_stream_params,
unsigned int latency_frames,
cubeb_data_callback data_callback,
- cubeb_state_callback state_callback, void * user_ptr)
+ cubeb_state_callback state_callback,
+ void * user_ptr)
{
- assert(context);
- auto_lock context_lock(context->mutex);
- audiounit_increment_active_streams(context);
- unique_ptr<cubeb_stream, decltype(&audiounit_stream_destroy)> stm(
- new cubeb_stream(context), audiounit_stream_destroy_internal);
+ 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;
+ }
+
+ 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;
-
- if ((input_device && !input_stream_params) ||
- (output_device && !output_stream_params)) {
- return CUBEB_ERROR_INVALID_PARAMETER;
- }
+ stm->device_changed_callback = NULL;
if (input_stream_params) {
stm->input_stream_params = *input_stream_params;
- r = audiounit_set_device_info(
- stm.get(), reinterpret_cast<uintptr_t>(input_device), io_side::INPUT);
- if (r != CUBEB_OK) {
- LOG("(%p) Fail to set device info for input.", stm.get());
- return r;
- }
+ 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;
- r = audiounit_set_device_info(
- stm.get(), reinterpret_cast<uintptr_t>(output_device), io_side::OUTPUT);
- if (r != CUBEB_OK) {
- LOG("(%p) Fail to set device info for output.", stm.get());
- return r;
- }
+ stm->output_device = output_device;
}
+ /* Init data members where necessary */
+ stm->hw_latency_frames = UINT64_MAX;
+
+ 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
// `audiounit_setup_stream`.
+ context->active_streams += 1;
auto_lock lock(stm->mutex);
- r = audiounit_setup_stream(stm.get());
+ r = audiounit_setup_stream(stm);
}
if (r != CUBEB_OK) {
- LOG("(%p) Could not setup the audiounit stream.", stm.get());
+ LOG("(%p) Could not setup the audiounit stream.", stm);
+ audiounit_stream_destroy(stm);
return r;
}
- r = audiounit_install_system_changed_callback(stm.get());
+ r = audiounit_install_system_changed_callback(stm);
if (r != CUBEB_OK) {
- LOG("(%p) Could not install the device change callback.", stm.get());
+ LOG("(%p) Could not install the device change callback.", stm);
return r;
}
- *stream = stm.release();
- LOG("(%p) Cubeb stream init successful.", *stream);
+ *stream = stm;
+ LOG("Cubeb stream (%p) init successful.", stm);
return CUBEB_OK;
}
static void
-audiounit_close_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);
- stm->input_unit = nullptr;
}
- stm->input_linear_buffer.reset();
+ audiounit_destroy_input_linear_buffer(stm);
if (stm->output_unit) {
AudioUnitUninitialize(stm->output_unit);
AudioComponentInstanceDispose(stm->output_unit);
- stm->output_unit = nullptr;
}
- stm->resampler.reset();
- stm->mixer.reset();
-
- if (stm->aggregate_device_id != kAudioObjectUnknown) {
- audiounit_destroy_aggregate_device(stm->plugin_id,
- &stm->aggregate_device_id);
- stm->aggregate_device_id = kAudioObjectUnknown;
- }
+ cubeb_resampler_destroy(stm->resampler);
}
static void
-audiounit_stream_destroy_internal(cubeb_stream * stm)
+audiounit_stream_destroy(cubeb_stream * stm)
{
- stm->context->mutex.assert_current_thread_owns();
+ stm->shutdown = true;
int r = audiounit_uninstall_system_changed_callback(stm);
if (r != CUBEB_OK) {
LOG("(%p) Could not uninstall the device changed callback", stm);
}
- r = audiounit_uninstall_device_changed_callback(stm);
- if (r != CUBEB_OK) {
- LOG("(%p) Could not uninstall all device change listeners", stm);
- }
- auto_lock lock(stm->mutex);
- audiounit_close_stream(stm);
- assert(audiounit_active_streams(stm->context) >= 1);
- audiounit_decrement_active_streams(stm->context);
-}
-
-static void
-audiounit_stream_destroy(cubeb_stream * stm)
-{
- int r = audiounit_uninstall_system_changed_callback(stm);
- if (r != CUBEB_OK) {
- LOG("(%p) Could not uninstall the device changed callback", stm);
- }
r = audiounit_uninstall_device_changed_callback(stm);
if (r != CUBEB_OK) {
- LOG("(%p) Could not uninstall all device change listeners", stm);
+ LOG("(%p) Could not uninstall the device changed callback", stm);
}
- if (!stm->shutdown.load()) {
- auto_lock context_lock(stm->context->mutex);
- audiounit_stream_stop_internal(stm);
- stm->shutdown = true;
- }
+ auto_lock context_lock(stm->context->mutex);
+ audiounit_stream_stop_internal(stm);
- stm->destroy_pending = true;
// Execute close in serial queue to avoid collision
// with reinit when un/plug devices
dispatch_sync(stm->context->serial_queue, ^() {
- auto_lock context_lock(stm->context->mutex);
- audiounit_stream_destroy_internal(stm);
+ auto_lock lock(stm->mutex);
+ audiounit_close_stream(stm);
});
+ assert(stm->context->active_streams >= 1);
+ stm->context->active_streams -= 1;
+
LOG("Cubeb stream (%p) destroyed successful.", stm);
- delete stm;
+
+ stm->~cubeb_stream();
+ free(stm);
}
-static int
+void
audiounit_stream_start_internal(cubeb_stream * stm)
{
OSStatus r;
if (stm->input_unit != NULL) {
r = AudioOutputUnitStart(stm->input_unit);
- if (r != noErr) {
- LOG("AudioOutputUnitStart (input) rv=%d", r);
- return CUBEB_ERROR;
- }
+ assert(r == 0);
}
if (stm->output_unit != NULL) {
r = AudioOutputUnitStart(stm->output_unit);
- if (r != noErr) {
- LOG("AudioOutputUnitStart (output) rv=%d", r);
- return CUBEB_ERROR;
- }
+ assert(r == 0);
}
- return CUBEB_OK;
}
static int
@@ -2941,10 +1963,7 @@ audiounit_stream_start(cubeb_stream * stm)
stm->shutdown = false;
stm->draining = false;
- int r = audiounit_stream_start_internal(stm);
- if (r != CUBEB_OK) {
- return r;
- }
+ audiounit_stream_start_internal(stm);
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
@@ -2983,12 +2002,9 @@ audiounit_stream_stop(cubeb_stream * stm)
static int
audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
{
- assert(stm);
- if (stm->current_latency_frames > stm->frames_played) {
- *position = 0;
- } else {
- *position = stm->frames_played - stm->current_latency_frames;
- }
+ auto_lock lock(stm->mutex);
+
+ *position = stm->frames_played;
return CUBEB_OK;
}
@@ -2996,10 +2012,77 @@ int
audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
{
#if TARGET_OS_IPHONE
- // TODO
+ //TODO
return CUBEB_ERROR_NOT_SUPPORTED;
#else
- *latency = stm->total_output_latency_frames;
+ auto_lock lock(stm->mutex);
+ if (stm->hw_latency_frames == UINT64_MAX) {
+ UInt32 size;
+ uint32_t device_latency_frames, device_safety_offset;
+ double unit_latency_sec;
+ AudioDeviceID output_device_id;
+ OSStatus r;
+ AudioObjectPropertyAddress latency_address = {
+ kAudioDevicePropertyLatency,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
+ AudioObjectPropertyAddress safety_offset_address = {
+ kAudioDevicePropertySafetyOffset,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
+
+ r = audiounit_get_output_device_id(&output_device_id);
+ if (r != noErr) {
+ return CUBEB_ERROR;
+ }
+
+ size = sizeof(unit_latency_sec);
+ r = AudioUnitGetProperty(stm->output_unit,
+ kAudioUnitProperty_Latency,
+ kAudioUnitScope_Global,
+ 0,
+ &unit_latency_sec,
+ &size);
+ if (r != noErr) {
+ PRINT_ERROR_CODE("AudioUnitGetProperty/kAudioUnitProperty_Latency", r);
+ return CUBEB_ERROR;
+ }
+
+ size = sizeof(device_latency_frames);
+ r = AudioObjectGetPropertyData(output_device_id,
+ &latency_address,
+ 0,
+ NULL,
+ &size,
+ &device_latency_frames);
+ if (r != noErr) {
+ PRINT_ERROR_CODE("AudioUnitGetPropertyData/latency_frames", r);
+ return CUBEB_ERROR;
+ }
+
+ size = sizeof(device_safety_offset);
+ r = AudioObjectGetPropertyData(output_device_id,
+ &safety_offset_address,
+ 0,
+ NULL,
+ &size,
+ &device_safety_offset);
+ if (r != noErr) {
+ PRINT_ERROR_CODE("AudioUnitGetPropertyData/safety_offset", r);
+ return CUBEB_ERROR;
+ }
+
+ /* This part is fixed and depend on the stream parameter and the hardware. */
+ stm->hw_latency_frames =
+ static_cast<uint32_t>(unit_latency_sec * stm->output_desc.mSampleRate)
+ + device_latency_frames
+ + device_safety_offset;
+ }
+
+ *latency = stm->hw_latency_frames + stm->current_latency_frames;
+
return CUBEB_OK;
#endif
}
@@ -3008,8 +2091,10 @@ static int
audiounit_stream_get_volume(cubeb_stream * stm, float * volume)
{
assert(stm->output_unit);
- OSStatus r = AudioUnitGetParameter(stm->output_unit, kHALOutputParam_Volume,
- kAudioUnitScope_Global, 0, volume);
+ OSStatus r = AudioUnitGetParameter(stm->output_unit,
+ kHALOutputParam_Volume,
+ kAudioUnitScope_Global,
+ 0, volume);
if (r != noErr) {
LOG("AudioUnitGetParameter/kHALOutputParam_Volume rv=%d", r);
return CUBEB_ERROR;
@@ -3017,137 +2102,182 @@ audiounit_stream_get_volume(cubeb_stream * stm, float * volume)
return CUBEB_OK;
}
-static int
-audiounit_stream_set_volume(cubeb_stream * stm, float volume)
+int audiounit_stream_set_volume(cubeb_stream * stm, float volume)
{
- assert(stm->output_unit);
OSStatus r;
- r = AudioUnitSetParameter(stm->output_unit, kHALOutputParam_Volume,
- kAudioUnitScope_Global, 0, volume, 0);
+
+ r = AudioUnitSetParameter(stm->output_unit,
+ kHALOutputParam_Volume,
+ kAudioUnitScope_Global,
+ 0, volume, 0);
if (r != noErr) {
- LOG("AudioUnitSetParameter/kHALOutputParam_Volume rv=%d", r);
+ PRINT_ERROR_CODE("AudioUnitSetParameter/kHALOutputParam_Volume", r);
return CUBEB_ERROR;
}
return CUBEB_OK;
}
-unique_ptr<char[]>
-convert_uint32_into_string(UInt32 data)
+int audiounit_stream_set_panning(cubeb_stream * stm, float panning)
{
- // Simply create an empty string if no data.
- size_t size = data == 0 ? 0 : 4; // 4 bytes for uint32.
- auto str = unique_ptr<char[]>{new char[size + 1]}; // + 1 for '\0'.
- str[size] = '\0';
- if (size < 4) {
- return str;
+ if (stm->output_desc.mChannelsPerFrame > 2) {
+ return CUBEB_ERROR_INVALID_PARAMETER;
}
- // Reverse 0xWXYZ into 0xZYXW.
- str[0] = (char)(data >> 24);
- str[1] = (char)(data >> 16);
- str[2] = (char)(data >> 8);
- str[3] = (char)(data);
- return str;
+ stm->panning.store(panning, std::memory_order_relaxed);
+ return CUBEB_OK;
}
-int
-audiounit_get_default_device_datasource(cubeb_device_type type, UInt32 * data)
+int audiounit_stream_get_current_device(cubeb_stream * stm,
+ cubeb_device ** const device)
{
- AudioDeviceID id = audiounit_get_default_device_id(type);
- if (id == kAudioObjectUnknown) {
- return CUBEB_ERROR;
- }
+#if TARGET_OS_IPHONE
+ //TODO
+ return CUBEB_ERROR_NOT_SUPPORTED;
+#else
+ OSStatus r;
+ UInt32 size;
+ UInt32 data;
+ char strdata[4];
+ AudioDeviceID output_device_id;
+ AudioDeviceID input_device_id;
- UInt32 size = sizeof(*data);
- /* This fails with some USB headsets (e.g., Plantronic .Audio 628). */
- OSStatus r = AudioObjectGetPropertyData(
- id,
- type == CUBEB_DEVICE_TYPE_INPUT ? &INPUT_DATA_SOURCE_PROPERTY_ADDRESS
- : &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS,
- 0, NULL, &size, data);
- if (r != noErr) {
- *data = 0;
- }
+ AudioObjectPropertyAddress datasource_address = {
+ kAudioDevicePropertyDataSource,
+ kAudioDevicePropertyScopeOutput,
+ kAudioObjectPropertyElementMaster
+ };
- return CUBEB_OK;
-}
+ AudioObjectPropertyAddress datasource_address_input = {
+ kAudioDevicePropertyDataSource,
+ kAudioDevicePropertyScopeInput,
+ kAudioObjectPropertyElementMaster
+ };
-int
-audiounit_get_default_device_name(cubeb_stream * stm,
- cubeb_device * const device,
- cubeb_device_type type)
-{
- assert(stm);
- assert(device);
+ *device = NULL;
- UInt32 data;
- int r = audiounit_get_default_device_datasource(type, &data);
- if (r != CUBEB_OK) {
- return r;
- }
- char ** name = type == CUBEB_DEVICE_TYPE_INPUT ? &device->input_name
- : &device->output_name;
- *name = convert_uint32_into_string(data).release();
- if (!strlen(*name)) { // empty string.
- LOG("(%p) name of %s device is empty!", stm,
- type == CUBEB_DEVICE_TYPE_INPUT ? "input" : "output");
+ if (audiounit_get_output_device_id(&output_device_id) != CUBEB_OK) {
+ return CUBEB_ERROR;
}
- return CUBEB_OK;
-}
-int
-audiounit_stream_get_current_device(cubeb_stream * stm,
- cubeb_device ** const device)
-{
-#if TARGET_OS_IPHONE
- // TODO
- return CUBEB_ERROR_NOT_SUPPORTED;
-#else
*device = new cubeb_device;
if (!*device) {
return CUBEB_ERROR;
}
PodZero(*device, 1);
- int r =
- audiounit_get_default_device_name(stm, *device, CUBEB_DEVICE_TYPE_OUTPUT);
- if (r != CUBEB_OK) {
- return r;
+ size = sizeof(UInt32);
+ /* This fails with some USB headset, so simply return an empty string. */
+ r = AudioObjectGetPropertyData(output_device_id,
+ &datasource_address,
+ 0, NULL, &size, &data);
+ if (r != noErr) {
+ size = 0;
+ data = 0;
}
- r = audiounit_get_default_device_name(stm, *device, CUBEB_DEVICE_TYPE_INPUT);
- if (r != CUBEB_OK) {
- return r;
+ (*device)->output_name = new char[size + 1];
+ if (!(*device)->output_name) {
+ return CUBEB_ERROR;
+ }
+
+ // Turn the four chars packed into a uint32 into a string
+ strdata[0] = (char)(data >> 24);
+ strdata[1] = (char)(data >> 16);
+ strdata[2] = (char)(data >> 8);
+ strdata[3] = (char)(data);
+
+ memcpy((*device)->output_name, strdata, size);
+ (*device)->output_name[size] = '\0';
+
+ if (audiounit_get_input_device_id(&input_device_id) != CUBEB_OK) {
+ return CUBEB_ERROR;
+ }
+
+ size = sizeof(UInt32);
+ r = AudioObjectGetPropertyData(input_device_id, &datasource_address_input, 0, NULL, &size, &data);
+ if (r != noErr) {
+ LOG("(%p) Error when getting device !", stm);
+ size = 0;
+ data = 0;
+ }
+
+ (*device)->input_name = new char[size + 1];
+ if (!(*device)->input_name) {
+ return CUBEB_ERROR;
}
+ // Turn the four chars packed into a uint32 into a string
+ strdata[0] = (char)(data >> 24);
+ strdata[1] = (char)(data >> 16);
+ strdata[2] = (char)(data >> 8);
+ strdata[3] = (char)(data);
+
+ memcpy((*device)->input_name, strdata, size);
+ (*device)->input_name[size] = '\0';
+
return CUBEB_OK;
#endif
}
-int
-audiounit_stream_device_destroy(cubeb_stream * /* stream */,
- cubeb_device * device)
+int audiounit_stream_device_destroy(cubeb_stream * /* stream */,
+ cubeb_device * device)
{
- delete[] device->output_name;
- delete[] device->input_name;
+ delete [] device->output_name;
+ delete [] device->input_name;
delete device;
return CUBEB_OK;
}
-int
-audiounit_stream_register_device_changed_callback(
- cubeb_stream * stream,
- cubeb_device_changed_callback device_changed_callback)
+int audiounit_stream_register_device_changed_callback(cubeb_stream * stream,
+ cubeb_device_changed_callback device_changed_callback)
{
- auto_lock dev_cb_lock(stream->device_changed_callback_lock);
/* Note: second register without unregister first causes 'nope' error.
* Current implementation requires unregister before register a new cb. */
- assert(!device_changed_callback || !stream->device_changed_callback);
+ assert(!stream->device_changed_callback);
+
+ auto_lock lock(stream->mutex);
+
stream->device_changed_callback = device_changed_callback;
+
return CUBEB_OK;
}
+static OSStatus
+audiounit_get_devices(AudioObjectID ** devices, uint32_t * count)
+{
+ OSStatus ret;
+ UInt32 size = 0;
+ AudioObjectPropertyAddress adr = { kAudioHardwarePropertyDevices,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster };
+
+ ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &adr, 0, NULL, &size);
+ if (ret != noErr) {
+ return ret;
+ }
+
+ *count = static_cast<uint32_t>(size / sizeof(AudioObjectID));
+ if (size >= sizeof(AudioObjectID)) {
+ if (*devices != NULL) {
+ delete [] (*devices);
+ }
+ *devices = new AudioObjectID[*count];
+ PodZero(*devices, *count);
+
+ ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, (void *)*devices);
+ if (ret != noErr) {
+ delete [] (*devices);
+ *devices = NULL;
+ }
+ } else {
+ *devices = NULL;
+ ret = -1;
+ }
+
+ return ret;
+}
+
static char *
audiounit_strref_to_cstr_utf8(CFStringRef strref)
{
@@ -3158,12 +2288,11 @@ audiounit_strref_to_cstr_utf8(CFStringRef strref)
}
len = CFStringGetLength(strref);
- // Add 1 to size to allow for '\0' termination character.
- size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1;
- ret = new char[size];
+ size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8);
+ ret = static_cast<char *>(malloc(size));
if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) {
- delete[] ret;
+ free(ret);
ret = NULL;
}
@@ -3173,18 +2302,15 @@ audiounit_strref_to_cstr_utf8(CFStringRef strref)
static uint32_t
audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope)
{
- AudioObjectPropertyAddress adr = {0, scope,
- kAudioObjectPropertyElementMaster};
+ AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
UInt32 size = 0;
uint32_t i, ret = 0;
adr.mSelector = kAudioDevicePropertyStreamConfiguration;
- if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr &&
- size > 0) {
+ if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr && size > 0) {
AudioBufferList * list = static_cast<AudioBufferList *>(alloca(size));
- if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) ==
- noErr) {
+ if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) == noErr) {
for (i = 0; i < list->mNumberBuffers; i++)
ret += list->mBuffers[i].mNumberChannels;
}
@@ -3194,20 +2320,16 @@ audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope)
}
static void
-audiounit_get_available_samplerate(AudioObjectID devid,
- AudioObjectPropertyScope scope,
- uint32_t * min, uint32_t * max,
- uint32_t * def)
+audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope,
+ uint32_t * min, uint32_t * max, uint32_t * def)
{
- AudioObjectPropertyAddress adr = {0, scope,
- kAudioObjectPropertyElementMaster};
+ AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
adr.mSelector = kAudioDevicePropertyNominalSampleRate;
if (AudioObjectHasProperty(devid, &adr)) {
UInt32 size = sizeof(Float64);
Float64 fvalue = 0.0;
- if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) ==
- noErr) {
+ if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) == noErr) {
*def = fvalue;
}
}
@@ -3217,33 +2339,32 @@ audiounit_get_available_samplerate(AudioObjectID devid,
AudioValueRange range;
if (AudioObjectHasProperty(devid, &adr) &&
AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) {
- uint32_t count = size / sizeof(AudioValueRange);
- vector<AudioValueRange> ranges(count);
+ uint32_t i, count = size / sizeof(AudioValueRange);
+ AudioValueRange * ranges = new AudioValueRange[count];
range.mMinimum = 9999999999.0;
range.mMaximum = 0.0;
- if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size,
- ranges.data()) == noErr) {
- for (uint32_t i = 0; i < count; i++) {
+ if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, ranges) == noErr) {
+ for (i = 0; i < count; i++) {
if (ranges[i].mMaximum > range.mMaximum)
range.mMaximum = ranges[i].mMaximum;
if (ranges[i].mMinimum < range.mMinimum)
range.mMinimum = ranges[i].mMinimum;
}
}
+ delete [] ranges;
*max = static_cast<uint32_t>(range.mMaximum);
*min = static_cast<uint32_t>(range.mMinimum);
} else {
*min = *max = 0;
}
+
}
static UInt32
-audiounit_get_device_presentation_latency(AudioObjectID devid,
- AudioObjectPropertyScope scope)
+audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope)
{
- AudioObjectPropertyAddress adr = {0, scope,
- kAudioObjectPropertyElementMaster};
- UInt32 size, dev, stream = 0;
+ AudioObjectPropertyAddress adr = { 0, scope, kAudioObjectPropertyElementMaster };
+ UInt32 size, dev, stream = 0, offset;
AudioStreamID sid[1];
adr.mSelector = kAudioDevicePropertyLatency;
@@ -3260,411 +2381,354 @@ audiounit_get_device_presentation_latency(AudioObjectID devid,
AudioObjectGetPropertyData(sid[0], &adr, 0, NULL, &size, &stream);
}
- return dev + stream;
+ adr.mSelector = kAudioDevicePropertySafetyOffset;
+ size = sizeof(UInt32);
+ if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &offset) != noErr) {
+ offset = 0;
+ }
+
+ return dev + stream + offset;
}
-static int
-audiounit_create_device_from_hwdev(cubeb_device_info * dev_info,
- AudioObjectID devid, cubeb_device_type type)
+static cubeb_device_info *
+audiounit_create_device_from_hwdev(AudioObjectID devid, cubeb_device_type type)
{
- AudioObjectPropertyAddress adr = {0, 0, kAudioObjectPropertyElementMaster};
- UInt32 size;
+ AudioObjectPropertyAddress adr = { 0, 0, kAudioObjectPropertyElementMaster };
+ UInt32 size, ch, latency;
+ cubeb_device_info * ret;
+ CFStringRef str = NULL;
+ AudioValueRange range;
if (type == CUBEB_DEVICE_TYPE_OUTPUT) {
adr.mScope = kAudioDevicePropertyScopeOutput;
} else if (type == CUBEB_DEVICE_TYPE_INPUT) {
adr.mScope = kAudioDevicePropertyScopeInput;
} else {
- return CUBEB_ERROR;
+ return NULL;
}
- UInt32 ch = audiounit_get_channel_count(devid, adr.mScope);
+ ch = audiounit_get_channel_count(devid, adr.mScope);
if (ch == 0) {
- return CUBEB_ERROR;
+ return NULL;
}
- PodZero(dev_info, 1);
+ ret = new cubeb_device_info;
+ PodZero(ret, 1);
- CFStringRef device_id_str = nullptr;
size = sizeof(CFStringRef);
adr.mSelector = kAudioDevicePropertyDeviceUID;
- OSStatus ret =
- AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &device_id_str);
- if (ret == noErr && device_id_str != NULL) {
- dev_info->device_id = audiounit_strref_to_cstr_utf8(device_id_str);
- static_assert(sizeof(cubeb_devid) >= sizeof(decltype(devid)),
- "cubeb_devid can't represent devid");
- dev_info->devid = reinterpret_cast<cubeb_devid>(devid);
- dev_info->group_id = dev_info->device_id;
- CFRelease(device_id_str);
- }
-
- CFStringRef friendly_name_str = nullptr;
- UInt32 ds;
- size = sizeof(UInt32);
- adr.mSelector = kAudioDevicePropertyDataSource;
- ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds);
- if (ret == noErr) {
- AudioValueTranslation trl = {&ds, sizeof(ds), &friendly_name_str,
- sizeof(CFStringRef)};
- adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString;
- size = sizeof(AudioValueTranslation);
- AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl);
+ if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) {
+ ret->device_id = audiounit_strref_to_cstr_utf8(str);
+ ret->devid = (cubeb_devid)(size_t)devid;
+ ret->group_id = strdup(ret->device_id);
+ CFRelease(str);
}
- // If there is no datasource for this device, fall back to the
- // device name.
- if (!friendly_name_str) {
- size = sizeof(CFStringRef);
- adr.mSelector = kAudioObjectPropertyName;
- AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &friendly_name_str);
- }
+ size = sizeof(CFStringRef);
+ adr.mSelector = kAudioObjectPropertyName;
+ if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) {
+ UInt32 ds;
+ size = sizeof(UInt32);
+ adr.mSelector = kAudioDevicePropertyDataSource;
+ if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds) == noErr) {
+ CFStringRef dsname;
+ AudioValueTranslation trl = { &ds, sizeof(ds), &dsname, sizeof(dsname) };
+ adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString;
+ size = sizeof(AudioValueTranslation);
+ // If there is a datasource for this device, use it instead of the device
+ // name.
+ if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl) == noErr) {
+ CFRelease(str);
+ str = dsname;
+ }
+ }
- if (friendly_name_str) {
- dev_info->friendly_name = audiounit_strref_to_cstr_utf8(friendly_name_str);
- CFRelease(friendly_name_str);
- } else {
- // Couldn't get a datasource name nor a device name, return a
- // valid string of length 0.
- char * fallback_name = new char[1];
- fallback_name[0] = '\0';
- dev_info->friendly_name = fallback_name;
+ ret->friendly_name = audiounit_strref_to_cstr_utf8(str);
+ CFRelease(str);
}
- CFStringRef vendor_name_str = nullptr;
size = sizeof(CFStringRef);
adr.mSelector = kAudioObjectPropertyManufacturer;
- ret =
- AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &vendor_name_str);
- if (ret == noErr && vendor_name_str != NULL) {
- dev_info->vendor_name = audiounit_strref_to_cstr_utf8(vendor_name_str);
- CFRelease(vendor_name_str);
- }
-
- dev_info->type = type;
- dev_info->state = CUBEB_DEVICE_STATE_ENABLED;
- dev_info->preferred = (devid == audiounit_get_default_device_id(type))
- ? CUBEB_DEVICE_PREF_ALL
- : CUBEB_DEVICE_PREF_NONE;
-
- dev_info->max_channels = ch;
- dev_info->format =
- (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
+ if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &str) == noErr && str != NULL) {
+ ret->vendor_name = audiounit_strref_to_cstr_utf8(str);
+ CFRelease(str);
+ }
+
+ ret->type = type;
+ ret->state = CUBEB_DEVICE_STATE_ENABLED;
+ ret->preferred = (devid == audiounit_get_default_device_id(type)) ?
+ CUBEB_DEVICE_PREF_ALL : CUBEB_DEVICE_PREF_NONE;
+
+ ret->max_channels = ch;
+ ret->format = (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
/* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */
- dev_info->default_format = CUBEB_DEVICE_FMT_F32NE;
- audiounit_get_available_samplerate(devid, adr.mScope, &dev_info->min_rate,
- &dev_info->max_rate,
- &dev_info->default_rate);
+ ret->default_format = CUBEB_DEVICE_FMT_F32NE;
+ audiounit_get_available_samplerate(devid, adr.mScope,
+ &ret->min_rate, &ret->max_rate, &ret->default_rate);
- UInt32 latency = audiounit_get_device_presentation_latency(devid, adr.mScope);
+ latency = audiounit_get_device_presentation_latency(devid, adr.mScope);
- AudioValueRange range;
adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
size = sizeof(AudioValueRange);
- ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range);
- if (ret == noErr) {
- dev_info->latency_lo = latency + range.mMinimum;
- dev_info->latency_hi = latency + range.mMaximum;
+ if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range) == noErr) {
+ ret->latency_lo = latency + range.mMinimum;
+ ret->latency_hi = latency + range.mMaximum;
} else {
- dev_info->latency_lo =
- 10 * dev_info->default_rate / 1000; /* Default to 10ms */
- dev_info->latency_hi =
- 100 * dev_info->default_rate / 1000; /* Default to 100ms */
+ ret->latency_lo = 10 * ret->default_rate / 1000; /* Default to 10ms */
+ ret->latency_hi = 100 * ret->default_rate / 1000; /* Default to 100ms */
}
- return CUBEB_OK;
-}
-
-bool
-is_aggregate_device(cubeb_device_info * device_info)
-{
- assert(device_info->friendly_name);
- return !strncmp(device_info->friendly_name, PRIVATE_AGGREGATE_DEVICE_NAME,
- strlen(PRIVATE_AGGREGATE_DEVICE_NAME));
+ return ret;
}
static int
audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type,
- cubeb_device_collection * collection)
+ cubeb_device_collection ** collection)
{
- vector<AudioObjectID> input_devs;
- vector<AudioObjectID> output_devs;
+ AudioObjectID * hwdevs = NULL;
+ uint32_t i, hwdevcount = 0;
+ OSStatus err;
- // Count number of input and output devices. This is not
- // necessarily the same as the count of raw devices supported by the
- // system since, for example, with Soundflower installed, some
- // devices may report as being both input *and* output and cubeb
- // separates those into two different devices.
-
- if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
- output_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
+ if ((err = audiounit_get_devices(&hwdevs, &hwdevcount)) != noErr) {
+ return CUBEB_ERROR;
}
- if (type & CUBEB_DEVICE_TYPE_INPUT) {
- input_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
- }
+ *collection = static_cast<cubeb_device_collection *>(malloc(sizeof(cubeb_device_collection) +
+ sizeof(cubeb_device_info*) * (hwdevcount > 0 ? hwdevcount - 1 : 0)));
+ (*collection)->count = 0;
- auto devices = new cubeb_device_info[output_devs.size() + input_devs.size()];
- collection->count = 0;
+ if (hwdevcount > 0) {
+ cubeb_device_info * cur;
- if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
- for (auto dev : output_devs) {
- auto device = &devices[collection->count];
- auto err = audiounit_create_device_from_hwdev(device, dev,
- CUBEB_DEVICE_TYPE_OUTPUT);
- if (err != CUBEB_OK || is_aggregate_device(device)) {
- continue;
+ if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
+ for (i = 0; i < hwdevcount; i++) {
+ if ((cur = audiounit_create_device_from_hwdev(hwdevs[i], CUBEB_DEVICE_TYPE_OUTPUT)) != NULL)
+ (*collection)->device[(*collection)->count++] = cur;
}
- collection->count += 1;
}
- }
- if (type & CUBEB_DEVICE_TYPE_INPUT) {
- for (auto dev : input_devs) {
- auto device = &devices[collection->count];
- auto err = audiounit_create_device_from_hwdev(device, dev,
- CUBEB_DEVICE_TYPE_INPUT);
- if (err != CUBEB_OK || is_aggregate_device(device)) {
- continue;
+ if (type & CUBEB_DEVICE_TYPE_INPUT) {
+ for (i = 0; i < hwdevcount; i++) {
+ if ((cur = audiounit_create_device_from_hwdev(hwdevs[i], CUBEB_DEVICE_TYPE_INPUT)) != NULL)
+ (*collection)->device[(*collection)->count++] = cur;
}
- collection->count += 1;
}
}
- if (collection->count > 0) {
- collection->device = devices;
- } else {
- delete[] devices;
- collection->device = NULL;
- }
+ delete [] hwdevs;
return CUBEB_OK;
}
-static void
-audiounit_device_destroy(cubeb_device_info * device)
+/* qsort compare method. */
+int compare_devid(const void * a, const void * b)
{
- delete[] device->device_id;
- delete[] device->friendly_name;
- delete[] device->vendor_name;
+ return (*(AudioObjectID*)a - *(AudioObjectID*)b);
}
-static int
-audiounit_device_collection_destroy(cubeb * /* context */,
- cubeb_device_collection * collection)
+static uint32_t
+audiounit_get_devices_of_type(cubeb_device_type devtype, AudioObjectID ** devid_array)
{
- for (size_t i = 0; i < collection->count; i++) {
- audiounit_device_destroy(&collection->device[i]);
- }
- delete[] collection->device;
+ assert(devid_array == NULL || *devid_array == NULL);
- return CUBEB_OK;
-}
-
-static vector<AudioObjectID>
-audiounit_get_devices_of_type(cubeb_device_type devtype)
-{
+ AudioObjectPropertyAddress adr = { kAudioHardwarePropertyDevices,
+ kAudioObjectPropertyScopeGlobal,
+ kAudioObjectPropertyElementMaster };
UInt32 size = 0;
- OSStatus ret = AudioObjectGetPropertyDataSize(
- kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS, 0, NULL, &size);
+ OSStatus ret = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &adr, 0, NULL, &size);
if (ret != noErr) {
- return vector<AudioObjectID>();
- }
- vector<AudioObjectID> devices(size / sizeof(AudioObjectID));
- ret = AudioObjectGetPropertyData(kAudioObjectSystemObject,
- &DEVICES_PROPERTY_ADDRESS, 0, NULL, &size,
- devices.data());
- if (ret != noErr) {
- return vector<AudioObjectID>();
+ return 0;
}
+ /* Total number of input and output devices. */
+ uint32_t count = (uint32_t)(size / sizeof(AudioObjectID));
- // Remove the aggregate device from the list of devices (if any).
- for (auto it = devices.begin(); it != devices.end();) {
- CFStringRef name = get_device_name(*it);
- if (name && CFStringFind(name, CFSTR("CubebAggregateDevice"), 0).location !=
- kCFNotFound) {
- it = devices.erase(it);
- } else {
- it++;
- }
- if (name) {
- CFRelease(name);
- }
+ AudioObjectID devices[count];
+ ret = AudioObjectGetPropertyData(kAudioObjectSystemObject, &adr, 0, NULL, &size, &devices);
+ if (ret != noErr) {
+ return 0;
}
-
/* Expected sorted but did not find anything in the docs. */
- sort(devices.begin(), devices.end(),
- [](AudioObjectID a, AudioObjectID b) { return a < b; });
+ qsort(devices, count, sizeof(AudioObjectID), compare_devid);
if (devtype == (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) {
- return devices;
+ if (devid_array) {
+ *devid_array = new AudioObjectID[count];
+ assert(*devid_array);
+ memcpy(*devid_array, &devices, count * sizeof(AudioObjectID));
+ }
+ return count;
}
- AudioObjectPropertyScope scope = (devtype == CUBEB_DEVICE_TYPE_INPUT)
- ? kAudioDevicePropertyScopeInput
- : kAudioDevicePropertyScopeOutput;
+ AudioObjectPropertyScope scope = (devtype == CUBEB_DEVICE_TYPE_INPUT) ?
+ kAudioDevicePropertyScopeInput :
+ kAudioDevicePropertyScopeOutput;
- vector<AudioObjectID> devices_in_scope;
- for (uint32_t i = 0; i < devices.size(); ++i) {
+ uint32_t dev_count = 0;
+ AudioObjectID devices_in_scope[count];
+ for(uint32_t i = 0; i < count; ++i) {
/* For device in the given scope channel must be > 0. */
if (audiounit_get_channel_count(devices[i], scope) > 0) {
- devices_in_scope.push_back(devices[i]);
+ devices_in_scope[dev_count] = devices[i];
+ ++dev_count;
}
}
- return devices_in_scope;
+ if (devid_array && dev_count > 0) {
+ *devid_array = new AudioObjectID[dev_count];
+ assert(*devid_array);
+ memcpy(*devid_array, &devices_in_scope, dev_count * sizeof(AudioObjectID));
+ }
+ return dev_count;
+}
+
+static uint32_t
+audiounit_equal_arrays(AudioObjectID * left, AudioObjectID * right, uint32_t size)
+{
+ /* Expected sorted arrays. */
+ for (uint32_t i = 0; i < size; ++i) {
+ if (left[i] != right[i]) {
+ return 0;
+ }
+ }
+ return 1;
}
static OSStatus
-audiounit_collection_changed_callback(
- AudioObjectID /* inObjectID */, UInt32 /* inNumberAddresses */,
- const AudioObjectPropertyAddress * /* inAddresses */, void * inClientData)
+audiounit_collection_changed_callback(AudioObjectID /* inObjectID */,
+ UInt32 /* inNumberAddresses */,
+ const AudioObjectPropertyAddress * /* inAddresses */,
+ void * inClientData)
{
cubeb * context = static_cast<cubeb *>(inClientData);
+ auto_lock lock(context->mutex);
- // This can be called from inside an AudioUnit function, dispatch to another
- // queue.
- dispatch_async(context->serial_queue, ^() {
- auto_lock lock(context->mutex);
- if (!context->input_collection_changed_callback &&
- !context->output_collection_changed_callback) {
- /* Listener removed while waiting in mutex, abort. */
- return;
- }
- if (context->input_collection_changed_callback) {
- vector<AudioObjectID> devices =
- audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
- /* Elements in the vector expected sorted. */
- if (context->input_device_array != devices) {
- context->input_device_array = devices;
- context->input_collection_changed_callback(
- context, context->input_collection_changed_user_ptr);
- }
- }
- if (context->output_collection_changed_callback) {
- vector<AudioObjectID> devices =
- audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
- /* Elements in the vector expected sorted. */
- if (context->output_device_array != devices) {
- context->output_device_array = devices;
- context->output_collection_changed_callback(
- context, context->output_collection_changed_user_ptr);
- }
+ if (context->collection_changed_callback == NULL) {
+ /* Listener removed while waiting in mutex, abort. */
+ return noErr;
+ }
+
+ /* Differentiate input from output changes. */
+ if (context->collection_changed_devtype == CUBEB_DEVICE_TYPE_INPUT ||
+ context->collection_changed_devtype == CUBEB_DEVICE_TYPE_OUTPUT) {
+ AudioObjectID * devices = NULL;
+ uint32_t new_number_of_devices = audiounit_get_devices_of_type(context->collection_changed_devtype, &devices);
+ /* When count is the same examine the devid for the case of coalescing. */
+ 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. */
+ delete [] devices;
+ 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. */
+ delete [] context->devtype_device_array;
+ context->devtype_device_array = devices;
+ }
+
+ context->collection_changed_callback(context, context->collection_changed_user_ptr);
return noErr;
}
static OSStatus
-audiounit_add_device_listener(
- cubeb * context, cubeb_device_type devtype,
- cubeb_device_collection_changed_callback collection_changed_callback,
- void * user_ptr)
+audiounit_add_device_listener(cubeb * context,
+ cubeb_device_type devtype,
+ cubeb_device_collection_changed_callback collection_changed_callback,
+ void * user_ptr)
{
- context->mutex.assert_current_thread_owns();
- assert(devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT));
/* Note: second register without unregister first causes 'nope' error.
* Current implementation requires unregister before register a new cb. */
- assert((devtype & CUBEB_DEVICE_TYPE_INPUT) &&
- !context->input_collection_changed_callback ||
- (devtype & CUBEB_DEVICE_TYPE_OUTPUT) &&
- !context->output_collection_changed_callback);
-
- if (!context->input_collection_changed_callback &&
- !context->output_collection_changed_callback) {
- OSStatus ret = AudioObjectAddPropertyListener(
- kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS,
- audiounit_collection_changed_callback, context);
- if (ret != noErr) {
- return ret;
- }
- }
- if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
- /* Expected empty after unregister. */
- assert(context->input_device_array.empty());
- context->input_device_array =
- audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
- context->input_collection_changed_callback = collection_changed_callback;
- context->input_collection_changed_user_ptr = user_ptr;
- }
- if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
- /* Expected empty after unregister. */
- assert(context->output_device_array.empty());
- context->output_device_array =
- audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
- context->output_collection_changed_callback = collection_changed_callback;
- context->output_collection_changed_user_ptr = user_ptr;
+ assert(context->collection_changed_callback == NULL);
+
+ AudioObjectPropertyAddress devAddr;
+ devAddr.mSelector = kAudioHardwarePropertyDevices;
+ devAddr.mScope = kAudioObjectPropertyScopeGlobal;
+ devAddr.mElement = kAudioObjectPropertyElementMaster;
+
+ OSStatus ret = AudioObjectAddPropertyListener(kAudioObjectSystemObject,
+ &devAddr,
+ audiounit_collection_changed_callback,
+ context);
+ if (ret == noErr) {
+ /* Expected zero after unregister. */
+ assert(context->devtype_device_count == 0);
+ assert(context->devtype_device_array == NULL);
+ /* Listener works for input and output.
+ * When requested one of them we need to differentiate. */
+ if (devtype == CUBEB_DEVICE_TYPE_INPUT ||
+ devtype == CUBEB_DEVICE_TYPE_OUTPUT) {
+ /* Used to differentiate input from output device changes. */
+ context->devtype_device_count = audiounit_get_devices_of_type(devtype, &context->devtype_device_array);
+ }
+ context->collection_changed_devtype = devtype;
+ context->collection_changed_callback = collection_changed_callback;
+ context->collection_changed_user_ptr = user_ptr;
}
- return noErr;
+ return ret;
}
static OSStatus
-audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype)
+audiounit_remove_device_listener(cubeb * context)
{
- context->mutex.assert_current_thread_owns();
+ AudioObjectPropertyAddress devAddr;
+ devAddr.mSelector = kAudioHardwarePropertyDevices;
+ devAddr.mScope = kAudioObjectPropertyScopeGlobal;
+ devAddr.mElement = kAudioObjectPropertyElementMaster;
- if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
- context->input_collection_changed_callback = nullptr;
- context->input_collection_changed_user_ptr = nullptr;
- context->input_device_array.clear();
- }
- if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
- context->output_collection_changed_callback = nullptr;
- context->output_collection_changed_user_ptr = nullptr;
- context->output_device_array.clear();
- }
-
- if (context->input_collection_changed_callback ||
- context->output_collection_changed_callback) {
- return noErr;
- }
/* Note: unregister a non registered cb is not a problem, not checking. */
- return AudioObjectRemovePropertyListener(
- kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS,
- audiounit_collection_changed_callback, context);
+ OSStatus ret = AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
+ &devAddr,
+ audiounit_collection_changed_callback,
+ context);
+ if (ret == noErr) {
+ /* Reset all values. */
+ 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) {
+ delete [] context->devtype_device_array;
+ context->devtype_device_array = NULL;
+ }
+ }
+ return ret;
}
-int
-audiounit_register_device_collection_changed(
- cubeb * context, cubeb_device_type devtype,
- cubeb_device_collection_changed_callback collection_changed_callback,
- void * user_ptr)
+int audiounit_register_device_collection_changed(cubeb * context,
+ cubeb_device_type devtype,
+ cubeb_device_collection_changed_callback collection_changed_callback,
+ void * user_ptr)
{
- if (devtype == CUBEB_DEVICE_TYPE_UNKNOWN) {
- return CUBEB_ERROR_INVALID_PARAMETER;
- }
OSStatus ret;
auto_lock lock(context->mutex);
if (collection_changed_callback) {
ret = audiounit_add_device_listener(context, devtype,
- collection_changed_callback, user_ptr);
+ collection_changed_callback,
+ user_ptr);
} else {
- ret = audiounit_remove_device_listener(context, devtype);
+ ret = audiounit_remove_device_listener(context);
}
return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR;
}
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,
- /*.device_collection_destroy =*/audiounit_device_collection_destroy,
- /*.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_get_input_latency =*/NULL,
- /*.stream_set_volume =*/audiounit_stream_set_volume,
- /*.stream_set_name =*/NULL,
- /*.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};
+ /*.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
+};