duckstation

duckstation, but archived from the revision just before upstream changed it to a proprietary software project, this version is the libre one
git clone https://git.neptards.moe/u3shit/duckstation.git
Log | Files | Refs | README | LICENSE

cubeb_audiounit.cpp (123615B)


      1 /*
      2  * Copyright © 2011 Mozilla Foundation
      3  *
      4  * This program is made available under an ISC-style license.  See the
      5  * accompanying file LICENSE for details.
      6  */
      7 #undef NDEBUG
      8 
      9 #include <AudioUnit/AudioUnit.h>
     10 #include <TargetConditionals.h>
     11 #include <assert.h>
     12 #include <mach/mach_time.h>
     13 #include <pthread.h>
     14 #include <stdlib.h>
     15 #if !TARGET_OS_IPHONE
     16 #include <AvailabilityMacros.h>
     17 #include <CoreAudio/AudioHardware.h>
     18 #include <CoreAudio/HostTime.h>
     19 #include <CoreFoundation/CoreFoundation.h>
     20 #endif
     21 #include "cubeb-internal.h"
     22 #include "cubeb/cubeb.h"
     23 #include "cubeb_mixer.h"
     24 #include <AudioToolbox/AudioToolbox.h>
     25 #include <CoreAudio/CoreAudioTypes.h>
     26 #if !TARGET_OS_IPHONE
     27 #include "cubeb_osx_run_loop.h"
     28 #endif
     29 #include "cubeb_resampler.h"
     30 #include "cubeb_ring_array.h"
     31 #include <algorithm>
     32 #include <atomic>
     33 #include <set>
     34 #include <string>
     35 #include <sys/time.h>
     36 #include <vector>
     37 
     38 using namespace std;
     39 
     40 #if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
     41 typedef UInt32 AudioFormatFlags;
     42 #endif
     43 
     44 #define AU_OUT_BUS 0
     45 #define AU_IN_BUS 1
     46 
     47 const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb";
     48 const char * PRIVATE_AGGREGATE_DEVICE_NAME = "CubebAggregateDevice";
     49 
     50 #ifdef ALOGV
     51 #undef ALOGV
     52 #endif
     53 #define ALOGV(msg, ...)                                                        \
     54   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),   \
     55                  ^{                                                            \
     56                    LOGV(msg, ##__VA_ARGS__);                                   \
     57                  })
     58 
     59 #ifdef ALOG
     60 #undef ALOG
     61 #endif
     62 #define ALOG(msg, ...)                                                         \
     63   dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),   \
     64                  ^{                                                            \
     65                    LOG(msg, ##__VA_ARGS__);                                    \
     66                  })
     67 
     68 /* Testing empirically, some headsets report a minimal latency that is very
     69  * low, but this does not work in practice. Lie and say the minimum is 256
     70  * frames. */
     71 const uint32_t SAFE_MIN_LATENCY_FRAMES = 128;
     72 const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
     73 
     74 const AudioObjectPropertyAddress DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS = {
     75     kAudioHardwarePropertyDefaultInputDevice, kAudioObjectPropertyScopeGlobal,
     76     kAudioObjectPropertyElementMaster};
     77 
     78 const AudioObjectPropertyAddress DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS = {
     79     kAudioHardwarePropertyDefaultOutputDevice, kAudioObjectPropertyScopeGlobal,
     80     kAudioObjectPropertyElementMaster};
     81 
     82 const AudioObjectPropertyAddress DEVICE_IS_ALIVE_PROPERTY_ADDRESS = {
     83     kAudioDevicePropertyDeviceIsAlive, kAudioObjectPropertyScopeGlobal,
     84     kAudioObjectPropertyElementMaster};
     85 
     86 const AudioObjectPropertyAddress DEVICES_PROPERTY_ADDRESS = {
     87     kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
     88     kAudioObjectPropertyElementMaster};
     89 
     90 const AudioObjectPropertyAddress INPUT_DATA_SOURCE_PROPERTY_ADDRESS = {
     91     kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeInput,
     92     kAudioObjectPropertyElementMaster};
     93 
     94 const AudioObjectPropertyAddress OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS = {
     95     kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput,
     96     kAudioObjectPropertyElementMaster};
     97 
     98 typedef uint32_t device_flags_value;
     99 
    100 enum device_flags {
    101   DEV_UNKNOWN = 0x00,        /* Unknown */
    102   DEV_INPUT = 0x01,          /* Record device like mic */
    103   DEV_OUTPUT = 0x02,         /* Playback device like speakers */
    104   DEV_SYSTEM_DEFAULT = 0x04, /* System default device */
    105   DEV_SELECTED_DEFAULT =
    106       0x08, /* User selected to use the system default device */
    107 };
    108 
    109 void
    110 audiounit_stream_stop_internal(cubeb_stream * stm);
    111 static int
    112 audiounit_stream_start_internal(cubeb_stream * stm);
    113 static void
    114 audiounit_close_stream(cubeb_stream * stm);
    115 static int
    116 audiounit_setup_stream(cubeb_stream * stm);
    117 static vector<AudioObjectID>
    118 audiounit_get_devices_of_type(cubeb_device_type devtype);
    119 static UInt32
    120 audiounit_get_device_presentation_latency(AudioObjectID devid,
    121                                           AudioObjectPropertyScope scope);
    122 
    123 #if !TARGET_OS_IPHONE
    124 static AudioObjectID
    125 audiounit_get_default_device_id(cubeb_device_type type);
    126 static int
    127 audiounit_uninstall_device_changed_callback(cubeb_stream * stm);
    128 static int
    129 audiounit_uninstall_system_changed_callback(cubeb_stream * stm);
    130 static void
    131 audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags);
    132 #endif
    133 
    134 extern cubeb_ops const audiounit_ops;
    135 
    136 struct cubeb {
    137   cubeb_ops const * ops = &audiounit_ops;
    138   owned_critical_section mutex;
    139   int active_streams = 0;
    140   uint32_t global_latency_frames = 0;
    141   cubeb_device_collection_changed_callback input_collection_changed_callback =
    142       nullptr;
    143   void * input_collection_changed_user_ptr = nullptr;
    144   cubeb_device_collection_changed_callback output_collection_changed_callback =
    145       nullptr;
    146   void * output_collection_changed_user_ptr = nullptr;
    147   // Store list of devices to detect changes
    148   vector<AudioObjectID> input_device_array;
    149   vector<AudioObjectID> output_device_array;
    150   // The queue should be released when it’s no longer needed.
    151   dispatch_queue_t serial_queue =
    152       dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL);
    153   // Current used channel layout
    154   atomic<cubeb_channel_layout> layout{CUBEB_LAYOUT_UNDEFINED};
    155   uint32_t channels = 0;
    156 };
    157 
    158 static unique_ptr<AudioChannelLayout, decltype(&free)>
    159 make_sized_audio_channel_layout(size_t sz)
    160 {
    161   assert(sz >= sizeof(AudioChannelLayout));
    162   AudioChannelLayout * acl =
    163       reinterpret_cast<AudioChannelLayout *>(calloc(1, sz));
    164   assert(acl); // Assert the allocation works.
    165   return unique_ptr<AudioChannelLayout, decltype(&free)>(acl, free);
    166 }
    167 
    168 enum class io_side {
    169   INPUT,
    170   OUTPUT,
    171 };
    172 
    173 static char const *
    174 to_string(io_side side)
    175 {
    176   switch (side) {
    177   case io_side::INPUT:
    178     return "input";
    179   case io_side::OUTPUT:
    180     return "output";
    181   }
    182 }
    183 
    184 struct device_info {
    185   AudioDeviceID id = kAudioObjectUnknown;
    186   device_flags_value flags = DEV_UNKNOWN;
    187 };
    188 
    189 struct property_listener {
    190   AudioDeviceID device_id;
    191   const AudioObjectPropertyAddress * property_address;
    192   AudioObjectPropertyListenerProc callback;
    193   cubeb_stream * stream;
    194 
    195   property_listener(AudioDeviceID id,
    196                     const AudioObjectPropertyAddress * address,
    197                     AudioObjectPropertyListenerProc proc, cubeb_stream * stm)
    198       : device_id(id), property_address(address), callback(proc), stream(stm)
    199   {
    200   }
    201 };
    202 
    203 struct cubeb_stream {
    204   explicit cubeb_stream(cubeb * context);
    205 
    206   /* Note: Must match cubeb_stream layout in cubeb.c. */
    207   cubeb * context;
    208   void * user_ptr = nullptr;
    209   /**/
    210 
    211   cubeb_data_callback data_callback = nullptr;
    212   cubeb_state_callback state_callback = nullptr;
    213   cubeb_device_changed_callback device_changed_callback = nullptr;
    214   owned_critical_section device_changed_callback_lock;
    215   /* Stream creation parameters */
    216   cubeb_stream_params input_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
    217                                              CUBEB_LAYOUT_UNDEFINED,
    218                                              CUBEB_STREAM_PREF_NONE};
    219   cubeb_stream_params output_stream_params = {CUBEB_SAMPLE_FLOAT32NE, 0, 0,
    220                                               CUBEB_LAYOUT_UNDEFINED,
    221                                               CUBEB_STREAM_PREF_NONE};
    222   device_info input_device;
    223   device_info output_device;
    224   /* Format descriptions */
    225   AudioStreamBasicDescription input_desc;
    226   AudioStreamBasicDescription output_desc;
    227   /* I/O AudioUnits */
    228   AudioUnit input_unit = nullptr;
    229   AudioUnit output_unit = nullptr;
    230   /* I/O device sample rate */
    231   Float64 input_hw_rate = 0;
    232   Float64 output_hw_rate = 0;
    233   /* Expected I/O thread interleave,
    234    * calculated from I/O hw rate. */
    235   int expected_output_callbacks_in_a_row = 0;
    236   owned_critical_section mutex;
    237   // Hold the input samples in every input callback iteration.
    238   // Only accessed on input/output callback thread and during initial configure.
    239   unique_ptr<auto_array_wrapper> input_linear_buffer;
    240   /* Frame counters */
    241   atomic<uint64_t> frames_played{0};
    242   uint64_t frames_queued = 0;
    243   // How many frames got read from the input since the stream started (includes
    244   // padded silence)
    245   atomic<int64_t> frames_read{0};
    246   // How many frames got written to the output device since the stream started
    247   atomic<int64_t> frames_written{0};
    248   atomic<bool> shutdown{true};
    249   atomic<bool> draining{false};
    250   atomic<bool> reinit_pending{false};
    251   atomic<bool> destroy_pending{false};
    252   /* Latency requested by the user. */
    253   uint32_t latency_frames = 0;
    254   atomic<uint32_t> current_latency_frames{0};
    255   atomic<uint32_t> total_output_latency_frames{0};
    256   unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler;
    257   /* This is true if a device change callback is currently running.  */
    258   atomic<bool> switching_device{false};
    259   atomic<bool> buffer_size_change_state{false};
    260   AudioDeviceID aggregate_device_id =
    261       kAudioObjectUnknown; // the aggregate device id
    262   AudioObjectID plugin_id =
    263       kAudioObjectUnknown; // used to create aggregate device
    264   /* Mixer interface */
    265   unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> mixer;
    266   /* Buffer where remixing/resampling will occur when upmixing is required */
    267   /* Only accessed from callback thread */
    268   unique_ptr<uint8_t[]> temp_buffer;
    269   size_t temp_buffer_size = 0; // size in bytes.
    270   /* Listeners indicating what system events are monitored. */
    271   unique_ptr<property_listener> default_input_listener;
    272   unique_ptr<property_listener> default_output_listener;
    273   unique_ptr<property_listener> input_alive_listener;
    274   unique_ptr<property_listener> input_source_listener;
    275   unique_ptr<property_listener> output_source_listener;
    276 };
    277 
    278 bool
    279 has_input(cubeb_stream * stm)
    280 {
    281   return stm->input_stream_params.rate != 0;
    282 }
    283 
    284 bool
    285 has_output(cubeb_stream * stm)
    286 {
    287   return stm->output_stream_params.rate != 0;
    288 }
    289 
    290 cubeb_channel
    291 channel_label_to_cubeb_channel(UInt32 label)
    292 {
    293   switch (label) {
    294   case kAudioChannelLabel_Left:
    295     return CHANNEL_FRONT_LEFT;
    296   case kAudioChannelLabel_Right:
    297     return CHANNEL_FRONT_RIGHT;
    298   case kAudioChannelLabel_Center:
    299     return CHANNEL_FRONT_CENTER;
    300   case kAudioChannelLabel_LFEScreen:
    301     return CHANNEL_LOW_FREQUENCY;
    302   case kAudioChannelLabel_LeftSurround:
    303     return CHANNEL_BACK_LEFT;
    304   case kAudioChannelLabel_RightSurround:
    305     return CHANNEL_BACK_RIGHT;
    306   case kAudioChannelLabel_LeftCenter:
    307     return CHANNEL_FRONT_LEFT_OF_CENTER;
    308   case kAudioChannelLabel_RightCenter:
    309     return CHANNEL_FRONT_RIGHT_OF_CENTER;
    310   case kAudioChannelLabel_CenterSurround:
    311     return CHANNEL_BACK_CENTER;
    312   case kAudioChannelLabel_LeftSurroundDirect:
    313     return CHANNEL_SIDE_LEFT;
    314   case kAudioChannelLabel_RightSurroundDirect:
    315     return CHANNEL_SIDE_RIGHT;
    316   case kAudioChannelLabel_TopCenterSurround:
    317     return CHANNEL_TOP_CENTER;
    318   case kAudioChannelLabel_VerticalHeightLeft:
    319     return CHANNEL_TOP_FRONT_LEFT;
    320   case kAudioChannelLabel_VerticalHeightCenter:
    321     return CHANNEL_TOP_FRONT_CENTER;
    322   case kAudioChannelLabel_VerticalHeightRight:
    323     return CHANNEL_TOP_FRONT_RIGHT;
    324   case kAudioChannelLabel_TopBackLeft:
    325     return CHANNEL_TOP_BACK_LEFT;
    326   case kAudioChannelLabel_TopBackCenter:
    327     return CHANNEL_TOP_BACK_CENTER;
    328   case kAudioChannelLabel_TopBackRight:
    329     return CHANNEL_TOP_BACK_RIGHT;
    330   default:
    331     return CHANNEL_UNKNOWN;
    332   }
    333 }
    334 
    335 AudioChannelLabel
    336 cubeb_channel_to_channel_label(cubeb_channel channel)
    337 {
    338   switch (channel) {
    339   case CHANNEL_FRONT_LEFT:
    340     return kAudioChannelLabel_Left;
    341   case CHANNEL_FRONT_RIGHT:
    342     return kAudioChannelLabel_Right;
    343   case CHANNEL_FRONT_CENTER:
    344     return kAudioChannelLabel_Center;
    345   case CHANNEL_LOW_FREQUENCY:
    346     return kAudioChannelLabel_LFEScreen;
    347   case CHANNEL_BACK_LEFT:
    348     return kAudioChannelLabel_LeftSurround;
    349   case CHANNEL_BACK_RIGHT:
    350     return kAudioChannelLabel_RightSurround;
    351   case CHANNEL_FRONT_LEFT_OF_CENTER:
    352     return kAudioChannelLabel_LeftCenter;
    353   case CHANNEL_FRONT_RIGHT_OF_CENTER:
    354     return kAudioChannelLabel_RightCenter;
    355   case CHANNEL_BACK_CENTER:
    356     return kAudioChannelLabel_CenterSurround;
    357   case CHANNEL_SIDE_LEFT:
    358     return kAudioChannelLabel_LeftSurroundDirect;
    359   case CHANNEL_SIDE_RIGHT:
    360     return kAudioChannelLabel_RightSurroundDirect;
    361   case CHANNEL_TOP_CENTER:
    362     return kAudioChannelLabel_TopCenterSurround;
    363   case CHANNEL_TOP_FRONT_LEFT:
    364     return kAudioChannelLabel_VerticalHeightLeft;
    365   case CHANNEL_TOP_FRONT_CENTER:
    366     return kAudioChannelLabel_VerticalHeightCenter;
    367   case CHANNEL_TOP_FRONT_RIGHT:
    368     return kAudioChannelLabel_VerticalHeightRight;
    369   case CHANNEL_TOP_BACK_LEFT:
    370     return kAudioChannelLabel_TopBackLeft;
    371   case CHANNEL_TOP_BACK_CENTER:
    372     return kAudioChannelLabel_TopBackCenter;
    373   case CHANNEL_TOP_BACK_RIGHT:
    374     return kAudioChannelLabel_TopBackRight;
    375   default:
    376     return kAudioChannelLabel_Unknown;
    377   }
    378 }
    379 
    380 bool
    381 is_common_sample_rate(Float64 sample_rate)
    382 {
    383   /* Some commonly used sample rates and their multiples and divisors. */
    384   return sample_rate == 8000 || sample_rate == 16000 || sample_rate == 22050 ||
    385          sample_rate == 32000 || sample_rate == 44100 || sample_rate == 48000 ||
    386          sample_rate == 88200 || sample_rate == 96000;
    387 }
    388 
    389 #if TARGET_OS_IPHONE
    390 typedef UInt32 AudioDeviceID;
    391 typedef UInt32 AudioObjectID;
    392 
    393 #define AudioGetCurrentHostTime mach_absolute_time
    394 
    395 #endif
    396 
    397 uint64_t
    398 ConvertHostTimeToNanos(uint64_t host_time)
    399 {
    400   static struct mach_timebase_info timebase_info;
    401   static bool initialized = false;
    402   if (!initialized) {
    403     mach_timebase_info(&timebase_info);
    404     initialized = true;
    405   }
    406 
    407   long double answer = host_time;
    408   if (timebase_info.numer != timebase_info.denom) {
    409     answer *= timebase_info.numer;
    410     answer /= timebase_info.denom;
    411   }
    412   return (uint64_t)answer;
    413 }
    414 
    415 static void
    416 audiounit_increment_active_streams(cubeb * ctx)
    417 {
    418   ctx->mutex.assert_current_thread_owns();
    419   ctx->active_streams += 1;
    420 }
    421 
    422 static void
    423 audiounit_decrement_active_streams(cubeb * ctx)
    424 {
    425   ctx->mutex.assert_current_thread_owns();
    426   ctx->active_streams -= 1;
    427 }
    428 
    429 static int
    430 audiounit_active_streams(cubeb * ctx)
    431 {
    432   ctx->mutex.assert_current_thread_owns();
    433   return ctx->active_streams;
    434 }
    435 
    436 static void
    437 audiounit_set_global_latency(cubeb * ctx, uint32_t latency_frames)
    438 {
    439   ctx->mutex.assert_current_thread_owns();
    440   assert(audiounit_active_streams(ctx) == 1);
    441   ctx->global_latency_frames = latency_frames;
    442 }
    443 
    444 static void
    445 audiounit_make_silent(AudioBuffer * ioData)
    446 {
    447   assert(ioData);
    448   assert(ioData->mData);
    449   memset(ioData->mData, 0, ioData->mDataByteSize);
    450 }
    451 
    452 static OSStatus
    453 audiounit_render_input(cubeb_stream * stm, AudioUnitRenderActionFlags * flags,
    454                        AudioTimeStamp const * tstamp, UInt32 bus,
    455                        UInt32 input_frames)
    456 {
    457   /* Create the AudioBufferList to store input. */
    458   AudioBufferList input_buffer_list;
    459   input_buffer_list.mBuffers[0].mDataByteSize =
    460       stm->input_desc.mBytesPerFrame * input_frames;
    461   input_buffer_list.mBuffers[0].mData = nullptr;
    462   input_buffer_list.mBuffers[0].mNumberChannels =
    463       stm->input_desc.mChannelsPerFrame;
    464   input_buffer_list.mNumberBuffers = 1;
    465 
    466   /* Render input samples */
    467   OSStatus r = AudioUnitRender(stm->input_unit, flags, tstamp, bus,
    468                                input_frames, &input_buffer_list);
    469 
    470   if (r != noErr) {
    471     LOG("AudioUnitRender rv=%d", r);
    472     if (r != kAudioUnitErr_CannotDoInCurrentContext) {
    473       return r;
    474     }
    475     if (stm->output_unit) {
    476       // kAudioUnitErr_CannotDoInCurrentContext is returned when using a BT
    477       // headset and the profile is changed from A2DP to HFP/HSP. The previous
    478       // output device is no longer valid and must be reset.
    479       audiounit_reinit_stream_async(stm, DEV_INPUT | DEV_OUTPUT);
    480     }
    481     // For now state that no error occurred and feed silence, stream will be
    482     // resumed once reinit has completed.
    483     ALOGV("(%p) input: reinit pending feeding silence instead", stm);
    484     stm->input_linear_buffer->push_silence(input_frames *
    485                                            stm->input_desc.mChannelsPerFrame);
    486   } else {
    487     /* Copy input data in linear buffer. */
    488     stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
    489                                    input_frames *
    490                                        stm->input_desc.mChannelsPerFrame);
    491   }
    492 
    493   /* Advance input frame counter. */
    494   assert(input_frames > 0);
    495   stm->frames_read += input_frames;
    496 
    497   ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, "
    498         "total frames %lu.",
    499         stm, (unsigned int)input_buffer_list.mNumberBuffers,
    500         (unsigned int)input_buffer_list.mBuffers[0].mDataByteSize,
    501         (unsigned int)input_buffer_list.mBuffers[0].mNumberChannels,
    502         (unsigned int)input_frames,
    503         stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
    504 
    505   return noErr;
    506 }
    507 
    508 static OSStatus
    509 audiounit_input_callback(void * user_ptr, AudioUnitRenderActionFlags * flags,
    510                          AudioTimeStamp const * tstamp, UInt32 bus,
    511                          UInt32 input_frames, AudioBufferList * /* bufs */)
    512 {
    513   cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
    514 
    515   assert(stm->input_unit != NULL);
    516   assert(AU_IN_BUS == bus);
    517 
    518   if (stm->shutdown) {
    519     ALOG("(%p) input shutdown", stm);
    520     return noErr;
    521   }
    522 
    523   if (stm->draining) {
    524     OSStatus r = AudioOutputUnitStop(stm->input_unit);
    525     assert(r == 0);
    526     // Only fire state callback in input-only stream. For duplex stream,
    527     // the state callback will be fired in output callback.
    528     if (stm->output_unit == NULL) {
    529       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
    530     }
    531     return noErr;
    532   }
    533 
    534   OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames);
    535   if (r != noErr) {
    536     return r;
    537   }
    538 
    539   // Full Duplex. We'll call data_callback in the AudioUnit output callback.
    540   if (stm->output_unit != NULL) {
    541     return noErr;
    542   }
    543 
    544   /* Input only. Call the user callback through resampler.
    545      Resampler will deliver input buffer in the correct rate. */
    546   assert(input_frames <= stm->input_linear_buffer->length() /
    547                              stm->input_desc.mChannelsPerFrame);
    548   long total_input_frames =
    549       stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
    550   long outframes = cubeb_resampler_fill(stm->resampler.get(),
    551                                         stm->input_linear_buffer->data(),
    552                                         &total_input_frames, NULL, 0);
    553   if (outframes < 0) {
    554     stm->shutdown = true;
    555     OSStatus r = AudioOutputUnitStop(stm->input_unit);
    556     assert(r == 0);
    557     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
    558     return noErr;
    559   }
    560   stm->draining = outframes < total_input_frames;
    561 
    562   // Reset input buffer
    563   stm->input_linear_buffer->clear();
    564 
    565   return noErr;
    566 }
    567 
    568 static void
    569 audiounit_mix_output_buffer(cubeb_stream * stm, size_t output_frames,
    570                             void * input_buffer, size_t input_buffer_size,
    571                             void * output_buffer, size_t output_buffer_size)
    572 {
    573   assert(input_buffer_size >=
    574          cubeb_sample_size(stm->output_stream_params.format) *
    575              stm->output_stream_params.channels * output_frames);
    576   assert(output_buffer_size >= stm->output_desc.mBytesPerFrame * output_frames);
    577 
    578   int r = cubeb_mixer_mix(stm->mixer.get(), output_frames, input_buffer,
    579                           input_buffer_size, output_buffer, output_buffer_size);
    580   if (r != 0) {
    581     LOG("Remix error = %d", r);
    582   }
    583 }
    584 
    585 // Return how many input frames (sampled at input_hw_rate) are needed to provide
    586 // output_frames (sampled at output_stream_params.rate)
    587 static int64_t
    588 minimum_resampling_input_frames(cubeb_stream * stm, uint32_t output_frames)
    589 {
    590   if (stm->input_hw_rate == stm->output_stream_params.rate) {
    591     // Fast path.
    592     return output_frames;
    593   }
    594   return ceil(stm->input_hw_rate * output_frames /
    595               stm->output_stream_params.rate);
    596 }
    597 
    598 static OSStatus
    599 audiounit_output_callback(void * user_ptr,
    600                           AudioUnitRenderActionFlags * /* flags */,
    601                           AudioTimeStamp const * tstamp, UInt32 bus,
    602                           UInt32 output_frames, AudioBufferList * outBufferList)
    603 {
    604   assert(AU_OUT_BUS == bus);
    605   assert(outBufferList->mNumberBuffers == 1);
    606 
    607   cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
    608 
    609   uint64_t now = ConvertHostTimeToNanos(mach_absolute_time());
    610   uint64_t audio_output_time = ConvertHostTimeToNanos(tstamp->mHostTime);
    611   uint64_t output_latency_ns = audio_output_time - now;
    612 
    613   const int ns2s = 1e9;
    614   // The total output latency is the timestamp difference + the stream latency +
    615   // the hardware latency.
    616   stm->total_output_latency_frames =
    617       output_latency_ns * stm->output_hw_rate / ns2s +
    618       stm->current_latency_frames;
    619 
    620   ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input "
    621         "frames %lu.",
    622         stm, (unsigned int)outBufferList->mNumberBuffers,
    623         (unsigned int)outBufferList->mBuffers[0].mDataByteSize,
    624         (unsigned int)outBufferList->mBuffers[0].mNumberChannels,
    625         (unsigned int)output_frames,
    626         has_input(stm) ? stm->input_linear_buffer->length() /
    627                              stm->input_desc.mChannelsPerFrame
    628                        : 0);
    629 
    630   long input_frames = 0;
    631   void *output_buffer = NULL, *input_buffer = NULL;
    632 
    633   if (stm->shutdown) {
    634     ALOG("(%p) output shutdown.", stm);
    635     audiounit_make_silent(&outBufferList->mBuffers[0]);
    636     return noErr;
    637   }
    638 
    639   if (stm->draining) {
    640     OSStatus r = AudioOutputUnitStop(stm->output_unit);
    641     assert(r == 0);
    642     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
    643     audiounit_make_silent(&outBufferList->mBuffers[0]);
    644     return noErr;
    645   }
    646 
    647   /* Get output buffer. */
    648   if (stm->mixer) {
    649     // If remixing needs to occur, we can't directly work in our final
    650     // destination buffer as data may be overwritten or too small to start with.
    651     size_t size_needed = output_frames * stm->output_stream_params.channels *
    652                          cubeb_sample_size(stm->output_stream_params.format);
    653     if (stm->temp_buffer_size < size_needed) {
    654       stm->temp_buffer.reset(new uint8_t[size_needed]);
    655       stm->temp_buffer_size = size_needed;
    656     }
    657     output_buffer = stm->temp_buffer.get();
    658   } else {
    659     output_buffer = outBufferList->mBuffers[0].mData;
    660   }
    661 
    662   stm->frames_written += output_frames;
    663 
    664   /* If Full duplex get also input buffer */
    665   if (stm->input_unit != NULL) {
    666     /* If the output callback came first and this is a duplex stream, we need to
    667      * fill in some additional silence in the resampler.
    668      * Otherwise, if we had more than expected callbacks in a row, or we're
    669      * currently switching, we add some silence as well to compensate for the
    670      * fact that we're lacking some input data. */
    671     uint32_t input_frames_needed =
    672         minimum_resampling_input_frames(stm, stm->frames_written);
    673     long missing_frames = input_frames_needed - stm->frames_read;
    674     if (missing_frames > 0) {
    675       stm->input_linear_buffer->push_silence(missing_frames *
    676                                              stm->input_desc.mChannelsPerFrame);
    677       stm->frames_read = input_frames_needed;
    678 
    679       ALOG("(%p) %s pushed %ld frames of input silence.", stm,
    680            stm->frames_read == 0   ? "Input hasn't started,"
    681            : stm->switching_device ? "Device switching,"
    682                                    : "Drop out,",
    683            missing_frames);
    684     }
    685     input_buffer = stm->input_linear_buffer->data();
    686     // Number of input frames in the buffer. It will change to actually used
    687     // frames inside fill
    688     input_frames =
    689         stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
    690   }
    691 
    692   /* Call user callback through resampler. */
    693   long outframes = cubeb_resampler_fill(stm->resampler.get(), input_buffer,
    694                                         input_buffer ? &input_frames : NULL,
    695                                         output_buffer, output_frames);
    696 
    697   if (input_buffer) {
    698     // Pop from the buffer the frames used by the the resampler.
    699     stm->input_linear_buffer->pop(input_frames *
    700                                   stm->input_desc.mChannelsPerFrame);
    701   }
    702 
    703   if (outframes < 0 || outframes > output_frames) {
    704     stm->shutdown = true;
    705     OSStatus r = AudioOutputUnitStop(stm->output_unit);
    706     assert(r == 0);
    707     if (stm->input_unit) {
    708       r = AudioOutputUnitStop(stm->input_unit);
    709       assert(r == 0);
    710     }
    711     stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
    712     audiounit_make_silent(&outBufferList->mBuffers[0]);
    713     return noErr;
    714   }
    715 
    716   stm->draining = (UInt32)outframes < output_frames;
    717   stm->frames_played = stm->frames_queued;
    718   stm->frames_queued += outframes;
    719 
    720   /* Post process output samples. */
    721   if (stm->draining) {
    722     /* Clear missing frames (silence) */
    723     size_t channels = stm->output_stream_params.channels;
    724     size_t missing_samples = (output_frames - outframes) * channels;
    725     size_t size_sample = cubeb_sample_size(stm->output_stream_params.format);
    726     /* number of bytes that have been filled with valid audio by the callback.
    727      */
    728     size_t audio_byte_count = outframes * channels * size_sample;
    729     PodZero((uint8_t *)output_buffer + audio_byte_count,
    730             missing_samples * size_sample);
    731   }
    732 
    733   /* Mixing */
    734   if (stm->mixer) {
    735     audiounit_mix_output_buffer(stm, output_frames, output_buffer,
    736                                 stm->temp_buffer_size,
    737                                 outBufferList->mBuffers[0].mData,
    738                                 outBufferList->mBuffers[0].mDataByteSize);
    739   }
    740 
    741   return noErr;
    742 }
    743 
    744 extern "C" {
    745 int
    746 audiounit_init(cubeb ** context, char const * /* context_name */)
    747 {
    748 #if !TARGET_OS_IPHONE
    749   cubeb_set_coreaudio_notification_runloop();
    750 #endif
    751 
    752   *context = new cubeb;
    753 
    754   return CUBEB_OK;
    755 }
    756 }
    757 
    758 static char const *
    759 audiounit_get_backend_id(cubeb * /* ctx */)
    760 {
    761   return "audiounit";
    762 }
    763 
    764 #if !TARGET_OS_IPHONE
    765 
    766 static int
    767 audiounit_stream_get_volume(cubeb_stream * stm, float * volume);
    768 static int
    769 audiounit_stream_set_volume(cubeb_stream * stm, float volume);
    770 
    771 static int
    772 audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side)
    773 {
    774   assert(stm);
    775 
    776   device_info * info = nullptr;
    777   cubeb_device_type type = CUBEB_DEVICE_TYPE_UNKNOWN;
    778 
    779   if (side == io_side::INPUT) {
    780     info = &stm->input_device;
    781     type = CUBEB_DEVICE_TYPE_INPUT;
    782   } else if (side == io_side::OUTPUT) {
    783     info = &stm->output_device;
    784     type = CUBEB_DEVICE_TYPE_OUTPUT;
    785   }
    786   memset(info, 0, sizeof(device_info));
    787   info->id = id;
    788 
    789   if (side == io_side::INPUT) {
    790     info->flags |= DEV_INPUT;
    791   } else if (side == io_side::OUTPUT) {
    792     info->flags |= DEV_OUTPUT;
    793   }
    794 
    795   AudioDeviceID default_device_id = audiounit_get_default_device_id(type);
    796   if (default_device_id == kAudioObjectUnknown) {
    797     return CUBEB_ERROR;
    798   }
    799   if (id == kAudioObjectUnknown) {
    800     info->id = default_device_id;
    801     info->flags |= DEV_SELECTED_DEFAULT;
    802   }
    803 
    804   if (info->id == default_device_id) {
    805     info->flags |= DEV_SYSTEM_DEFAULT;
    806   }
    807 
    808   assert(info->id);
    809   assert(info->flags & DEV_INPUT && !(info->flags & DEV_OUTPUT) ||
    810          !(info->flags & DEV_INPUT) && info->flags & DEV_OUTPUT);
    811 
    812   return CUBEB_OK;
    813 }
    814 
    815 static int
    816 audiounit_reinit_stream(cubeb_stream * stm, device_flags_value flags)
    817 {
    818   auto_lock context_lock(stm->context->mutex);
    819   assert((flags & DEV_INPUT && stm->input_unit) ||
    820          (flags & DEV_OUTPUT && stm->output_unit));
    821   if (!stm->shutdown) {
    822     audiounit_stream_stop_internal(stm);
    823   }
    824 
    825   int r = audiounit_uninstall_device_changed_callback(stm);
    826   if (r != CUBEB_OK) {
    827     LOG("(%p) Could not uninstall all device change listeners.", stm);
    828   }
    829 
    830   {
    831     auto_lock lock(stm->mutex);
    832     float volume = 0.0;
    833     int vol_rv = CUBEB_ERROR;
    834     if (stm->output_unit) {
    835       vol_rv = audiounit_stream_get_volume(stm, &volume);
    836     }
    837 
    838     audiounit_close_stream(stm);
    839 
    840     /* Reinit occurs in one of the following case:
    841      * - When the device is not alive any more
    842      * - When the default system device change.
    843      * - The bluetooth device changed from A2DP to/from HFP/HSP profile
    844      * We first attempt to re-use the same device id, should that fail we will
    845      * default to the (potentially new) default device. */
    846     AudioDeviceID input_device =
    847         flags & DEV_INPUT ? stm->input_device.id : kAudioObjectUnknown;
    848     if (flags & DEV_INPUT) {
    849       r = audiounit_set_device_info(stm, input_device, io_side::INPUT);
    850       if (r != CUBEB_OK) {
    851         LOG("(%p) Set input device info failed. This can happen when last "
    852             "media device is unplugged",
    853             stm);
    854         return CUBEB_ERROR;
    855       }
    856     }
    857 
    858     /* Always use the default output on reinit. This is not correct in every
    859      * case but it is sufficient for Firefox and prevent reinit from reporting
    860      * failures. It will change soon when reinit mechanism will be updated. */
    861     r = audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::OUTPUT);
    862     if (r != CUBEB_OK) {
    863       LOG("(%p) Set output device info failed. This can happen when last media "
    864           "device is unplugged",
    865           stm);
    866       return CUBEB_ERROR;
    867     }
    868 
    869     if (audiounit_setup_stream(stm) != CUBEB_OK) {
    870       LOG("(%p) Stream reinit failed.", stm);
    871       if (flags & DEV_INPUT && input_device != kAudioObjectUnknown) {
    872         // Attempt to re-use the same device-id failed, so attempt again with
    873         // default input device.
    874         audiounit_close_stream(stm);
    875         if (audiounit_set_device_info(stm, kAudioObjectUnknown,
    876                                       io_side::INPUT) != CUBEB_OK ||
    877             audiounit_setup_stream(stm) != CUBEB_OK) {
    878           LOG("(%p) Second stream reinit failed.", stm);
    879           return CUBEB_ERROR;
    880         }
    881       }
    882     }
    883 
    884     if (vol_rv == CUBEB_OK) {
    885       audiounit_stream_set_volume(stm, volume);
    886     }
    887 
    888     // If the stream was running, start it again.
    889     if (!stm->shutdown) {
    890       r = audiounit_stream_start_internal(stm);
    891       if (r != CUBEB_OK) {
    892         return CUBEB_ERROR;
    893       }
    894     }
    895   }
    896   return CUBEB_OK;
    897 }
    898 
    899 static void
    900 audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags)
    901 {
    902   if (std::atomic_exchange(&stm->reinit_pending, true)) {
    903     // A reinit task is already pending, nothing more to do.
    904     ALOG("(%p) re-init stream task already pending, cancelling request", stm);
    905     return;
    906   }
    907 
    908   // Use a new thread, through the queue, to avoid deadlock when calling
    909   // Get/SetProperties method from inside notify callback
    910   dispatch_async(stm->context->serial_queue, ^() {
    911     if (stm->destroy_pending) {
    912       ALOG("(%p) stream pending destroy, cancelling reinit task", stm);
    913       return;
    914     }
    915 
    916     if (audiounit_reinit_stream(stm, flags) != CUBEB_OK) {
    917       if (audiounit_uninstall_system_changed_callback(stm) != CUBEB_OK) {
    918         LOG("(%p) Could not uninstall system changed callback", stm);
    919       }
    920       stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
    921       LOG("(%p) Could not reopen the stream after switching.", stm);
    922     }
    923     stm->switching_device = false;
    924     stm->reinit_pending = false;
    925   });
    926 }
    927 
    928 static char const *
    929 event_addr_to_string(AudioObjectPropertySelector selector)
    930 {
    931   switch (selector) {
    932   case kAudioHardwarePropertyDefaultOutputDevice:
    933     return "kAudioHardwarePropertyDefaultOutputDevice";
    934   case kAudioHardwarePropertyDefaultInputDevice:
    935     return "kAudioHardwarePropertyDefaultInputDevice";
    936   case kAudioDevicePropertyDeviceIsAlive:
    937     return "kAudioDevicePropertyDeviceIsAlive";
    938   case kAudioDevicePropertyDataSource:
    939     return "kAudioDevicePropertyDataSource";
    940   default:
    941     return "Unknown";
    942   }
    943 }
    944 
    945 static OSStatus
    946 audiounit_property_listener_callback(
    947     AudioObjectID id, UInt32 address_count,
    948     const AudioObjectPropertyAddress * addresses, void * user)
    949 {
    950   cubeb_stream * stm = (cubeb_stream *)user;
    951   if (stm->switching_device) {
    952     LOG("Switching is already taking place. Skip Event %s for id=%d",
    953         event_addr_to_string(addresses[0].mSelector), id);
    954     return noErr;
    955   }
    956   stm->switching_device = true;
    957 
    958   LOG("(%p) Audio device changed, %u events.", stm,
    959       (unsigned int)address_count);
    960   for (UInt32 i = 0; i < address_count; i++) {
    961     switch (addresses[i].mSelector) {
    962     case kAudioHardwarePropertyDefaultOutputDevice: {
    963       LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultOutputDevice "
    964           "for id=%d",
    965           (unsigned int)i, id);
    966     } break;
    967     case kAudioHardwarePropertyDefaultInputDevice: {
    968       LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultInputDevice "
    969           "for id=%d",
    970           (unsigned int)i, id);
    971     } break;
    972     case kAudioDevicePropertyDeviceIsAlive: {
    973       LOG("Event[%u] - mSelector == kAudioDevicePropertyDeviceIsAlive for "
    974           "id=%d",
    975           (unsigned int)i, id);
    976       // If this is the default input device ignore the event,
    977       // kAudioHardwarePropertyDefaultInputDevice will take care of the switch
    978       if (stm->input_device.flags & DEV_SYSTEM_DEFAULT) {
    979         LOG("It's the default input device, ignore the event");
    980         stm->switching_device = false;
    981         return noErr;
    982       }
    983     } break;
    984     case kAudioDevicePropertyDataSource: {
    985       LOG("Event[%u] - mSelector == kAudioDevicePropertyDataSource for id=%d",
    986           (unsigned int)i, id);
    987     } break;
    988     default:
    989       LOG("Event[%u] - mSelector == Unexpected Event id %d, return",
    990           (unsigned int)i, addresses[i].mSelector);
    991       stm->switching_device = false;
    992       return noErr;
    993     }
    994   }
    995 
    996   // Allow restart to choose the new default
    997   device_flags_value switch_side = DEV_UNKNOWN;
    998   if (has_input(stm)) {
    999     switch_side |= DEV_INPUT;
   1000   }
   1001   if (has_output(stm)) {
   1002     switch_side |= DEV_OUTPUT;
   1003   }
   1004 
   1005   for (UInt32 i = 0; i < address_count; i++) {
   1006     switch (addresses[i].mSelector) {
   1007     case kAudioHardwarePropertyDefaultOutputDevice:
   1008     case kAudioHardwarePropertyDefaultInputDevice:
   1009     case kAudioDevicePropertyDeviceIsAlive:
   1010       /* fall through */
   1011     case kAudioDevicePropertyDataSource: {
   1012       auto_lock dev_cb_lock(stm->device_changed_callback_lock);
   1013       if (stm->device_changed_callback) {
   1014         stm->device_changed_callback(stm->user_ptr);
   1015       }
   1016       break;
   1017     }
   1018     }
   1019   }
   1020 
   1021   audiounit_reinit_stream_async(stm, switch_side);
   1022 
   1023   return noErr;
   1024 }
   1025 
   1026 OSStatus
   1027 audiounit_add_listener(const property_listener * listener)
   1028 {
   1029   assert(listener);
   1030   return AudioObjectAddPropertyListener(listener->device_id,
   1031                                         listener->property_address,
   1032                                         listener->callback, listener->stream);
   1033 }
   1034 
   1035 OSStatus
   1036 audiounit_remove_listener(const property_listener * listener)
   1037 {
   1038   assert(listener);
   1039   return AudioObjectRemovePropertyListener(
   1040       listener->device_id, listener->property_address, listener->callback,
   1041       listener->stream);
   1042 }
   1043 
   1044 static int
   1045 audiounit_install_device_changed_callback(cubeb_stream * stm)
   1046 {
   1047   OSStatus rv;
   1048   int r = CUBEB_OK;
   1049 
   1050   if (stm->output_unit) {
   1051     /* This event will notify us when the data source on the same device
   1052      * changes, for example when the user plugs in a normal (non-usb) headset in
   1053      * the headphone jack. */
   1054     stm->output_source_listener.reset(new property_listener(
   1055         stm->output_device.id, &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS,
   1056         &audiounit_property_listener_callback, stm));
   1057     rv = audiounit_add_listener(stm->output_source_listener.get());
   1058     if (rv != noErr) {
   1059       stm->output_source_listener.reset();
   1060       LOG("AudioObjectAddPropertyListener/output/"
   1061           "kAudioDevicePropertyDataSource rv=%d, device id=%d",
   1062           rv, stm->output_device.id);
   1063       r = CUBEB_ERROR;
   1064     }
   1065   }
   1066 
   1067   if (stm->input_unit) {
   1068     /* This event will notify us when the data source on the input device
   1069      * changes. */
   1070     stm->input_source_listener.reset(new property_listener(
   1071         stm->input_device.id, &INPUT_DATA_SOURCE_PROPERTY_ADDRESS,
   1072         &audiounit_property_listener_callback, stm));
   1073     rv = audiounit_add_listener(stm->input_source_listener.get());
   1074     if (rv != noErr) {
   1075       stm->input_source_listener.reset();
   1076       LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDataSource "
   1077           "rv=%d, device id=%d",
   1078           rv, stm->input_device.id);
   1079       r = CUBEB_ERROR;
   1080     }
   1081 
   1082     /* Event to notify when the input is going away. */
   1083     stm->input_alive_listener.reset(new property_listener(
   1084         stm->input_device.id, &DEVICE_IS_ALIVE_PROPERTY_ADDRESS,
   1085         &audiounit_property_listener_callback, stm));
   1086     rv = audiounit_add_listener(stm->input_alive_listener.get());
   1087     if (rv != noErr) {
   1088       stm->input_alive_listener.reset();
   1089       LOG("AudioObjectAddPropertyListener/input/"
   1090           "kAudioDevicePropertyDeviceIsAlive rv=%d, device id =%d",
   1091           rv, stm->input_device.id);
   1092       r = CUBEB_ERROR;
   1093     }
   1094   }
   1095 
   1096   return r;
   1097 }
   1098 
   1099 static int
   1100 audiounit_install_system_changed_callback(cubeb_stream * stm)
   1101 {
   1102   OSStatus r;
   1103 
   1104   if (stm->output_unit) {
   1105     /* This event will notify us when the default audio device changes,
   1106      * for example when the user plugs in a USB headset and the system chooses
   1107      * it automatically as the default, or when another device is chosen in the
   1108      * dropdown list. */
   1109     stm->default_output_listener.reset(new property_listener(
   1110         kAudioObjectSystemObject, &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS,
   1111         &audiounit_property_listener_callback, stm));
   1112     r = audiounit_add_listener(stm->default_output_listener.get());
   1113     if (r != noErr) {
   1114       stm->default_output_listener.reset();
   1115       LOG("AudioObjectAddPropertyListener/output/"
   1116           "kAudioHardwarePropertyDefaultOutputDevice rv=%d",
   1117           r);
   1118       return CUBEB_ERROR;
   1119     }
   1120   }
   1121 
   1122   if (stm->input_unit) {
   1123     /* This event will notify us when the default input device changes. */
   1124     stm->default_input_listener.reset(new property_listener(
   1125         kAudioObjectSystemObject, &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS,
   1126         &audiounit_property_listener_callback, stm));
   1127     r = audiounit_add_listener(stm->default_input_listener.get());
   1128     if (r != noErr) {
   1129       stm->default_input_listener.reset();
   1130       LOG("AudioObjectAddPropertyListener/input/"
   1131           "kAudioHardwarePropertyDefaultInputDevice rv=%d",
   1132           r);
   1133       return CUBEB_ERROR;
   1134     }
   1135   }
   1136 
   1137   return CUBEB_OK;
   1138 }
   1139 
   1140 static int
   1141 audiounit_uninstall_device_changed_callback(cubeb_stream * stm)
   1142 {
   1143   OSStatus rv;
   1144   // Failing to uninstall listeners is not a fatal error.
   1145   int r = CUBEB_OK;
   1146 
   1147   if (stm->output_source_listener) {
   1148     rv = audiounit_remove_listener(stm->output_source_listener.get());
   1149     if (rv != noErr) {
   1150       LOG("AudioObjectRemovePropertyListener/output/"
   1151           "kAudioDevicePropertyDataSource rv=%d, device id=%d",
   1152           rv, stm->output_device.id);
   1153       r = CUBEB_ERROR;
   1154     }
   1155     stm->output_source_listener.reset();
   1156   }
   1157 
   1158   if (stm->input_source_listener) {
   1159     rv = audiounit_remove_listener(stm->input_source_listener.get());
   1160     if (rv != noErr) {
   1161       LOG("AudioObjectRemovePropertyListener/input/"
   1162           "kAudioDevicePropertyDataSource rv=%d, device id=%d",
   1163           rv, stm->input_device.id);
   1164       r = CUBEB_ERROR;
   1165     }
   1166     stm->input_source_listener.reset();
   1167   }
   1168 
   1169   if (stm->input_alive_listener) {
   1170     rv = audiounit_remove_listener(stm->input_alive_listener.get());
   1171     if (rv != noErr) {
   1172       LOG("AudioObjectRemovePropertyListener/input/"
   1173           "kAudioDevicePropertyDeviceIsAlive rv=%d, device id=%d",
   1174           rv, stm->input_device.id);
   1175       r = CUBEB_ERROR;
   1176     }
   1177     stm->input_alive_listener.reset();
   1178   }
   1179 
   1180   return r;
   1181 }
   1182 
   1183 static int
   1184 audiounit_uninstall_system_changed_callback(cubeb_stream * stm)
   1185 {
   1186   OSStatus r;
   1187 
   1188   if (stm->default_output_listener) {
   1189     r = audiounit_remove_listener(stm->default_output_listener.get());
   1190     if (r != noErr) {
   1191       return CUBEB_ERROR;
   1192     }
   1193     stm->default_output_listener.reset();
   1194   }
   1195 
   1196   if (stm->default_input_listener) {
   1197     r = audiounit_remove_listener(stm->default_input_listener.get());
   1198     if (r != noErr) {
   1199       return CUBEB_ERROR;
   1200     }
   1201     stm->default_input_listener.reset();
   1202   }
   1203   return CUBEB_OK;
   1204 }
   1205 
   1206 /* Get the acceptable buffer size (in frames) that this device can work with. */
   1207 static int
   1208 audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
   1209 {
   1210   UInt32 size;
   1211   OSStatus r;
   1212   AudioDeviceID output_device_id;
   1213   AudioObjectPropertyAddress output_device_buffer_size_range = {
   1214       kAudioDevicePropertyBufferFrameSizeRange, kAudioDevicePropertyScopeOutput,
   1215       kAudioObjectPropertyElementMaster};
   1216 
   1217   output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
   1218   if (output_device_id == kAudioObjectUnknown) {
   1219     LOG("Could not get default output device id.");
   1220     return CUBEB_ERROR;
   1221   }
   1222 
   1223   /* Get the buffer size range this device supports */
   1224   size = sizeof(*latency_range);
   1225 
   1226   r = AudioObjectGetPropertyData(output_device_id,
   1227                                  &output_device_buffer_size_range, 0, NULL,
   1228                                  &size, latency_range);
   1229   if (r != noErr) {
   1230     LOG("AudioObjectGetPropertyData/buffer size range rv=%d", r);
   1231     return CUBEB_ERROR;
   1232   }
   1233 
   1234   return CUBEB_OK;
   1235 }
   1236 #endif /* !TARGET_OS_IPHONE */
   1237 
   1238 static AudioObjectID
   1239 audiounit_get_default_device_id(cubeb_device_type type)
   1240 {
   1241   const AudioObjectPropertyAddress * adr;
   1242   if (type == CUBEB_DEVICE_TYPE_OUTPUT) {
   1243     adr = &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS;
   1244   } else if (type == CUBEB_DEVICE_TYPE_INPUT) {
   1245     adr = &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS;
   1246   } else {
   1247     return kAudioObjectUnknown;
   1248   }
   1249 
   1250   AudioDeviceID devid;
   1251   UInt32 size = sizeof(AudioDeviceID);
   1252   if (AudioObjectGetPropertyData(kAudioObjectSystemObject, adr, 0, NULL, &size,
   1253                                  &devid) != noErr) {
   1254     return kAudioObjectUnknown;
   1255   }
   1256 
   1257   return devid;
   1258 }
   1259 
   1260 int
   1261 audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
   1262 {
   1263 #if TARGET_OS_IPHONE
   1264   // TODO: [[AVAudioSession sharedInstance] maximumOutputNumberOfChannels]
   1265   *max_channels = 2;
   1266 #else
   1267   UInt32 size;
   1268   OSStatus r;
   1269   AudioDeviceID output_device_id;
   1270   AudioStreamBasicDescription stream_format;
   1271   AudioObjectPropertyAddress stream_format_address = {
   1272       kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput,
   1273       kAudioObjectPropertyElementMaster};
   1274 
   1275   assert(ctx && max_channels);
   1276 
   1277   output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
   1278   if (output_device_id == kAudioObjectUnknown) {
   1279     return CUBEB_ERROR;
   1280   }
   1281 
   1282   size = sizeof(stream_format);
   1283 
   1284   r = AudioObjectGetPropertyData(output_device_id, &stream_format_address, 0,
   1285                                  NULL, &size, &stream_format);
   1286   if (r != noErr) {
   1287     LOG("AudioObjectPropertyAddress/StreamFormat rv=%d", r);
   1288     return CUBEB_ERROR;
   1289   }
   1290 
   1291   *max_channels = stream_format.mChannelsPerFrame;
   1292 #endif
   1293   return CUBEB_OK;
   1294 }
   1295 
   1296 static int
   1297 audiounit_get_min_latency(cubeb * /* ctx */, cubeb_stream_params /* params */,
   1298                           uint32_t * latency_frames)
   1299 {
   1300 #if TARGET_OS_IPHONE
   1301   // TODO: [[AVAudioSession sharedInstance] inputLatency]
   1302   return CUBEB_ERROR_NOT_SUPPORTED;
   1303 #else
   1304   AudioValueRange latency_range;
   1305   if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
   1306     LOG("Could not get acceptable latency range.");
   1307     return CUBEB_ERROR;
   1308   }
   1309 
   1310   *latency_frames =
   1311       max<uint32_t>(latency_range.mMinimum, SAFE_MIN_LATENCY_FRAMES);
   1312 #endif
   1313 
   1314   return CUBEB_OK;
   1315 }
   1316 
   1317 static int
   1318 audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate)
   1319 {
   1320 #if TARGET_OS_IPHONE
   1321   // TODO
   1322   return CUBEB_ERROR_NOT_SUPPORTED;
   1323 #else
   1324   UInt32 size;
   1325   OSStatus r;
   1326   Float64 fsamplerate;
   1327   AudioDeviceID output_device_id;
   1328   AudioObjectPropertyAddress samplerate_address = {
   1329       kAudioDevicePropertyNominalSampleRate, kAudioObjectPropertyScopeGlobal,
   1330       kAudioObjectPropertyElementMaster};
   1331 
   1332   output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
   1333   if (output_device_id == kAudioObjectUnknown) {
   1334     return CUBEB_ERROR;
   1335   }
   1336 
   1337   size = sizeof(fsamplerate);
   1338   r = AudioObjectGetPropertyData(output_device_id, &samplerate_address, 0, NULL,
   1339                                  &size, &fsamplerate);
   1340 
   1341   if (r != noErr) {
   1342     return CUBEB_ERROR;
   1343   }
   1344 
   1345   *rate = static_cast<uint32_t>(fsamplerate);
   1346 #endif
   1347   return CUBEB_OK;
   1348 }
   1349 
   1350 static cubeb_channel_layout
   1351 audiounit_convert_channel_layout(AudioChannelLayout * layout)
   1352 {
   1353   // When having one or two channel, force mono or stereo. Some devices (namely,
   1354   // Bose QC35, mark 1 and 2), expose a single channel mapped to the right for
   1355   // some reason.
   1356   if (layout->mNumberChannelDescriptions == 1) {
   1357     return CUBEB_LAYOUT_MONO;
   1358   } else if (layout->mNumberChannelDescriptions == 2) {
   1359     return CUBEB_LAYOUT_STEREO;
   1360   }
   1361 
   1362   if (layout->mChannelLayoutTag !=
   1363       kAudioChannelLayoutTag_UseChannelDescriptions) {
   1364     // kAudioChannelLayoutTag_UseChannelBitmap
   1365     // kAudioChannelLayoutTag_Mono
   1366     // kAudioChannelLayoutTag_Stereo
   1367     // ....
   1368     LOG("Only handle UseChannelDescriptions for now.\n");
   1369     return CUBEB_LAYOUT_UNDEFINED;
   1370   }
   1371 
   1372   cubeb_channel_layout cl = 0;
   1373   for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) {
   1374     cubeb_channel cc = channel_label_to_cubeb_channel(
   1375         layout->mChannelDescriptions[i].mChannelLabel);
   1376     if (cc == CHANNEL_UNKNOWN) {
   1377       return CUBEB_LAYOUT_UNDEFINED;
   1378     }
   1379     cl |= cc;
   1380   }
   1381 
   1382   return cl;
   1383 }
   1384 
   1385 static cubeb_channel_layout
   1386 audiounit_get_preferred_channel_layout(AudioUnit output_unit)
   1387 {
   1388   OSStatus rv = noErr;
   1389   UInt32 size = 0;
   1390   rv = AudioUnitGetPropertyInfo(
   1391       output_unit, kAudioDevicePropertyPreferredChannelLayout,
   1392       kAudioUnitScope_Output, AU_OUT_BUS, &size, nullptr);
   1393   if (rv != noErr) {
   1394     LOG("AudioUnitGetPropertyInfo/kAudioDevicePropertyPreferredChannelLayout "
   1395         "rv=%d",
   1396         rv);
   1397     return CUBEB_LAYOUT_UNDEFINED;
   1398   }
   1399   assert(size > 0);
   1400 
   1401   auto layout = make_sized_audio_channel_layout(size);
   1402   rv = AudioUnitGetProperty(
   1403       output_unit, kAudioDevicePropertyPreferredChannelLayout,
   1404       kAudioUnitScope_Output, AU_OUT_BUS, layout.get(), &size);
   1405   if (rv != noErr) {
   1406     LOG("AudioUnitGetProperty/kAudioDevicePropertyPreferredChannelLayout rv=%d",
   1407         rv);
   1408     return CUBEB_LAYOUT_UNDEFINED;
   1409   }
   1410 
   1411   return audiounit_convert_channel_layout(layout.get());
   1412 }
   1413 
   1414 static cubeb_channel_layout
   1415 audiounit_get_current_channel_layout(AudioUnit output_unit)
   1416 {
   1417   OSStatus rv = noErr;
   1418   UInt32 size = 0;
   1419   rv = AudioUnitGetPropertyInfo(
   1420       output_unit, kAudioUnitProperty_AudioChannelLayout,
   1421       kAudioUnitScope_Output, AU_OUT_BUS, &size, nullptr);
   1422   if (rv != noErr) {
   1423     LOG("AudioUnitGetPropertyInfo/kAudioUnitProperty_AudioChannelLayout rv=%d",
   1424         rv);
   1425     // This property isn't known before macOS 10.12, attempt another method.
   1426     return audiounit_get_preferred_channel_layout(output_unit);
   1427   }
   1428   assert(size > 0);
   1429 
   1430   auto layout = make_sized_audio_channel_layout(size);
   1431   rv = AudioUnitGetProperty(output_unit, kAudioUnitProperty_AudioChannelLayout,
   1432                             kAudioUnitScope_Output, AU_OUT_BUS, layout.get(),
   1433                             &size);
   1434   if (rv != noErr) {
   1435     LOG("AudioUnitGetProperty/kAudioUnitProperty_AudioChannelLayout rv=%d", rv);
   1436     return CUBEB_LAYOUT_UNDEFINED;
   1437   }
   1438 
   1439   return audiounit_convert_channel_layout(layout.get());
   1440 }
   1441 
   1442 static int
   1443 audiounit_create_unit(AudioUnit * unit, device_info * device);
   1444 
   1445 static OSStatus
   1446 audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype);
   1447 
   1448 static void
   1449 audiounit_destroy(cubeb * ctx)
   1450 {
   1451   {
   1452     auto_lock lock(ctx->mutex);
   1453 
   1454     // Disabling this assert for bug 1083664 -- we seem to leak a stream
   1455     // assert(ctx->active_streams == 0);
   1456     if (audiounit_active_streams(ctx) > 0) {
   1457       LOG("(%p) API misuse, %d streams active when context destroyed!", ctx,
   1458           audiounit_active_streams(ctx));
   1459     }
   1460 
   1461     // Destroying a cubeb context with device collection callbacks registered
   1462     // is misuse of the API, assert then attempt to clean up.
   1463     assert(!ctx->input_collection_changed_callback &&
   1464            !ctx->input_collection_changed_user_ptr &&
   1465            !ctx->output_collection_changed_callback &&
   1466            !ctx->output_collection_changed_user_ptr);
   1467 
   1468     /* Unregister the callback if necessary. */
   1469     if (ctx->input_collection_changed_callback) {
   1470       audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_INPUT);
   1471     }
   1472     if (ctx->output_collection_changed_callback) {
   1473       audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_OUTPUT);
   1474     }
   1475   }
   1476 
   1477   dispatch_release(ctx->serial_queue);
   1478 
   1479   delete ctx;
   1480 }
   1481 
   1482 static void
   1483 audiounit_stream_destroy(cubeb_stream * stm);
   1484 
   1485 static int
   1486 audio_stream_desc_init(AudioStreamBasicDescription * ss,
   1487                        const cubeb_stream_params * stream_params)
   1488 {
   1489   switch (stream_params->format) {
   1490   case CUBEB_SAMPLE_S16LE:
   1491     ss->mBitsPerChannel = 16;
   1492     ss->mFormatFlags = kAudioFormatFlagIsSignedInteger;
   1493     break;
   1494   case CUBEB_SAMPLE_S16BE:
   1495     ss->mBitsPerChannel = 16;
   1496     ss->mFormatFlags =
   1497         kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsBigEndian;
   1498     break;
   1499   case CUBEB_SAMPLE_FLOAT32LE:
   1500     ss->mBitsPerChannel = 32;
   1501     ss->mFormatFlags = kAudioFormatFlagIsFloat;
   1502     break;
   1503   case CUBEB_SAMPLE_FLOAT32BE:
   1504     ss->mBitsPerChannel = 32;
   1505     ss->mFormatFlags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsBigEndian;
   1506     break;
   1507   default:
   1508     return CUBEB_ERROR_INVALID_FORMAT;
   1509   }
   1510 
   1511   ss->mFormatID = kAudioFormatLinearPCM;
   1512   ss->mFormatFlags |= kLinearPCMFormatFlagIsPacked;
   1513   ss->mSampleRate = stream_params->rate;
   1514   ss->mChannelsPerFrame = stream_params->channels;
   1515 
   1516   ss->mBytesPerFrame = (ss->mBitsPerChannel / 8) * ss->mChannelsPerFrame;
   1517   ss->mFramesPerPacket = 1;
   1518   ss->mBytesPerPacket = ss->mBytesPerFrame * ss->mFramesPerPacket;
   1519 
   1520   ss->mReserved = 0;
   1521 
   1522   return CUBEB_OK;
   1523 }
   1524 
   1525 void
   1526 audiounit_init_mixer(cubeb_stream * stm)
   1527 {
   1528   // We can't rely on macOS' AudioUnit to properly downmix (or upmix) the audio
   1529   // data, it silently drop the channels so we need to remix the
   1530   // audio data by ourselves to keep all the information.
   1531   stm->mixer.reset(cubeb_mixer_create(
   1532       stm->output_stream_params.format, stm->output_stream_params.channels,
   1533       stm->output_stream_params.layout, stm->context->channels,
   1534       stm->context->layout));
   1535   assert(stm->mixer);
   1536 }
   1537 
   1538 static int
   1539 audiounit_set_channel_layout(AudioUnit unit, io_side side,
   1540                              cubeb_channel_layout layout)
   1541 {
   1542   if (side != io_side::OUTPUT) {
   1543     return CUBEB_ERROR;
   1544   }
   1545 
   1546   if (layout == CUBEB_LAYOUT_UNDEFINED) {
   1547     // We leave everything as-is...
   1548     return CUBEB_OK;
   1549   }
   1550 
   1551   OSStatus r;
   1552   uint32_t nb_channels = cubeb_channel_layout_nb_channels(layout);
   1553 
   1554   // We do not use CoreAudio standard layout for lack of documentation on what
   1555   // the actual channel orders are. So we set a custom layout.
   1556   size_t size = offsetof(AudioChannelLayout, mChannelDescriptions[nb_channels]);
   1557   auto au_layout = make_sized_audio_channel_layout(size);
   1558   au_layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
   1559   au_layout->mNumberChannelDescriptions = nb_channels;
   1560 
   1561   uint32_t channels = 0;
   1562   cubeb_channel_layout channelMap = layout;
   1563   for (uint32_t i = 0; channelMap != 0; ++i) {
   1564     XASSERT(channels < nb_channels);
   1565     uint32_t channel = (channelMap & 1) << i;
   1566     if (channel != 0) {
   1567       au_layout->mChannelDescriptions[channels].mChannelLabel =
   1568           cubeb_channel_to_channel_label(static_cast<cubeb_channel>(channel));
   1569       au_layout->mChannelDescriptions[channels].mChannelFlags =
   1570           kAudioChannelFlags_AllOff;
   1571       channels++;
   1572     }
   1573     channelMap = channelMap >> 1;
   1574   }
   1575 
   1576   r = AudioUnitSetProperty(unit, kAudioUnitProperty_AudioChannelLayout,
   1577                            kAudioUnitScope_Input, AU_OUT_BUS, au_layout.get(),
   1578                            size);
   1579   if (r != noErr) {
   1580     LOG("AudioUnitSetProperty/%s/kAudioUnitProperty_AudioChannelLayout rv=%d",
   1581         to_string(side), r);
   1582     return CUBEB_ERROR;
   1583   }
   1584 
   1585   return CUBEB_OK;
   1586 }
   1587 
   1588 void
   1589 audiounit_layout_init(cubeb_stream * stm, io_side side)
   1590 {
   1591   // We currently don't support the input layout setting.
   1592   if (side == io_side::INPUT) {
   1593     return;
   1594   }
   1595 
   1596   stm->context->layout = audiounit_get_current_channel_layout(stm->output_unit);
   1597 
   1598   audiounit_set_channel_layout(stm->output_unit, io_side::OUTPUT,
   1599                                stm->context->layout);
   1600 }
   1601 
   1602 static vector<AudioObjectID>
   1603 audiounit_get_sub_devices(AudioDeviceID device_id)
   1604 {
   1605   vector<AudioDeviceID> sub_devices;
   1606   AudioObjectPropertyAddress property_address = {
   1607       kAudioAggregateDevicePropertyActiveSubDeviceList,
   1608       kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
   1609   UInt32 size = 0;
   1610   OSStatus rv = AudioObjectGetPropertyDataSize(device_id, &property_address, 0,
   1611                                                nullptr, &size);
   1612 
   1613   if (rv != noErr) {
   1614     sub_devices.push_back(device_id);
   1615     return sub_devices;
   1616   }
   1617 
   1618   uint32_t count = static_cast<uint32_t>(size / sizeof(AudioObjectID));
   1619   sub_devices.resize(count);
   1620   rv = AudioObjectGetPropertyData(device_id, &property_address, 0, nullptr,
   1621                                   &size, sub_devices.data());
   1622   if (rv != noErr) {
   1623     sub_devices.clear();
   1624     sub_devices.push_back(device_id);
   1625   } else {
   1626     LOG("Found %u sub-devices", count);
   1627   }
   1628   return sub_devices;
   1629 }
   1630 
   1631 static int
   1632 audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id,
   1633                                         AudioDeviceID * aggregate_device_id)
   1634 {
   1635   AudioObjectPropertyAddress address_plugin_bundle_id = {
   1636       kAudioHardwarePropertyPlugInForBundleID, kAudioObjectPropertyScopeGlobal,
   1637       kAudioObjectPropertyElementMaster};
   1638   UInt32 size = 0;
   1639   OSStatus r = AudioObjectGetPropertyDataSize(
   1640       kAudioObjectSystemObject, &address_plugin_bundle_id, 0, NULL, &size);
   1641   if (r != noErr) {
   1642     LOG("AudioObjectGetPropertyDataSize/"
   1643         "kAudioHardwarePropertyPlugInForBundleID, rv=%d",
   1644         r);
   1645     return CUBEB_ERROR;
   1646   }
   1647 
   1648   AudioValueTranslation translation_value;
   1649   CFStringRef in_bundle_ref = CFSTR("com.apple.audio.CoreAudio");
   1650   translation_value.mInputData = &in_bundle_ref;
   1651   translation_value.mInputDataSize = sizeof(in_bundle_ref);
   1652   translation_value.mOutputData = plugin_id;
   1653   translation_value.mOutputDataSize = sizeof(*plugin_id);
   1654 
   1655   r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
   1656                                  &address_plugin_bundle_id, 0, nullptr, &size,
   1657                                  &translation_value);
   1658   if (r != noErr) {
   1659     LOG("AudioObjectGetPropertyData/kAudioHardwarePropertyPlugInForBundleID, "
   1660         "rv=%d",
   1661         r);
   1662     return CUBEB_ERROR;
   1663   }
   1664 
   1665   AudioObjectPropertyAddress create_aggregate_device_address = {
   1666       kAudioPlugInCreateAggregateDevice, kAudioObjectPropertyScopeGlobal,
   1667       kAudioObjectPropertyElementMaster};
   1668   r = AudioObjectGetPropertyDataSize(
   1669       *plugin_id, &create_aggregate_device_address, 0, nullptr, &size);
   1670   if (r != noErr) {
   1671     LOG("AudioObjectGetPropertyDataSize/kAudioPlugInCreateAggregateDevice, "
   1672         "rv=%d",
   1673         r);
   1674     return CUBEB_ERROR;
   1675   }
   1676 
   1677   CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable(
   1678       kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
   1679       &kCFTypeDictionaryValueCallBacks);
   1680   struct timeval timestamp;
   1681   gettimeofday(&timestamp, NULL);
   1682   long long int time_id = timestamp.tv_sec * 1000000LL + timestamp.tv_usec;
   1683   CFStringRef aggregate_device_name = CFStringCreateWithFormat(
   1684       NULL, NULL, CFSTR("%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
   1685   CFDictionaryAddValue(aggregate_device_dict,
   1686                        CFSTR(kAudioAggregateDeviceNameKey),
   1687                        aggregate_device_name);
   1688   CFRelease(aggregate_device_name);
   1689 
   1690   CFStringRef aggregate_device_UID =
   1691       CFStringCreateWithFormat(NULL, NULL, CFSTR("org.mozilla.%s_%llx"),
   1692                                PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
   1693   CFDictionaryAddValue(aggregate_device_dict,
   1694                        CFSTR(kAudioAggregateDeviceUIDKey),
   1695                        aggregate_device_UID);
   1696   CFRelease(aggregate_device_UID);
   1697 
   1698   int private_value = 1;
   1699   CFNumberRef aggregate_device_private_key =
   1700       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &private_value);
   1701   CFDictionaryAddValue(aggregate_device_dict,
   1702                        CFSTR(kAudioAggregateDeviceIsPrivateKey),
   1703                        aggregate_device_private_key);
   1704   CFRelease(aggregate_device_private_key);
   1705 
   1706   int stacked_value = 0;
   1707   CFNumberRef aggregate_device_stacked_key =
   1708       CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &stacked_value);
   1709   CFDictionaryAddValue(aggregate_device_dict,
   1710                        CFSTR(kAudioAggregateDeviceIsStackedKey),
   1711                        aggregate_device_stacked_key);
   1712   CFRelease(aggregate_device_stacked_key);
   1713 
   1714   r = AudioObjectGetPropertyData(*plugin_id, &create_aggregate_device_address,
   1715                                  sizeof(aggregate_device_dict),
   1716                                  &aggregate_device_dict, &size,
   1717                                  aggregate_device_id);
   1718   CFRelease(aggregate_device_dict);
   1719   if (r != noErr) {
   1720     LOG("AudioObjectGetPropertyData/kAudioPlugInCreateAggregateDevice, rv=%d",
   1721         r);
   1722     return CUBEB_ERROR;
   1723   }
   1724   LOG("New aggregate device %u", *aggregate_device_id);
   1725 
   1726   return CUBEB_OK;
   1727 }
   1728 
   1729 // The returned CFStringRef object needs to be released (via CFRelease)
   1730 // if it's not NULL, since the reference count of the returned CFStringRef
   1731 // object is increased.
   1732 static CFStringRef
   1733 get_device_name(AudioDeviceID id)
   1734 {
   1735   UInt32 size = sizeof(CFStringRef);
   1736   CFStringRef UIname = nullptr;
   1737   AudioObjectPropertyAddress address_uuid = {kAudioDevicePropertyDeviceUID,
   1738                                              kAudioObjectPropertyScopeGlobal,
   1739                                              kAudioObjectPropertyElementMaster};
   1740   OSStatus err =
   1741       AudioObjectGetPropertyData(id, &address_uuid, 0, nullptr, &size, &UIname);
   1742   return (err == noErr) ? UIname : NULL;
   1743 }
   1744 
   1745 static int
   1746 audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id,
   1747                                         AudioDeviceID input_device_id,
   1748                                         AudioDeviceID output_device_id)
   1749 {
   1750   LOG("Add devices input %u and output %u into aggregate device %u",
   1751       input_device_id, output_device_id, aggregate_device_id);
   1752   const vector<AudioDeviceID> output_sub_devices =
   1753       audiounit_get_sub_devices(output_device_id);
   1754   const vector<AudioDeviceID> input_sub_devices =
   1755       audiounit_get_sub_devices(input_device_id);
   1756 
   1757   CFMutableArrayRef aggregate_sub_devices_array =
   1758       CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
   1759   /* The order of the items in the array is significant and is used to determine
   1760      the order of the streams of the AudioAggregateDevice. */
   1761   for (UInt32 i = 0; i < output_sub_devices.size(); i++) {
   1762     CFStringRef ref = get_device_name(output_sub_devices[i]);
   1763     if (ref == NULL) {
   1764       CFRelease(aggregate_sub_devices_array);
   1765       return CUBEB_ERROR;
   1766     }
   1767     CFArrayAppendValue(aggregate_sub_devices_array, ref);
   1768     CFRelease(ref);
   1769   }
   1770   for (UInt32 i = 0; i < input_sub_devices.size(); i++) {
   1771     CFStringRef ref = get_device_name(input_sub_devices[i]);
   1772     if (ref == NULL) {
   1773       CFRelease(aggregate_sub_devices_array);
   1774       return CUBEB_ERROR;
   1775     }
   1776     CFArrayAppendValue(aggregate_sub_devices_array, ref);
   1777     CFRelease(ref);
   1778   }
   1779 
   1780   AudioObjectPropertyAddress aggregate_sub_device_list = {
   1781       kAudioAggregateDevicePropertyFullSubDeviceList,
   1782       kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
   1783   UInt32 size = sizeof(CFMutableArrayRef);
   1784   OSStatus rv = AudioObjectSetPropertyData(
   1785       aggregate_device_id, &aggregate_sub_device_list, 0, nullptr, size,
   1786       &aggregate_sub_devices_array);
   1787   CFRelease(aggregate_sub_devices_array);
   1788   if (rv != noErr) {
   1789     LOG("AudioObjectSetPropertyData/"
   1790         "kAudioAggregateDevicePropertyFullSubDeviceList, rv=%d",
   1791         rv);
   1792     return CUBEB_ERROR;
   1793   }
   1794 
   1795   return CUBEB_OK;
   1796 }
   1797 
   1798 static int
   1799 audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id)
   1800 {
   1801   assert(aggregate_device_id != kAudioObjectUnknown);
   1802   AudioObjectPropertyAddress master_aggregate_sub_device = {
   1803       kAudioAggregateDevicePropertyMasterSubDevice,
   1804       kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster};
   1805 
   1806   // Master become the 1st output sub device
   1807   AudioDeviceID output_device_id =
   1808       audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
   1809   const vector<AudioDeviceID> output_sub_devices =
   1810       audiounit_get_sub_devices(output_device_id);
   1811   CFStringRef master_sub_device = get_device_name(output_sub_devices[0]);
   1812 
   1813   UInt32 size = sizeof(CFStringRef);
   1814   OSStatus rv = AudioObjectSetPropertyData(aggregate_device_id,
   1815                                            &master_aggregate_sub_device, 0,
   1816                                            NULL, size, &master_sub_device);
   1817   if (master_sub_device) {
   1818     CFRelease(master_sub_device);
   1819   }
   1820   if (rv != noErr) {
   1821     LOG("AudioObjectSetPropertyData/"
   1822         "kAudioAggregateDevicePropertyMasterSubDevice, rv=%d",
   1823         rv);
   1824     return CUBEB_ERROR;
   1825   }
   1826 
   1827   return CUBEB_OK;
   1828 }
   1829 
   1830 static int
   1831 audiounit_activate_clock_drift_compensation(
   1832     const AudioDeviceID aggregate_device_id)
   1833 {
   1834   assert(aggregate_device_id != kAudioObjectUnknown);
   1835   AudioObjectPropertyAddress address_owned = {
   1836       kAudioObjectPropertyOwnedObjects, kAudioObjectPropertyScopeGlobal,
   1837       kAudioObjectPropertyElementMaster};
   1838 
   1839   UInt32 qualifier_data_size = sizeof(AudioObjectID);
   1840   AudioClassID class_id = kAudioSubDeviceClassID;
   1841   void * qualifier_data = &class_id;
   1842   UInt32 size = 0;
   1843   OSStatus rv = AudioObjectGetPropertyDataSize(
   1844       aggregate_device_id, &address_owned, qualifier_data_size, qualifier_data,
   1845       &size);
   1846   if (rv != noErr) {
   1847     LOG("AudioObjectGetPropertyDataSize/kAudioObjectPropertyOwnedObjects, "
   1848         "rv=%d",
   1849         rv);
   1850     return CUBEB_ERROR;
   1851   }
   1852 
   1853   UInt32 subdevices_num = 0;
   1854   subdevices_num = size / sizeof(AudioObjectID);
   1855   AudioObjectID sub_devices[subdevices_num];
   1856   size = sizeof(sub_devices);
   1857 
   1858   rv = AudioObjectGetPropertyData(aggregate_device_id, &address_owned,
   1859                                   qualifier_data_size, qualifier_data, &size,
   1860                                   sub_devices);
   1861   if (rv != noErr) {
   1862     LOG("AudioObjectGetPropertyData/kAudioObjectPropertyOwnedObjects, rv=%d",
   1863         rv);
   1864     return CUBEB_ERROR;
   1865   }
   1866 
   1867   AudioObjectPropertyAddress address_drift = {
   1868       kAudioSubDevicePropertyDriftCompensation, kAudioObjectPropertyScopeGlobal,
   1869       kAudioObjectPropertyElementMaster};
   1870 
   1871   // Start from the second device since the first is the master clock
   1872   for (UInt32 i = 1; i < subdevices_num; ++i) {
   1873     UInt32 drift_compensation_value = 1;
   1874     rv = AudioObjectSetPropertyData(sub_devices[i], &address_drift, 0, nullptr,
   1875                                     sizeof(UInt32), &drift_compensation_value);
   1876     if (rv != noErr) {
   1877       LOG("AudioObjectSetPropertyData/"
   1878           "kAudioSubDevicePropertyDriftCompensation, rv=%d",
   1879           rv);
   1880       return CUBEB_OK;
   1881     }
   1882   }
   1883   return CUBEB_OK;
   1884 }
   1885 
   1886 static int
   1887 audiounit_destroy_aggregate_device(AudioObjectID plugin_id,
   1888                                    AudioDeviceID * aggregate_device_id);
   1889 static void
   1890 audiounit_get_available_samplerate(AudioObjectID devid,
   1891                                    AudioObjectPropertyScope scope,
   1892                                    uint32_t * min, uint32_t * max,
   1893                                    uint32_t * def);
   1894 static int
   1895 audiounit_create_device_from_hwdev(cubeb_device_info * dev_info,
   1896                                    AudioObjectID devid, cubeb_device_type type);
   1897 static void
   1898 audiounit_device_destroy(cubeb_device_info * device);
   1899 
   1900 static void
   1901 audiounit_workaround_for_airpod(cubeb_stream * stm)
   1902 {
   1903   cubeb_device_info input_device_info;
   1904   audiounit_create_device_from_hwdev(&input_device_info, stm->input_device.id,
   1905                                      CUBEB_DEVICE_TYPE_INPUT);
   1906 
   1907   cubeb_device_info output_device_info;
   1908   audiounit_create_device_from_hwdev(&output_device_info, stm->output_device.id,
   1909                                      CUBEB_DEVICE_TYPE_OUTPUT);
   1910 
   1911   std::string input_name_str(input_device_info.friendly_name);
   1912   std::string output_name_str(output_device_info.friendly_name);
   1913 
   1914   if (input_name_str.find("AirPods") != std::string::npos &&
   1915       output_name_str.find("AirPods") != std::string::npos) {
   1916     uint32_t input_min_rate = 0;
   1917     uint32_t input_max_rate = 0;
   1918     uint32_t input_nominal_rate = 0;
   1919     audiounit_get_available_samplerate(
   1920         stm->input_device.id, kAudioObjectPropertyScopeGlobal, &input_min_rate,
   1921         &input_max_rate, &input_nominal_rate);
   1922     LOG("(%p) Input device %u, name: %s, min: %u, max: %u, nominal rate: %u",
   1923         stm, stm->input_device.id, input_device_info.friendly_name,
   1924         input_min_rate, input_max_rate, input_nominal_rate);
   1925     uint32_t output_min_rate = 0;
   1926     uint32_t output_max_rate = 0;
   1927     uint32_t output_nominal_rate = 0;
   1928     audiounit_get_available_samplerate(
   1929         stm->output_device.id, kAudioObjectPropertyScopeGlobal,
   1930         &output_min_rate, &output_max_rate, &output_nominal_rate);
   1931     LOG("(%p) Output device %u, name: %s, min: %u, max: %u, nominal rate: %u",
   1932         stm, stm->output_device.id, output_device_info.friendly_name,
   1933         output_min_rate, output_max_rate, output_nominal_rate);
   1934 
   1935     Float64 rate = input_nominal_rate;
   1936     AudioObjectPropertyAddress addr = {kAudioDevicePropertyNominalSampleRate,
   1937                                        kAudioObjectPropertyScopeGlobal,
   1938                                        kAudioObjectPropertyElementMaster};
   1939 
   1940     OSStatus rv = AudioObjectSetPropertyData(stm->aggregate_device_id, &addr, 0,
   1941                                              nullptr, sizeof(Float64), &rate);
   1942     if (rv != noErr) {
   1943       LOG("Non fatal error, "
   1944           "AudioObjectSetPropertyData/kAudioDevicePropertyNominalSampleRate, "
   1945           "rv=%d",
   1946           rv);
   1947     }
   1948   }
   1949   audiounit_device_destroy(&input_device_info);
   1950   audiounit_device_destroy(&output_device_info);
   1951 }
   1952 
   1953 /*
   1954  * Aggregate Device is a virtual audio interface which utilizes inputs and
   1955  * outputs of one or more physical audio interfaces. It is possible to use the
   1956  * clock of one of the devices as a master clock for all the combined devices
   1957  * and enable drift compensation for the devices that are not designated clock
   1958  * master.
   1959  *
   1960  * Creating a new aggregate device programmatically requires [0][1]:
   1961  * 1. Locate the base plug-in ("com.apple.audio.CoreAudio")
   1962  * 2. Create a dictionary that describes the aggregate device
   1963  *    (don't add sub-devices in that step, prone to fail [0])
   1964  * 3. Ask the base plug-in to create the aggregate device (blank)
   1965  * 4. Add the array of sub-devices.
   1966  * 5. Set the master device (1st output device in our case)
   1967  * 6. Enable drift compensation for the non-master devices
   1968  *
   1969  * [0] https://lists.apple.com/archives/coreaudio-api/2006/Apr/msg00092.html
   1970  * [1] https://lists.apple.com/archives/coreaudio-api/2005/Jul/msg00150.html
   1971  * [2] CoreAudio.framework/Headers/AudioHardware.h
   1972  * */
   1973 static int
   1974 audiounit_create_aggregate_device(cubeb_stream * stm)
   1975 {
   1976   int r = audiounit_create_blank_aggregate_device(&stm->plugin_id,
   1977                                                   &stm->aggregate_device_id);
   1978   if (r != CUBEB_OK) {
   1979     LOG("(%p) Failed to create blank aggregate device", stm);
   1980     return CUBEB_ERROR;
   1981   }
   1982 
   1983   r = audiounit_set_aggregate_sub_device_list(
   1984       stm->aggregate_device_id, stm->input_device.id, stm->output_device.id);
   1985   if (r != CUBEB_OK) {
   1986     LOG("(%p) Failed to set aggregate sub-device list", stm);
   1987     audiounit_destroy_aggregate_device(stm->plugin_id,
   1988                                        &stm->aggregate_device_id);
   1989     return CUBEB_ERROR;
   1990   }
   1991 
   1992   r = audiounit_set_master_aggregate_device(stm->aggregate_device_id);
   1993   if (r != CUBEB_OK) {
   1994     LOG("(%p) Failed to set master sub-device for aggregate device", stm);
   1995     audiounit_destroy_aggregate_device(stm->plugin_id,
   1996                                        &stm->aggregate_device_id);
   1997     return CUBEB_ERROR;
   1998   }
   1999 
   2000   r = audiounit_activate_clock_drift_compensation(stm->aggregate_device_id);
   2001   if (r != CUBEB_OK) {
   2002     LOG("(%p) Failed to activate clock drift compensation for aggregate device",
   2003         stm);
   2004     audiounit_destroy_aggregate_device(stm->plugin_id,
   2005                                        &stm->aggregate_device_id);
   2006     return CUBEB_ERROR;
   2007   }
   2008 
   2009   audiounit_workaround_for_airpod(stm);
   2010 
   2011   return CUBEB_OK;
   2012 }
   2013 
   2014 static int
   2015 audiounit_destroy_aggregate_device(AudioObjectID plugin_id,
   2016                                    AudioDeviceID * aggregate_device_id)
   2017 {
   2018   assert(aggregate_device_id && *aggregate_device_id != kAudioDeviceUnknown &&
   2019          plugin_id != kAudioObjectUnknown);
   2020   AudioObjectPropertyAddress destroy_aggregate_device_addr = {
   2021       kAudioPlugInDestroyAggregateDevice, kAudioObjectPropertyScopeGlobal,
   2022       kAudioObjectPropertyElementMaster};
   2023   UInt32 size;
   2024   OSStatus rv = AudioObjectGetPropertyDataSize(
   2025       plugin_id, &destroy_aggregate_device_addr, 0, NULL, &size);
   2026   if (rv != noErr) {
   2027     LOG("AudioObjectGetPropertyDataSize/kAudioPlugInDestroyAggregateDevice, "
   2028         "rv=%d",
   2029         rv);
   2030     return CUBEB_ERROR;
   2031   }
   2032 
   2033   rv = AudioObjectGetPropertyData(plugin_id, &destroy_aggregate_device_addr, 0,
   2034                                   NULL, &size, aggregate_device_id);
   2035   if (rv != noErr) {
   2036     LOG("AudioObjectGetPropertyData/kAudioPlugInDestroyAggregateDevice, rv=%d",
   2037         rv);
   2038     return CUBEB_ERROR;
   2039   }
   2040 
   2041   LOG("Destroyed aggregate device %d", *aggregate_device_id);
   2042   *aggregate_device_id = kAudioObjectUnknown;
   2043   return CUBEB_OK;
   2044 }
   2045 
   2046 static int
   2047 audiounit_new_unit_instance(AudioUnit * unit, device_info * device)
   2048 {
   2049   AudioComponentDescription desc;
   2050   AudioComponent comp;
   2051   OSStatus rv;
   2052 
   2053   desc.componentType = kAudioUnitType_Output;
   2054 #if TARGET_OS_IPHONE
   2055   desc.componentSubType = kAudioUnitSubType_RemoteIO;
   2056 #else
   2057   // Use the DefaultOutputUnit for output when no device is specified
   2058   // so we retain automatic output device switching when the default
   2059   // changes.  Once we have complete support for device notifications
   2060   // and switching, we can use the AUHAL for everything.
   2061   if ((device->flags & DEV_SYSTEM_DEFAULT) && (device->flags & DEV_OUTPUT)) {
   2062     desc.componentSubType = kAudioUnitSubType_DefaultOutput;
   2063   } else {
   2064     desc.componentSubType = kAudioUnitSubType_HALOutput;
   2065   }
   2066 #endif
   2067   desc.componentManufacturer = kAudioUnitManufacturer_Apple;
   2068   desc.componentFlags = 0;
   2069   desc.componentFlagsMask = 0;
   2070   comp = AudioComponentFindNext(NULL, &desc);
   2071   if (comp == NULL) {
   2072     LOG("Could not find matching audio hardware.");
   2073     return CUBEB_ERROR;
   2074   }
   2075 
   2076   rv = AudioComponentInstanceNew(comp, unit);
   2077   if (rv != noErr) {
   2078     LOG("AudioComponentInstanceNew rv=%d", rv);
   2079     return CUBEB_ERROR;
   2080   }
   2081   return CUBEB_OK;
   2082 }
   2083 
   2084 enum enable_state {
   2085   DISABLE,
   2086   ENABLE,
   2087 };
   2088 
   2089 static int
   2090 audiounit_enable_unit_scope(AudioUnit * unit, io_side side, enable_state state)
   2091 {
   2092   OSStatus rv;
   2093   UInt32 enable = state;
   2094   rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_EnableIO,
   2095                             (side == io_side::INPUT) ? kAudioUnitScope_Input
   2096                                                      : kAudioUnitScope_Output,
   2097                             (side == io_side::INPUT) ? AU_IN_BUS : AU_OUT_BUS,
   2098                             &enable, sizeof(UInt32));
   2099   if (rv != noErr) {
   2100     LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_EnableIO rv=%d", rv);
   2101     return CUBEB_ERROR;
   2102   }
   2103   return CUBEB_OK;
   2104 }
   2105 
   2106 static int
   2107 audiounit_create_unit(AudioUnit * unit, device_info * device)
   2108 {
   2109   assert(*unit == nullptr);
   2110   assert(device);
   2111 
   2112   OSStatus rv;
   2113   int r;
   2114 
   2115   r = audiounit_new_unit_instance(unit, device);
   2116   if (r != CUBEB_OK) {
   2117     return r;
   2118   }
   2119   assert(*unit);
   2120 
   2121   if ((device->flags & DEV_SYSTEM_DEFAULT) && (device->flags & DEV_OUTPUT)) {
   2122     return CUBEB_OK;
   2123   }
   2124 
   2125   if (device->flags & DEV_INPUT) {
   2126     r = audiounit_enable_unit_scope(unit, io_side::INPUT, ENABLE);
   2127     if (r != CUBEB_OK) {
   2128       LOG("Failed to enable audiounit input scope");
   2129       return r;
   2130     }
   2131     r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, DISABLE);
   2132     if (r != CUBEB_OK) {
   2133       LOG("Failed to disable audiounit output scope");
   2134       return r;
   2135     }
   2136   } else if (device->flags & DEV_OUTPUT) {
   2137     r = audiounit_enable_unit_scope(unit, io_side::OUTPUT, ENABLE);
   2138     if (r != CUBEB_OK) {
   2139       LOG("Failed to enable audiounit output scope");
   2140       return r;
   2141     }
   2142     r = audiounit_enable_unit_scope(unit, io_side::INPUT, DISABLE);
   2143     if (r != CUBEB_OK) {
   2144       LOG("Failed to disable audiounit input scope");
   2145       return r;
   2146     }
   2147   } else {
   2148     assert(false);
   2149   }
   2150 
   2151   rv = AudioUnitSetProperty(*unit, kAudioOutputUnitProperty_CurrentDevice,
   2152                             kAudioUnitScope_Global, 0, &device->id,
   2153                             sizeof(AudioDeviceID));
   2154   if (rv != noErr) {
   2155     LOG("AudioUnitSetProperty/kAudioOutputUnitProperty_CurrentDevice rv=%d",
   2156         rv);
   2157     return CUBEB_ERROR;
   2158   }
   2159 
   2160   return CUBEB_OK;
   2161 }
   2162 
   2163 static int
   2164 audiounit_init_input_linear_buffer(cubeb_stream * stream, uint32_t capacity)
   2165 {
   2166   uint32_t size =
   2167       capacity * stream->latency_frames * stream->input_desc.mChannelsPerFrame;
   2168   if (stream->input_desc.mFormatFlags & kAudioFormatFlagIsSignedInteger) {
   2169     stream->input_linear_buffer.reset(new auto_array_wrapper_impl<short>(size));
   2170   } else {
   2171     stream->input_linear_buffer.reset(new auto_array_wrapper_impl<float>(size));
   2172   }
   2173   assert(stream->input_linear_buffer->length() == 0);
   2174 
   2175   return CUBEB_OK;
   2176 }
   2177 
   2178 static uint32_t
   2179 audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
   2180 {
   2181   // For the 1st stream set anything within safe min-max
   2182   assert(audiounit_active_streams(stm->context) > 0);
   2183   if (audiounit_active_streams(stm->context) == 1) {
   2184     return max(min<uint32_t>(latency_frames, SAFE_MAX_LATENCY_FRAMES),
   2185                SAFE_MIN_LATENCY_FRAMES);
   2186   }
   2187   assert(stm->output_unit);
   2188 
   2189   // If more than one stream operates in parallel
   2190   // allow only lower values of latency
   2191   int r;
   2192   UInt32 output_buffer_size = 0;
   2193   UInt32 size = sizeof(output_buffer_size);
   2194   if (stm->output_unit) {
   2195     r = AudioUnitGetProperty(
   2196         stm->output_unit, kAudioDevicePropertyBufferFrameSize,
   2197         kAudioUnitScope_Output, AU_OUT_BUS, &output_buffer_size, &size);
   2198     if (r != noErr) {
   2199       LOG("AudioUnitGetProperty/output/kAudioDevicePropertyBufferFrameSize "
   2200           "rv=%d",
   2201           r);
   2202       return 0;
   2203     }
   2204 
   2205     output_buffer_size =
   2206         max(min<uint32_t>(output_buffer_size, SAFE_MAX_LATENCY_FRAMES),
   2207             SAFE_MIN_LATENCY_FRAMES);
   2208   }
   2209 
   2210   UInt32 input_buffer_size = 0;
   2211   if (stm->input_unit) {
   2212     r = AudioUnitGetProperty(
   2213         stm->input_unit, kAudioDevicePropertyBufferFrameSize,
   2214         kAudioUnitScope_Input, AU_IN_BUS, &input_buffer_size, &size);
   2215     if (r != noErr) {
   2216       LOG("AudioUnitGetProperty/input/kAudioDevicePropertyBufferFrameSize "
   2217           "rv=%d",
   2218           r);
   2219       return 0;
   2220     }
   2221 
   2222     input_buffer_size =
   2223         max(min<uint32_t>(input_buffer_size, SAFE_MAX_LATENCY_FRAMES),
   2224             SAFE_MIN_LATENCY_FRAMES);
   2225   }
   2226 
   2227   // Every following active streams can only set smaller latency
   2228   UInt32 upper_latency_limit = 0;
   2229   if (input_buffer_size != 0 && output_buffer_size != 0) {
   2230     upper_latency_limit = min<uint32_t>(input_buffer_size, output_buffer_size);
   2231   } else if (input_buffer_size != 0) {
   2232     upper_latency_limit = input_buffer_size;
   2233   } else if (output_buffer_size != 0) {
   2234     upper_latency_limit = output_buffer_size;
   2235   } else {
   2236     upper_latency_limit = SAFE_MAX_LATENCY_FRAMES;
   2237   }
   2238 
   2239   return max(min<uint32_t>(latency_frames, upper_latency_limit),
   2240              SAFE_MIN_LATENCY_FRAMES);
   2241 }
   2242 
   2243 /*
   2244  * Change buffer size is prone to deadlock thus we change it
   2245  * following the steps:
   2246  * - register a listener for the buffer size property
   2247  * - change the property
   2248  * - wait until the listener is executed
   2249  * - property has changed, remove the listener
   2250  * */
   2251 static void
   2252 buffer_size_changed_callback(void * inClientData, AudioUnit inUnit,
   2253                              AudioUnitPropertyID inPropertyID,
   2254                              AudioUnitScope inScope, AudioUnitElement inElement)
   2255 {
   2256   cubeb_stream * stm = (cubeb_stream *)inClientData;
   2257 
   2258   AudioUnit au = inUnit;
   2259   AudioUnitScope au_scope = kAudioUnitScope_Input;
   2260   AudioUnitElement au_element = inElement;
   2261   char const * au_type = "output";
   2262 
   2263   if (AU_IN_BUS == inElement) {
   2264     au_scope = kAudioUnitScope_Output;
   2265     au_type = "input";
   2266   }
   2267 
   2268   switch (inPropertyID) {
   2269 
   2270   case kAudioDevicePropertyBufferFrameSize: {
   2271     if (inScope != au_scope) {
   2272       break;
   2273     }
   2274     UInt32 new_buffer_size;
   2275     UInt32 outSize = sizeof(UInt32);
   2276     OSStatus r =
   2277         AudioUnitGetProperty(au, kAudioDevicePropertyBufferFrameSize, au_scope,
   2278                              au_element, &new_buffer_size, &outSize);
   2279     if (r != noErr) {
   2280       LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: Cannot get current "
   2281           "buffer size",
   2282           stm);
   2283     } else {
   2284       LOG("(%p) Event: kAudioDevicePropertyBufferFrameSize: New %s buffer size "
   2285           "= %d for scope %d",
   2286           stm, au_type, new_buffer_size, inScope);
   2287     }
   2288     stm->buffer_size_change_state = true;
   2289     break;
   2290   }
   2291   }
   2292 }
   2293 
   2294 static int
   2295 audiounit_set_buffer_size(cubeb_stream * stm, uint32_t new_size_frames,
   2296                           io_side side)
   2297 {
   2298   AudioUnit au = stm->output_unit;
   2299   AudioUnitScope au_scope = kAudioUnitScope_Input;
   2300   AudioUnitElement au_element = AU_OUT_BUS;
   2301 
   2302   if (side == io_side::INPUT) {
   2303     au = stm->input_unit;
   2304     au_scope = kAudioUnitScope_Output;
   2305     au_element = AU_IN_BUS;
   2306   }
   2307 
   2308   uint32_t buffer_frames = 0;
   2309   UInt32 size = sizeof(buffer_frames);
   2310   int r = AudioUnitGetProperty(au, kAudioDevicePropertyBufferFrameSize,
   2311                                au_scope, au_element, &buffer_frames, &size);
   2312   if (r != noErr) {
   2313     LOG("AudioUnitGetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d",
   2314         to_string(side), r);
   2315     return CUBEB_ERROR;
   2316   }
   2317 
   2318   if (new_size_frames == buffer_frames) {
   2319     LOG("(%p) No need to update %s buffer size already %u frames", stm,
   2320         to_string(side), buffer_frames);
   2321     return CUBEB_OK;
   2322   }
   2323 
   2324   r = AudioUnitAddPropertyListener(au, kAudioDevicePropertyBufferFrameSize,
   2325                                    buffer_size_changed_callback, stm);
   2326   if (r != noErr) {
   2327     LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize "
   2328         "rv=%d",
   2329         to_string(side), r);
   2330     return CUBEB_ERROR;
   2331   }
   2332 
   2333   stm->buffer_size_change_state = false;
   2334 
   2335   r = AudioUnitSetProperty(au, kAudioDevicePropertyBufferFrameSize, au_scope,
   2336                            au_element, &new_size_frames,
   2337                            sizeof(new_size_frames));
   2338   if (r != noErr) {
   2339     LOG("AudioUnitSetProperty/%s/kAudioDevicePropertyBufferFrameSize rv=%d",
   2340         to_string(side), r);
   2341 
   2342     r = AudioUnitRemovePropertyListenerWithUserData(
   2343         au, kAudioDevicePropertyBufferFrameSize, buffer_size_changed_callback,
   2344         stm);
   2345     if (r != noErr) {
   2346       LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize "
   2347           "rv=%d",
   2348           to_string(side), r);
   2349     }
   2350 
   2351     return CUBEB_ERROR;
   2352   }
   2353 
   2354   int count = 0;
   2355   while (!stm->buffer_size_change_state && count++ < 30) {
   2356     struct timespec req, rem;
   2357     req.tv_sec = 0;
   2358     req.tv_nsec = 100000000L; // 0.1 sec
   2359     if (nanosleep(&req, &rem) < 0) {
   2360       LOG("(%p) Warning: nanosleep call failed or interrupted. Remaining time "
   2361           "%ld nano secs \n",
   2362           stm, rem.tv_nsec);
   2363     }
   2364     LOG("(%p) audiounit_set_buffer_size : wait count = %d", stm, count);
   2365   }
   2366 
   2367   r = AudioUnitRemovePropertyListenerWithUserData(
   2368       au, kAudioDevicePropertyBufferFrameSize, buffer_size_changed_callback,
   2369       stm);
   2370   if (r != noErr) {
   2371     LOG("AudioUnitAddPropertyListener/%s/kAudioDevicePropertyBufferFrameSize "
   2372         "rv=%d",
   2373         to_string(side), r);
   2374     return CUBEB_ERROR;
   2375   }
   2376 
   2377   if (!stm->buffer_size_change_state && count >= 30) {
   2378     LOG("(%p) Error, did not get buffer size change callback ...", stm);
   2379     return CUBEB_ERROR;
   2380   }
   2381 
   2382   LOG("(%p) %s buffer size changed to %u frames.", stm, to_string(side),
   2383       new_size_frames);
   2384   return CUBEB_OK;
   2385 }
   2386 
   2387 static int
   2388 audiounit_configure_input(cubeb_stream * stm)
   2389 {
   2390   assert(stm && stm->input_unit);
   2391 
   2392   int r = 0;
   2393   UInt32 size;
   2394   AURenderCallbackStruct aurcbs_in;
   2395 
   2396   LOG("(%p) Opening input side: rate %u, channels %u, format %d, latency in "
   2397       "frames %u.",
   2398       stm, stm->input_stream_params.rate, stm->input_stream_params.channels,
   2399       stm->input_stream_params.format, stm->latency_frames);
   2400 
   2401   /* Get input device sample rate. */
   2402   AudioStreamBasicDescription input_hw_desc;
   2403   size = sizeof(AudioStreamBasicDescription);
   2404   r = AudioUnitGetProperty(stm->input_unit, kAudioUnitProperty_StreamFormat,
   2405                            kAudioUnitScope_Input, AU_IN_BUS, &input_hw_desc,
   2406                            &size);
   2407   if (r != noErr) {
   2408     LOG("AudioUnitGetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r);
   2409     return CUBEB_ERROR;
   2410   }
   2411   stm->input_hw_rate = input_hw_desc.mSampleRate;
   2412   LOG("(%p) Input device sampling rate: %.2f", stm, stm->input_hw_rate);
   2413 
   2414   /* Set format description according to the input params. */
   2415   r = audio_stream_desc_init(&stm->input_desc, &stm->input_stream_params);
   2416   if (r != CUBEB_OK) {
   2417     LOG("(%p) Setting format description for input failed.", stm);
   2418     return r;
   2419   }
   2420 
   2421   // Use latency to set buffer size
   2422   r = audiounit_set_buffer_size(stm, stm->latency_frames, io_side::INPUT);
   2423   if (r != CUBEB_OK) {
   2424     LOG("(%p) Error in change input buffer size.", stm);
   2425     return CUBEB_ERROR;
   2426   }
   2427 
   2428   AudioStreamBasicDescription src_desc = stm->input_desc;
   2429   /* Input AudioUnit must be configured with device's sample rate.
   2430      we will resample inside input callback. */
   2431   src_desc.mSampleRate = stm->input_hw_rate;
   2432 
   2433   r = AudioUnitSetProperty(stm->input_unit, kAudioUnitProperty_StreamFormat,
   2434                            kAudioUnitScope_Output, AU_IN_BUS, &src_desc,
   2435                            sizeof(AudioStreamBasicDescription));
   2436   if (r != noErr) {
   2437     LOG("AudioUnitSetProperty/input/kAudioUnitProperty_StreamFormat rv=%d", r);
   2438     return CUBEB_ERROR;
   2439   }
   2440 
   2441   /* Frames per buffer in the input callback. */
   2442   r = AudioUnitSetProperty(
   2443       stm->input_unit, kAudioUnitProperty_MaximumFramesPerSlice,
   2444       kAudioUnitScope_Global, AU_IN_BUS, &stm->latency_frames, sizeof(UInt32));
   2445   if (r != noErr) {
   2446     LOG("AudioUnitSetProperty/input/kAudioUnitProperty_MaximumFramesPerSlice "
   2447         "rv=%d",
   2448         r);
   2449     return CUBEB_ERROR;
   2450   }
   2451 
   2452   // Input only capacity
   2453   unsigned int array_capacity = 1;
   2454   if (has_output(stm)) {
   2455     // Full-duplex increase capacity
   2456     array_capacity = 8;
   2457   }
   2458   if (audiounit_init_input_linear_buffer(stm, array_capacity) != CUBEB_OK) {
   2459     return CUBEB_ERROR;
   2460   }
   2461 
   2462   aurcbs_in.inputProc = audiounit_input_callback;
   2463   aurcbs_in.inputProcRefCon = stm;
   2464 
   2465   r = AudioUnitSetProperty(
   2466       stm->input_unit, kAudioOutputUnitProperty_SetInputCallback,
   2467       kAudioUnitScope_Global, AU_OUT_BUS, &aurcbs_in, sizeof(aurcbs_in));
   2468   if (r != noErr) {
   2469     LOG("AudioUnitSetProperty/input/kAudioOutputUnitProperty_SetInputCallback "
   2470         "rv=%d",
   2471         r);
   2472     return CUBEB_ERROR;
   2473   }
   2474 
   2475   stm->frames_read = 0;
   2476 
   2477   LOG("(%p) Input audiounit init successfully.", stm);
   2478 
   2479   return CUBEB_OK;
   2480 }
   2481 
   2482 static int
   2483 audiounit_configure_output(cubeb_stream * stm)
   2484 {
   2485   assert(stm && stm->output_unit);
   2486 
   2487   int r;
   2488   AURenderCallbackStruct aurcbs_out;
   2489   UInt32 size;
   2490 
   2491   LOG("(%p) Opening output side: rate %u, channels %u, format %d, latency in "
   2492       "frames %u.",
   2493       stm, stm->output_stream_params.rate, stm->output_stream_params.channels,
   2494       stm->output_stream_params.format, stm->latency_frames);
   2495 
   2496   r = audio_stream_desc_init(&stm->output_desc, &stm->output_stream_params);
   2497   if (r != CUBEB_OK) {
   2498     LOG("(%p) Could not initialize the audio stream description.", stm);
   2499     return r;
   2500   }
   2501 
   2502   /* Get output device sample rate. */
   2503   AudioStreamBasicDescription output_hw_desc;
   2504   size = sizeof(AudioStreamBasicDescription);
   2505   memset(&output_hw_desc, 0, size);
   2506   r = AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_StreamFormat,
   2507                            kAudioUnitScope_Output, AU_OUT_BUS, &output_hw_desc,
   2508                            &size);
   2509   if (r != noErr) {
   2510     LOG("AudioUnitGetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r);
   2511     return CUBEB_ERROR;
   2512   }
   2513   stm->output_hw_rate = output_hw_desc.mSampleRate;
   2514   if (!is_common_sample_rate(stm->output_desc.mSampleRate)) {
   2515     /* For uncommon sample rates, we may run into issues with the OS
   2516        resampler if we don't do the resampling ourselves, so set the
   2517        AudioUnit sample rate to the hardware rate and resample. */
   2518     stm->output_desc.mSampleRate = stm->output_hw_rate;
   2519   }
   2520   LOG("(%p) Output device sampling rate: %.2f", stm,
   2521       output_hw_desc.mSampleRate);
   2522   stm->context->channels = output_hw_desc.mChannelsPerFrame;
   2523 
   2524   // Set the input layout to match the output device layout.
   2525   audiounit_layout_init(stm, io_side::OUTPUT);
   2526   if (stm->context->channels != stm->output_stream_params.channels ||
   2527       stm->context->layout != stm->output_stream_params.layout) {
   2528     LOG("Incompatible channel layouts detected, setting up remixer");
   2529     audiounit_init_mixer(stm);
   2530     // We will be remixing the data before it reaches the output device.
   2531     // We need to adjust the number of channels and other
   2532     // AudioStreamDescription details.
   2533     stm->output_desc.mChannelsPerFrame = stm->context->channels;
   2534     stm->output_desc.mBytesPerFrame = (stm->output_desc.mBitsPerChannel / 8) *
   2535                                       stm->output_desc.mChannelsPerFrame;
   2536     stm->output_desc.mBytesPerPacket =
   2537         stm->output_desc.mBytesPerFrame * stm->output_desc.mFramesPerPacket;
   2538   } else {
   2539     stm->mixer = nullptr;
   2540   }
   2541 
   2542   r = AudioUnitSetProperty(stm->output_unit, kAudioUnitProperty_StreamFormat,
   2543                            kAudioUnitScope_Input, AU_OUT_BUS, &stm->output_desc,
   2544                            sizeof(AudioStreamBasicDescription));
   2545   if (r != noErr) {
   2546     LOG("AudioUnitSetProperty/output/kAudioUnitProperty_StreamFormat rv=%d", r);
   2547     return CUBEB_ERROR;
   2548   }
   2549 
   2550   r = audiounit_set_buffer_size(stm, stm->latency_frames, io_side::OUTPUT);
   2551   if (r != CUBEB_OK) {
   2552     LOG("(%p) Error in change output buffer size.", stm);
   2553     return CUBEB_ERROR;
   2554   }
   2555 
   2556   /* Frames per buffer in the input callback. */
   2557   r = AudioUnitSetProperty(
   2558       stm->output_unit, kAudioUnitProperty_MaximumFramesPerSlice,
   2559       kAudioUnitScope_Global, AU_OUT_BUS, &stm->latency_frames, sizeof(UInt32));
   2560   if (r != noErr) {
   2561     LOG("AudioUnitSetProperty/output/kAudioUnitProperty_MaximumFramesPerSlice "
   2562         "rv=%d",
   2563         r);
   2564     return CUBEB_ERROR;
   2565   }
   2566 
   2567   aurcbs_out.inputProc = audiounit_output_callback;
   2568   aurcbs_out.inputProcRefCon = stm;
   2569   r = AudioUnitSetProperty(
   2570       stm->output_unit, kAudioUnitProperty_SetRenderCallback,
   2571       kAudioUnitScope_Global, AU_OUT_BUS, &aurcbs_out, sizeof(aurcbs_out));
   2572   if (r != noErr) {
   2573     LOG("AudioUnitSetProperty/output/kAudioUnitProperty_SetRenderCallback "
   2574         "rv=%d",
   2575         r);
   2576     return CUBEB_ERROR;
   2577   }
   2578 
   2579   stm->frames_written = 0;
   2580 
   2581   LOG("(%p) Output audiounit init successfully.", stm);
   2582   return CUBEB_OK;
   2583 }
   2584 
   2585 static int
   2586 audiounit_setup_stream(cubeb_stream * stm)
   2587 {
   2588   stm->mutex.assert_current_thread_owns();
   2589 
   2590   if ((stm->input_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK) ||
   2591       (stm->output_stream_params.prefs & CUBEB_STREAM_PREF_LOOPBACK)) {
   2592     LOG("(%p) Loopback not supported for audiounit.", stm);
   2593     return CUBEB_ERROR_NOT_SUPPORTED;
   2594   }
   2595 
   2596   int r = 0;
   2597 
   2598   device_info in_dev_info = stm->input_device;
   2599   device_info out_dev_info = stm->output_device;
   2600 
   2601   if (has_input(stm) && has_output(stm) &&
   2602       stm->input_device.id != stm->output_device.id) {
   2603     r = audiounit_create_aggregate_device(stm);
   2604     if (r != CUBEB_OK) {
   2605       stm->aggregate_device_id = kAudioObjectUnknown;
   2606       LOG("(%p) Create aggregate devices failed.", stm);
   2607       // !!!NOTE: It is not necessary to return here. If it does not
   2608       // return it will fallback to the old implementation. The intention
   2609       // is to investigate how often it fails. I plan to remove
   2610       // it after a couple of weeks.
   2611       return r;
   2612     } else {
   2613       in_dev_info.id = out_dev_info.id = stm->aggregate_device_id;
   2614       in_dev_info.flags = DEV_INPUT;
   2615       out_dev_info.flags = DEV_OUTPUT;
   2616     }
   2617   }
   2618 
   2619   if (has_input(stm)) {
   2620     r = audiounit_create_unit(&stm->input_unit, &in_dev_info);
   2621     if (r != CUBEB_OK) {
   2622       LOG("(%p) AudioUnit creation for input failed.", stm);
   2623       return r;
   2624     }
   2625   }
   2626 
   2627   if (has_output(stm)) {
   2628     r = audiounit_create_unit(&stm->output_unit, &out_dev_info);
   2629     if (r != CUBEB_OK) {
   2630       LOG("(%p) AudioUnit creation for output failed.", stm);
   2631       return r;
   2632     }
   2633   }
   2634 
   2635   /* Latency cannot change if another stream is operating in parallel. In this
   2636    * case latency is set to the other stream value. */
   2637   if (audiounit_active_streams(stm->context) > 1) {
   2638     LOG("(%p) More than one active stream, use global latency.", stm);
   2639     stm->latency_frames = stm->context->global_latency_frames;
   2640   } else {
   2641     /* Silently clamp the latency down to the platform default, because we
   2642      * synthetize the clock from the callbacks, and we want the clock to update
   2643      * often. */
   2644     stm->latency_frames = audiounit_clamp_latency(stm, stm->latency_frames);
   2645     assert(stm->latency_frames); // Ugly error check
   2646     audiounit_set_global_latency(stm->context, stm->latency_frames);
   2647   }
   2648 
   2649   /* Configure I/O stream */
   2650   if (has_input(stm)) {
   2651     r = audiounit_configure_input(stm);
   2652     if (r != CUBEB_OK) {
   2653       LOG("(%p) Configure audiounit input failed.", stm);
   2654       return r;
   2655     }
   2656   }
   2657 
   2658   if (has_output(stm)) {
   2659     r = audiounit_configure_output(stm);
   2660     if (r != CUBEB_OK) {
   2661       LOG("(%p) Configure audiounit output failed.", stm);
   2662       return r;
   2663     }
   2664   }
   2665 
   2666   // Setting the latency doesn't work well for USB headsets (eg. plantronics).
   2667   // Keep the default latency for now.
   2668 #if 0
   2669   buffer_size = latency;
   2670 
   2671   /* Get the range of latency this particular device can work with, and clamp
   2672    * the requested latency to this acceptable range. */
   2673 #if !TARGET_OS_IPHONE
   2674   if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
   2675     return CUBEB_ERROR;
   2676   }
   2677 
   2678   if (buffer_size < (unsigned int) latency_range.mMinimum) {
   2679     buffer_size = (unsigned int) latency_range.mMinimum;
   2680   } else if (buffer_size > (unsigned int) latency_range.mMaximum) {
   2681     buffer_size = (unsigned int) latency_range.mMaximum;
   2682   }
   2683 
   2684   /**
   2685    * Get the default buffer size. If our latency request is below the default,
   2686    * set it. Otherwise, use the default latency.
   2687    **/
   2688   size = sizeof(default_buffer_size);
   2689   if (AudioUnitGetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
   2690         kAudioUnitScope_Output, 0, &default_buffer_size, &size) != 0) {
   2691     return CUBEB_ERROR;
   2692   }
   2693 
   2694   if (buffer_size < default_buffer_size) {
   2695     /* Set the maximum number of frame that the render callback will ask for,
   2696      * effectively setting the latency of the stream. This is process-wide. */
   2697     if (AudioUnitSetProperty(stm->output_unit, kAudioDevicePropertyBufferFrameSize,
   2698           kAudioUnitScope_Output, 0, &buffer_size, sizeof(buffer_size)) != 0) {
   2699       return CUBEB_ERROR;
   2700     }
   2701   }
   2702 #else // TARGET_OS_IPHONE
   2703   //TODO: [[AVAudioSession sharedInstance] inputLatency]
   2704   // http://stackoverflow.com/questions/13157523/kaudiodevicepropertybufferframesize-replacement-for-ios
   2705 #endif
   2706 #endif
   2707 
   2708   /* We use a resampler because input AudioUnit operates
   2709    * reliable only in the capture device sample rate.
   2710    * Resampler will convert it to the user sample rate
   2711    * and deliver it to the callback. */
   2712   uint32_t target_sample_rate;
   2713   if (has_input(stm)) {
   2714     target_sample_rate = stm->input_stream_params.rate;
   2715   } else {
   2716     assert(has_output(stm));
   2717     target_sample_rate = stm->output_stream_params.rate;
   2718   }
   2719 
   2720   cubeb_stream_params input_unconverted_params;
   2721   if (has_input(stm)) {
   2722     input_unconverted_params = stm->input_stream_params;
   2723     /* Use the rate of the input device. */
   2724     input_unconverted_params.rate = stm->input_hw_rate;
   2725   }
   2726 
   2727   cubeb_stream_params output_unconverted_params;
   2728   if (has_output(stm)) {
   2729     output_unconverted_params = stm->output_stream_params;
   2730     output_unconverted_params.rate = stm->output_desc.mSampleRate;
   2731   }
   2732 
   2733   /* Create resampler. */
   2734   stm->resampler.reset(cubeb_resampler_create(
   2735       stm, has_input(stm) ? &input_unconverted_params : NULL,
   2736       has_output(stm) ? &output_unconverted_params : NULL, target_sample_rate,
   2737       stm->data_callback, stm->user_ptr, CUBEB_RESAMPLER_QUALITY_DESKTOP,
   2738       CUBEB_RESAMPLER_RECLOCK_NONE));
   2739   if (!stm->resampler) {
   2740     LOG("(%p) Could not create resampler.", stm);
   2741     return CUBEB_ERROR;
   2742   }
   2743 
   2744   if (stm->input_unit != NULL) {
   2745     r = AudioUnitInitialize(stm->input_unit);
   2746     if (r != noErr) {
   2747       LOG("AudioUnitInitialize/input rv=%d", r);
   2748       return CUBEB_ERROR;
   2749     }
   2750   }
   2751 
   2752   if (stm->output_unit != NULL) {
   2753     r = AudioUnitInitialize(stm->output_unit);
   2754     if (r != noErr) {
   2755       LOG("AudioUnitInitialize/output rv=%d", r);
   2756       return CUBEB_ERROR;
   2757     }
   2758 
   2759     stm->current_latency_frames = audiounit_get_device_presentation_latency(
   2760         stm->output_device.id, kAudioDevicePropertyScopeOutput);
   2761 
   2762     Float64 unit_s;
   2763     UInt32 size = sizeof(unit_s);
   2764     if (AudioUnitGetProperty(stm->output_unit, kAudioUnitProperty_Latency,
   2765                              kAudioUnitScope_Global, 0, &unit_s,
   2766                              &size) == noErr) {
   2767       stm->current_latency_frames +=
   2768           static_cast<uint32_t>(unit_s * stm->output_desc.mSampleRate);
   2769     }
   2770   }
   2771 
   2772   if (stm->input_unit && stm->output_unit) {
   2773     // According to the I/O hardware rate it is expected a specific pattern of
   2774     // callbacks for example is input is 44100 and output is 48000 we expected
   2775     // no more than 2 out callback in a row.
   2776     stm->expected_output_callbacks_in_a_row =
   2777         ceilf(stm->output_hw_rate / stm->input_hw_rate);
   2778   }
   2779 
   2780   r = audiounit_install_device_changed_callback(stm);
   2781   if (r != CUBEB_OK) {
   2782     LOG("(%p) Could not install all device change callback.", stm);
   2783   }
   2784 
   2785   return CUBEB_OK;
   2786 }
   2787 
   2788 cubeb_stream::cubeb_stream(cubeb * context)
   2789     : context(context), resampler(nullptr, cubeb_resampler_destroy),
   2790       mixer(nullptr, cubeb_mixer_destroy)
   2791 {
   2792   PodZero(&input_desc, 1);
   2793   PodZero(&output_desc, 1);
   2794 }
   2795 
   2796 static void
   2797 audiounit_stream_destroy_internal(cubeb_stream * stm);
   2798 
   2799 static int
   2800 audiounit_stream_init(cubeb * context, cubeb_stream ** stream,
   2801                       char const * /* stream_name */, cubeb_devid input_device,
   2802                       cubeb_stream_params * input_stream_params,
   2803                       cubeb_devid output_device,
   2804                       cubeb_stream_params * output_stream_params,
   2805                       unsigned int latency_frames,
   2806                       cubeb_data_callback data_callback,
   2807                       cubeb_state_callback state_callback, void * user_ptr)
   2808 {
   2809   assert(context);
   2810   auto_lock context_lock(context->mutex);
   2811   audiounit_increment_active_streams(context);
   2812   unique_ptr<cubeb_stream, decltype(&audiounit_stream_destroy)> stm(
   2813       new cubeb_stream(context), audiounit_stream_destroy_internal);
   2814   int r;
   2815   *stream = NULL;
   2816   assert(latency_frames > 0);
   2817 
   2818   /* These could be different in the future if we have both
   2819    * full-duplex stream and different devices for input vs output. */
   2820   stm->data_callback = data_callback;
   2821   stm->state_callback = state_callback;
   2822   stm->user_ptr = user_ptr;
   2823   stm->latency_frames = latency_frames;
   2824 
   2825   if ((input_device && !input_stream_params) ||
   2826       (output_device && !output_stream_params)) {
   2827     return CUBEB_ERROR_INVALID_PARAMETER;
   2828   }
   2829   if (input_stream_params) {
   2830     stm->input_stream_params = *input_stream_params;
   2831     r = audiounit_set_device_info(
   2832         stm.get(), reinterpret_cast<uintptr_t>(input_device), io_side::INPUT);
   2833     if (r != CUBEB_OK) {
   2834       LOG("(%p) Fail to set device info for input.", stm.get());
   2835       return r;
   2836     }
   2837   }
   2838   if (output_stream_params) {
   2839     stm->output_stream_params = *output_stream_params;
   2840     r = audiounit_set_device_info(
   2841         stm.get(), reinterpret_cast<uintptr_t>(output_device), io_side::OUTPUT);
   2842     if (r != CUBEB_OK) {
   2843       LOG("(%p) Fail to set device info for output.", stm.get());
   2844       return r;
   2845     }
   2846   }
   2847 
   2848   {
   2849     // It's not critical to lock here, because no other thread has been started
   2850     // yet, but it allows to assert that the lock has been taken in
   2851     // `audiounit_setup_stream`.
   2852     auto_lock lock(stm->mutex);
   2853     r = audiounit_setup_stream(stm.get());
   2854   }
   2855 
   2856   if (r != CUBEB_OK) {
   2857     LOG("(%p) Could not setup the audiounit stream.", stm.get());
   2858     return r;
   2859   }
   2860 
   2861   r = audiounit_install_system_changed_callback(stm.get());
   2862   if (r != CUBEB_OK) {
   2863     LOG("(%p) Could not install the device change callback.", stm.get());
   2864     return r;
   2865   }
   2866 
   2867   *stream = stm.release();
   2868   LOG("(%p) Cubeb stream init successful.", *stream);
   2869   return CUBEB_OK;
   2870 }
   2871 
   2872 static void
   2873 audiounit_close_stream(cubeb_stream * stm)
   2874 {
   2875   stm->mutex.assert_current_thread_owns();
   2876 
   2877   if (stm->input_unit) {
   2878     AudioUnitUninitialize(stm->input_unit);
   2879     AudioComponentInstanceDispose(stm->input_unit);
   2880     stm->input_unit = nullptr;
   2881   }
   2882 
   2883   stm->input_linear_buffer.reset();
   2884 
   2885   if (stm->output_unit) {
   2886     AudioUnitUninitialize(stm->output_unit);
   2887     AudioComponentInstanceDispose(stm->output_unit);
   2888     stm->output_unit = nullptr;
   2889   }
   2890 
   2891   stm->resampler.reset();
   2892   stm->mixer.reset();
   2893 
   2894   if (stm->aggregate_device_id != kAudioObjectUnknown) {
   2895     audiounit_destroy_aggregate_device(stm->plugin_id,
   2896                                        &stm->aggregate_device_id);
   2897     stm->aggregate_device_id = kAudioObjectUnknown;
   2898   }
   2899 }
   2900 
   2901 static void
   2902 audiounit_stream_destroy_internal(cubeb_stream * stm)
   2903 {
   2904   stm->context->mutex.assert_current_thread_owns();
   2905 
   2906   int r = audiounit_uninstall_system_changed_callback(stm);
   2907   if (r != CUBEB_OK) {
   2908     LOG("(%p) Could not uninstall the device changed callback", stm);
   2909   }
   2910   r = audiounit_uninstall_device_changed_callback(stm);
   2911   if (r != CUBEB_OK) {
   2912     LOG("(%p) Could not uninstall all device change listeners", stm);
   2913   }
   2914 
   2915   auto_lock lock(stm->mutex);
   2916   audiounit_close_stream(stm);
   2917   assert(audiounit_active_streams(stm->context) >= 1);
   2918   audiounit_decrement_active_streams(stm->context);
   2919 }
   2920 
   2921 static void
   2922 audiounit_stream_destroy(cubeb_stream * stm)
   2923 {
   2924   int r = audiounit_uninstall_system_changed_callback(stm);
   2925   if (r != CUBEB_OK) {
   2926     LOG("(%p) Could not uninstall the device changed callback", stm);
   2927   }
   2928   r = audiounit_uninstall_device_changed_callback(stm);
   2929   if (r != CUBEB_OK) {
   2930     LOG("(%p) Could not uninstall all device change listeners", stm);
   2931   }
   2932 
   2933   if (!stm->shutdown.load()) {
   2934     auto_lock context_lock(stm->context->mutex);
   2935     audiounit_stream_stop_internal(stm);
   2936     stm->shutdown = true;
   2937   }
   2938 
   2939   stm->destroy_pending = true;
   2940   // Execute close in serial queue to avoid collision
   2941   // with reinit when un/plug devices
   2942   dispatch_sync(stm->context->serial_queue, ^() {
   2943     auto_lock context_lock(stm->context->mutex);
   2944     audiounit_stream_destroy_internal(stm);
   2945   });
   2946 
   2947   LOG("Cubeb stream (%p) destroyed successful.", stm);
   2948   delete stm;
   2949 }
   2950 
   2951 static int
   2952 audiounit_stream_start_internal(cubeb_stream * stm)
   2953 {
   2954   OSStatus r;
   2955   if (stm->input_unit != NULL) {
   2956     r = AudioOutputUnitStart(stm->input_unit);
   2957     if (r != noErr) {
   2958       LOG("AudioOutputUnitStart (input) rv=%d", r);
   2959       return CUBEB_ERROR;
   2960     }
   2961   }
   2962   if (stm->output_unit != NULL) {
   2963     r = AudioOutputUnitStart(stm->output_unit);
   2964     if (r != noErr) {
   2965       LOG("AudioOutputUnitStart (output) rv=%d", r);
   2966       return CUBEB_ERROR;
   2967     }
   2968   }
   2969   return CUBEB_OK;
   2970 }
   2971 
   2972 static int
   2973 audiounit_stream_start(cubeb_stream * stm)
   2974 {
   2975   auto_lock context_lock(stm->context->mutex);
   2976   stm->shutdown = false;
   2977   stm->draining = false;
   2978 
   2979   int r = audiounit_stream_start_internal(stm);
   2980   if (r != CUBEB_OK) {
   2981     return r;
   2982   }
   2983 
   2984   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED);
   2985 
   2986   LOG("Cubeb stream (%p) started successfully.", stm);
   2987   return CUBEB_OK;
   2988 }
   2989 
   2990 void
   2991 audiounit_stream_stop_internal(cubeb_stream * stm)
   2992 {
   2993   OSStatus r;
   2994   if (stm->input_unit != NULL) {
   2995     r = AudioOutputUnitStop(stm->input_unit);
   2996     assert(r == 0);
   2997   }
   2998   if (stm->output_unit != NULL) {
   2999     r = AudioOutputUnitStop(stm->output_unit);
   3000     assert(r == 0);
   3001   }
   3002 }
   3003 
   3004 static int
   3005 audiounit_stream_stop(cubeb_stream * stm)
   3006 {
   3007   auto_lock context_lock(stm->context->mutex);
   3008   stm->shutdown = true;
   3009 
   3010   audiounit_stream_stop_internal(stm);
   3011 
   3012   stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED);
   3013 
   3014   LOG("Cubeb stream (%p) stopped successfully.", stm);
   3015   return CUBEB_OK;
   3016 }
   3017 
   3018 static int
   3019 audiounit_stream_get_position(cubeb_stream * stm, uint64_t * position)
   3020 {
   3021   assert(stm);
   3022   if (stm->current_latency_frames > stm->frames_played) {
   3023     *position = 0;
   3024   } else {
   3025     *position = stm->frames_played - stm->current_latency_frames;
   3026   }
   3027   return CUBEB_OK;
   3028 }
   3029 
   3030 int
   3031 audiounit_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
   3032 {
   3033 #if TARGET_OS_IPHONE
   3034   // TODO
   3035   return CUBEB_ERROR_NOT_SUPPORTED;
   3036 #else
   3037   *latency = stm->total_output_latency_frames;
   3038   return CUBEB_OK;
   3039 #endif
   3040 }
   3041 
   3042 static int
   3043 audiounit_stream_get_volume(cubeb_stream * stm, float * volume)
   3044 {
   3045   assert(stm->output_unit);
   3046   OSStatus r = AudioUnitGetParameter(stm->output_unit, kHALOutputParam_Volume,
   3047                                      kAudioUnitScope_Global, 0, volume);
   3048   if (r != noErr) {
   3049     LOG("AudioUnitGetParameter/kHALOutputParam_Volume rv=%d", r);
   3050     return CUBEB_ERROR;
   3051   }
   3052   return CUBEB_OK;
   3053 }
   3054 
   3055 static int
   3056 audiounit_stream_set_volume(cubeb_stream * stm, float volume)
   3057 {
   3058   assert(stm->output_unit);
   3059   OSStatus r;
   3060   r = AudioUnitSetParameter(stm->output_unit, kHALOutputParam_Volume,
   3061                             kAudioUnitScope_Global, 0, volume, 0);
   3062 
   3063   if (r != noErr) {
   3064     LOG("AudioUnitSetParameter/kHALOutputParam_Volume rv=%d", r);
   3065     return CUBEB_ERROR;
   3066   }
   3067   return CUBEB_OK;
   3068 }
   3069 
   3070 unique_ptr<char[]>
   3071 convert_uint32_into_string(UInt32 data)
   3072 {
   3073   // Simply create an empty string if no data.
   3074   size_t size = data == 0 ? 0 : 4;                   // 4 bytes for uint32.
   3075   auto str = unique_ptr<char[]>{new char[size + 1]}; // + 1 for '\0'.
   3076   str[size] = '\0';
   3077   if (size < 4) {
   3078     return str;
   3079   }
   3080 
   3081   // Reverse 0xWXYZ into 0xZYXW.
   3082   str[0] = (char)(data >> 24);
   3083   str[1] = (char)(data >> 16);
   3084   str[2] = (char)(data >> 8);
   3085   str[3] = (char)(data);
   3086   return str;
   3087 }
   3088 
   3089 int
   3090 audiounit_get_default_device_datasource(cubeb_device_type type, UInt32 * data)
   3091 {
   3092   AudioDeviceID id = audiounit_get_default_device_id(type);
   3093   if (id == kAudioObjectUnknown) {
   3094     return CUBEB_ERROR;
   3095   }
   3096 
   3097   UInt32 size = sizeof(*data);
   3098   /* This fails with some USB headsets (e.g., Plantronic .Audio 628). */
   3099   OSStatus r = AudioObjectGetPropertyData(
   3100       id,
   3101       type == CUBEB_DEVICE_TYPE_INPUT ? &INPUT_DATA_SOURCE_PROPERTY_ADDRESS
   3102                                       : &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS,
   3103       0, NULL, &size, data);
   3104   if (r != noErr) {
   3105     *data = 0;
   3106   }
   3107 
   3108   return CUBEB_OK;
   3109 }
   3110 
   3111 int
   3112 audiounit_get_default_device_name(cubeb_stream * stm,
   3113                                   cubeb_device * const device,
   3114                                   cubeb_device_type type)
   3115 {
   3116   assert(stm);
   3117   assert(device);
   3118 
   3119   UInt32 data;
   3120   int r = audiounit_get_default_device_datasource(type, &data);
   3121   if (r != CUBEB_OK) {
   3122     return r;
   3123   }
   3124   char ** name = type == CUBEB_DEVICE_TYPE_INPUT ? &device->input_name
   3125                                                  : &device->output_name;
   3126   *name = convert_uint32_into_string(data).release();
   3127   if (!strlen(*name)) { // empty string.
   3128     LOG("(%p) name of %s device is empty!", stm,
   3129         type == CUBEB_DEVICE_TYPE_INPUT ? "input" : "output");
   3130   }
   3131   return CUBEB_OK;
   3132 }
   3133 
   3134 int
   3135 audiounit_stream_get_current_device(cubeb_stream * stm,
   3136                                     cubeb_device ** const device)
   3137 {
   3138 #if TARGET_OS_IPHONE
   3139   // TODO
   3140   return CUBEB_ERROR_NOT_SUPPORTED;
   3141 #else
   3142   *device = new cubeb_device;
   3143   if (!*device) {
   3144     return CUBEB_ERROR;
   3145   }
   3146   PodZero(*device, 1);
   3147 
   3148   int r =
   3149       audiounit_get_default_device_name(stm, *device, CUBEB_DEVICE_TYPE_OUTPUT);
   3150   if (r != CUBEB_OK) {
   3151     return r;
   3152   }
   3153 
   3154   r = audiounit_get_default_device_name(stm, *device, CUBEB_DEVICE_TYPE_INPUT);
   3155   if (r != CUBEB_OK) {
   3156     return r;
   3157   }
   3158 
   3159   return CUBEB_OK;
   3160 #endif
   3161 }
   3162 
   3163 int
   3164 audiounit_stream_device_destroy(cubeb_stream * /* stream */,
   3165                                 cubeb_device * device)
   3166 {
   3167   delete[] device->output_name;
   3168   delete[] device->input_name;
   3169   delete device;
   3170   return CUBEB_OK;
   3171 }
   3172 
   3173 int
   3174 audiounit_stream_register_device_changed_callback(
   3175     cubeb_stream * stream,
   3176     cubeb_device_changed_callback device_changed_callback)
   3177 {
   3178   auto_lock dev_cb_lock(stream->device_changed_callback_lock);
   3179   /* Note: second register without unregister first causes 'nope' error.
   3180    * Current implementation requires unregister before register a new cb. */
   3181   assert(!device_changed_callback || !stream->device_changed_callback);
   3182   stream->device_changed_callback = device_changed_callback;
   3183   return CUBEB_OK;
   3184 }
   3185 
   3186 static char *
   3187 audiounit_strref_to_cstr_utf8(CFStringRef strref)
   3188 {
   3189   CFIndex len, size;
   3190   char * ret;
   3191   if (strref == NULL) {
   3192     return NULL;
   3193   }
   3194 
   3195   len = CFStringGetLength(strref);
   3196   // Add 1 to size to allow for '\0' termination character.
   3197   size = CFStringGetMaximumSizeForEncoding(len, kCFStringEncodingUTF8) + 1;
   3198   ret = new char[size];
   3199 
   3200   if (!CFStringGetCString(strref, ret, size, kCFStringEncodingUTF8)) {
   3201     delete[] ret;
   3202     ret = NULL;
   3203   }
   3204 
   3205   return ret;
   3206 }
   3207 
   3208 static uint32_t
   3209 audiounit_get_channel_count(AudioObjectID devid, AudioObjectPropertyScope scope)
   3210 {
   3211   AudioObjectPropertyAddress adr = {0, scope,
   3212                                     kAudioObjectPropertyElementMaster};
   3213   UInt32 size = 0;
   3214   uint32_t i, ret = 0;
   3215 
   3216   adr.mSelector = kAudioDevicePropertyStreamConfiguration;
   3217 
   3218   if (AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr &&
   3219       size > 0) {
   3220     AudioBufferList * list = static_cast<AudioBufferList *>(alloca(size));
   3221     if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, list) ==
   3222         noErr) {
   3223       for (i = 0; i < list->mNumberBuffers; i++)
   3224         ret += list->mBuffers[i].mNumberChannels;
   3225     }
   3226   }
   3227 
   3228   return ret;
   3229 }
   3230 
   3231 static void
   3232 audiounit_get_available_samplerate(AudioObjectID devid,
   3233                                    AudioObjectPropertyScope scope,
   3234                                    uint32_t * min, uint32_t * max,
   3235                                    uint32_t * def)
   3236 {
   3237   AudioObjectPropertyAddress adr = {0, scope,
   3238                                     kAudioObjectPropertyElementMaster};
   3239 
   3240   adr.mSelector = kAudioDevicePropertyNominalSampleRate;
   3241   if (AudioObjectHasProperty(devid, &adr)) {
   3242     UInt32 size = sizeof(Float64);
   3243     Float64 fvalue = 0.0;
   3244     if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &fvalue) ==
   3245         noErr) {
   3246       *def = fvalue;
   3247     }
   3248   }
   3249 
   3250   adr.mSelector = kAudioDevicePropertyAvailableNominalSampleRates;
   3251   UInt32 size = 0;
   3252   AudioValueRange range;
   3253   if (AudioObjectHasProperty(devid, &adr) &&
   3254       AudioObjectGetPropertyDataSize(devid, &adr, 0, NULL, &size) == noErr) {
   3255     uint32_t count = size / sizeof(AudioValueRange);
   3256     vector<AudioValueRange> ranges(count);
   3257     range.mMinimum = 9999999999.0;
   3258     range.mMaximum = 0.0;
   3259     if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size,
   3260                                    ranges.data()) == noErr) {
   3261       for (uint32_t i = 0; i < count; i++) {
   3262         if (ranges[i].mMaximum > range.mMaximum)
   3263           range.mMaximum = ranges[i].mMaximum;
   3264         if (ranges[i].mMinimum < range.mMinimum)
   3265           range.mMinimum = ranges[i].mMinimum;
   3266       }
   3267     }
   3268     *max = static_cast<uint32_t>(range.mMaximum);
   3269     *min = static_cast<uint32_t>(range.mMinimum);
   3270   } else {
   3271     *min = *max = 0;
   3272   }
   3273 }
   3274 
   3275 static UInt32
   3276 audiounit_get_device_presentation_latency(AudioObjectID devid,
   3277                                           AudioObjectPropertyScope scope)
   3278 {
   3279   AudioObjectPropertyAddress adr = {0, scope,
   3280                                     kAudioObjectPropertyElementMaster};
   3281   UInt32 size, dev, stream = 0;
   3282   AudioStreamID sid[1];
   3283 
   3284   adr.mSelector = kAudioDevicePropertyLatency;
   3285   size = sizeof(UInt32);
   3286   if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &dev) != noErr) {
   3287     dev = 0;
   3288   }
   3289 
   3290   adr.mSelector = kAudioDevicePropertyStreams;
   3291   size = sizeof(sid);
   3292   if (AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, sid) == noErr) {
   3293     adr.mSelector = kAudioStreamPropertyLatency;
   3294     size = sizeof(UInt32);
   3295     AudioObjectGetPropertyData(sid[0], &adr, 0, NULL, &size, &stream);
   3296   }
   3297 
   3298   return dev + stream;
   3299 }
   3300 
   3301 static int
   3302 audiounit_create_device_from_hwdev(cubeb_device_info * dev_info,
   3303                                    AudioObjectID devid, cubeb_device_type type)
   3304 {
   3305   AudioObjectPropertyAddress adr = {0, 0, kAudioObjectPropertyElementMaster};
   3306   UInt32 size;
   3307 
   3308   if (type == CUBEB_DEVICE_TYPE_OUTPUT) {
   3309     adr.mScope = kAudioDevicePropertyScopeOutput;
   3310   } else if (type == CUBEB_DEVICE_TYPE_INPUT) {
   3311     adr.mScope = kAudioDevicePropertyScopeInput;
   3312   } else {
   3313     return CUBEB_ERROR;
   3314   }
   3315 
   3316   UInt32 ch = audiounit_get_channel_count(devid, adr.mScope);
   3317   if (ch == 0) {
   3318     return CUBEB_ERROR;
   3319   }
   3320 
   3321   PodZero(dev_info, 1);
   3322 
   3323   CFStringRef device_id_str = nullptr;
   3324   size = sizeof(CFStringRef);
   3325   adr.mSelector = kAudioDevicePropertyDeviceUID;
   3326   OSStatus ret =
   3327       AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &device_id_str);
   3328   if (ret == noErr && device_id_str != NULL) {
   3329     dev_info->device_id = audiounit_strref_to_cstr_utf8(device_id_str);
   3330     static_assert(sizeof(cubeb_devid) >= sizeof(decltype(devid)),
   3331                   "cubeb_devid can't represent devid");
   3332     dev_info->devid = reinterpret_cast<cubeb_devid>(devid);
   3333     dev_info->group_id = dev_info->device_id;
   3334     CFRelease(device_id_str);
   3335   }
   3336 
   3337   CFStringRef friendly_name_str = nullptr;
   3338   UInt32 ds;
   3339   size = sizeof(UInt32);
   3340   adr.mSelector = kAudioDevicePropertyDataSource;
   3341   ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &ds);
   3342   if (ret == noErr) {
   3343     AudioValueTranslation trl = {&ds, sizeof(ds), &friendly_name_str,
   3344                                  sizeof(CFStringRef)};
   3345     adr.mSelector = kAudioDevicePropertyDataSourceNameForIDCFString;
   3346     size = sizeof(AudioValueTranslation);
   3347     AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &trl);
   3348   }
   3349 
   3350   // If there is no datasource for this device, fall back to the
   3351   // device name.
   3352   if (!friendly_name_str) {
   3353     size = sizeof(CFStringRef);
   3354     adr.mSelector = kAudioObjectPropertyName;
   3355     AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &friendly_name_str);
   3356   }
   3357 
   3358   if (friendly_name_str) {
   3359     dev_info->friendly_name = audiounit_strref_to_cstr_utf8(friendly_name_str);
   3360     CFRelease(friendly_name_str);
   3361   } else {
   3362     // Couldn't get a datasource name nor a device name, return a
   3363     // valid string of length 0.
   3364     char * fallback_name = new char[1];
   3365     fallback_name[0] = '\0';
   3366     dev_info->friendly_name = fallback_name;
   3367   }
   3368 
   3369   CFStringRef vendor_name_str = nullptr;
   3370   size = sizeof(CFStringRef);
   3371   adr.mSelector = kAudioObjectPropertyManufacturer;
   3372   ret =
   3373       AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &vendor_name_str);
   3374   if (ret == noErr && vendor_name_str != NULL) {
   3375     dev_info->vendor_name = audiounit_strref_to_cstr_utf8(vendor_name_str);
   3376     CFRelease(vendor_name_str);
   3377   }
   3378 
   3379   dev_info->type = type;
   3380   dev_info->state = CUBEB_DEVICE_STATE_ENABLED;
   3381   dev_info->preferred = (devid == audiounit_get_default_device_id(type))
   3382                             ? CUBEB_DEVICE_PREF_ALL
   3383                             : CUBEB_DEVICE_PREF_NONE;
   3384 
   3385   dev_info->max_channels = ch;
   3386   dev_info->format =
   3387       (cubeb_device_fmt)CUBEB_DEVICE_FMT_ALL; /* CoreAudio supports All! */
   3388   /* kAudioFormatFlagsAudioUnitCanonical is deprecated, prefer floating point */
   3389   dev_info->default_format = CUBEB_DEVICE_FMT_F32NE;
   3390   audiounit_get_available_samplerate(devid, adr.mScope, &dev_info->min_rate,
   3391                                      &dev_info->max_rate,
   3392                                      &dev_info->default_rate);
   3393 
   3394   UInt32 latency = audiounit_get_device_presentation_latency(devid, adr.mScope);
   3395 
   3396   AudioValueRange range;
   3397   adr.mSelector = kAudioDevicePropertyBufferFrameSizeRange;
   3398   size = sizeof(AudioValueRange);
   3399   ret = AudioObjectGetPropertyData(devid, &adr, 0, NULL, &size, &range);
   3400   if (ret == noErr) {
   3401     dev_info->latency_lo = latency + range.mMinimum;
   3402     dev_info->latency_hi = latency + range.mMaximum;
   3403   } else {
   3404     dev_info->latency_lo =
   3405         10 * dev_info->default_rate / 1000; /* Default to 10ms */
   3406     dev_info->latency_hi =
   3407         100 * dev_info->default_rate / 1000; /* Default to 100ms */
   3408   }
   3409 
   3410   return CUBEB_OK;
   3411 }
   3412 
   3413 bool
   3414 is_aggregate_device(cubeb_device_info * device_info)
   3415 {
   3416   assert(device_info->friendly_name);
   3417   return !strncmp(device_info->friendly_name, PRIVATE_AGGREGATE_DEVICE_NAME,
   3418                   strlen(PRIVATE_AGGREGATE_DEVICE_NAME));
   3419 }
   3420 
   3421 static int
   3422 audiounit_enumerate_devices(cubeb * /* context */, cubeb_device_type type,
   3423                             cubeb_device_collection * collection)
   3424 {
   3425   vector<AudioObjectID> input_devs;
   3426   vector<AudioObjectID> output_devs;
   3427 
   3428   // Count number of input and output devices.  This is not
   3429   // necessarily the same as the count of raw devices supported by the
   3430   // system since, for example, with Soundflower installed, some
   3431   // devices may report as being both input *and* output and cubeb
   3432   // separates those into two different devices.
   3433 
   3434   if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
   3435     output_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
   3436   }
   3437 
   3438   if (type & CUBEB_DEVICE_TYPE_INPUT) {
   3439     input_devs = audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
   3440   }
   3441 
   3442   auto devices = new cubeb_device_info[output_devs.size() + input_devs.size()];
   3443   collection->count = 0;
   3444 
   3445   if (type & CUBEB_DEVICE_TYPE_OUTPUT) {
   3446     for (auto dev : output_devs) {
   3447       auto device = &devices[collection->count];
   3448       auto err = audiounit_create_device_from_hwdev(device, dev,
   3449                                                     CUBEB_DEVICE_TYPE_OUTPUT);
   3450       if (err != CUBEB_OK || is_aggregate_device(device)) {
   3451         continue;
   3452       }
   3453       collection->count += 1;
   3454     }
   3455   }
   3456 
   3457   if (type & CUBEB_DEVICE_TYPE_INPUT) {
   3458     for (auto dev : input_devs) {
   3459       auto device = &devices[collection->count];
   3460       auto err = audiounit_create_device_from_hwdev(device, dev,
   3461                                                     CUBEB_DEVICE_TYPE_INPUT);
   3462       if (err != CUBEB_OK || is_aggregate_device(device)) {
   3463         continue;
   3464       }
   3465       collection->count += 1;
   3466     }
   3467   }
   3468 
   3469   if (collection->count > 0) {
   3470     collection->device = devices;
   3471   } else {
   3472     delete[] devices;
   3473     collection->device = NULL;
   3474   }
   3475 
   3476   return CUBEB_OK;
   3477 }
   3478 
   3479 static void
   3480 audiounit_device_destroy(cubeb_device_info * device)
   3481 {
   3482   delete[] device->device_id;
   3483   delete[] device->friendly_name;
   3484   delete[] device->vendor_name;
   3485 }
   3486 
   3487 static int
   3488 audiounit_device_collection_destroy(cubeb * /* context */,
   3489                                     cubeb_device_collection * collection)
   3490 {
   3491   for (size_t i = 0; i < collection->count; i++) {
   3492     audiounit_device_destroy(&collection->device[i]);
   3493   }
   3494   delete[] collection->device;
   3495 
   3496   return CUBEB_OK;
   3497 }
   3498 
   3499 static vector<AudioObjectID>
   3500 audiounit_get_devices_of_type(cubeb_device_type devtype)
   3501 {
   3502   UInt32 size = 0;
   3503   OSStatus ret = AudioObjectGetPropertyDataSize(
   3504       kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS, 0, NULL, &size);
   3505   if (ret != noErr) {
   3506     return vector<AudioObjectID>();
   3507   }
   3508   vector<AudioObjectID> devices(size / sizeof(AudioObjectID));
   3509   ret = AudioObjectGetPropertyData(kAudioObjectSystemObject,
   3510                                    &DEVICES_PROPERTY_ADDRESS, 0, NULL, &size,
   3511                                    devices.data());
   3512   if (ret != noErr) {
   3513     return vector<AudioObjectID>();
   3514   }
   3515 
   3516   // Remove the aggregate device from the list of devices (if any).
   3517   for (auto it = devices.begin(); it != devices.end();) {
   3518     CFStringRef name = get_device_name(*it);
   3519     if (name && CFStringFind(name, CFSTR("CubebAggregateDevice"), 0).location !=
   3520                     kCFNotFound) {
   3521       it = devices.erase(it);
   3522     } else {
   3523       it++;
   3524     }
   3525     if (name) {
   3526       CFRelease(name);
   3527     }
   3528   }
   3529 
   3530   /* Expected sorted but did not find anything in the docs. */
   3531   sort(devices.begin(), devices.end(),
   3532        [](AudioObjectID a, AudioObjectID b) { return a < b; });
   3533 
   3534   if (devtype == (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT)) {
   3535     return devices;
   3536   }
   3537 
   3538   AudioObjectPropertyScope scope = (devtype == CUBEB_DEVICE_TYPE_INPUT)
   3539                                        ? kAudioDevicePropertyScopeInput
   3540                                        : kAudioDevicePropertyScopeOutput;
   3541 
   3542   vector<AudioObjectID> devices_in_scope;
   3543   for (uint32_t i = 0; i < devices.size(); ++i) {
   3544     /* For device in the given scope channel must be > 0. */
   3545     if (audiounit_get_channel_count(devices[i], scope) > 0) {
   3546       devices_in_scope.push_back(devices[i]);
   3547     }
   3548   }
   3549 
   3550   return devices_in_scope;
   3551 }
   3552 
   3553 static OSStatus
   3554 audiounit_collection_changed_callback(
   3555     AudioObjectID /* inObjectID */, UInt32 /* inNumberAddresses */,
   3556     const AudioObjectPropertyAddress * /* inAddresses */, void * inClientData)
   3557 {
   3558   cubeb * context = static_cast<cubeb *>(inClientData);
   3559 
   3560   // This can be called from inside an AudioUnit function, dispatch to another
   3561   // queue.
   3562   dispatch_async(context->serial_queue, ^() {
   3563     auto_lock lock(context->mutex);
   3564     if (!context->input_collection_changed_callback &&
   3565         !context->output_collection_changed_callback) {
   3566       /* Listener removed while waiting in mutex, abort. */
   3567       return;
   3568     }
   3569     if (context->input_collection_changed_callback) {
   3570       vector<AudioObjectID> devices =
   3571           audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
   3572       /* Elements in the vector expected sorted. */
   3573       if (context->input_device_array != devices) {
   3574         context->input_device_array = devices;
   3575         context->input_collection_changed_callback(
   3576             context, context->input_collection_changed_user_ptr);
   3577       }
   3578     }
   3579     if (context->output_collection_changed_callback) {
   3580       vector<AudioObjectID> devices =
   3581           audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
   3582       /* Elements in the vector expected sorted. */
   3583       if (context->output_device_array != devices) {
   3584         context->output_device_array = devices;
   3585         context->output_collection_changed_callback(
   3586             context, context->output_collection_changed_user_ptr);
   3587       }
   3588     }
   3589   });
   3590   return noErr;
   3591 }
   3592 
   3593 static OSStatus
   3594 audiounit_add_device_listener(
   3595     cubeb * context, cubeb_device_type devtype,
   3596     cubeb_device_collection_changed_callback collection_changed_callback,
   3597     void * user_ptr)
   3598 {
   3599   context->mutex.assert_current_thread_owns();
   3600   assert(devtype & (CUBEB_DEVICE_TYPE_INPUT | CUBEB_DEVICE_TYPE_OUTPUT));
   3601   /* Note: second register without unregister first causes 'nope' error.
   3602    * Current implementation requires unregister before register a new cb. */
   3603   assert((devtype & CUBEB_DEVICE_TYPE_INPUT) &&
   3604              !context->input_collection_changed_callback ||
   3605          (devtype & CUBEB_DEVICE_TYPE_OUTPUT) &&
   3606              !context->output_collection_changed_callback);
   3607 
   3608   if (!context->input_collection_changed_callback &&
   3609       !context->output_collection_changed_callback) {
   3610     OSStatus ret = AudioObjectAddPropertyListener(
   3611         kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS,
   3612         audiounit_collection_changed_callback, context);
   3613     if (ret != noErr) {
   3614       return ret;
   3615     }
   3616   }
   3617   if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
   3618     /* Expected empty after unregister. */
   3619     assert(context->input_device_array.empty());
   3620     context->input_device_array =
   3621         audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_INPUT);
   3622     context->input_collection_changed_callback = collection_changed_callback;
   3623     context->input_collection_changed_user_ptr = user_ptr;
   3624   }
   3625   if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
   3626     /* Expected empty after unregister. */
   3627     assert(context->output_device_array.empty());
   3628     context->output_device_array =
   3629         audiounit_get_devices_of_type(CUBEB_DEVICE_TYPE_OUTPUT);
   3630     context->output_collection_changed_callback = collection_changed_callback;
   3631     context->output_collection_changed_user_ptr = user_ptr;
   3632   }
   3633   return noErr;
   3634 }
   3635 
   3636 static OSStatus
   3637 audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype)
   3638 {
   3639   context->mutex.assert_current_thread_owns();
   3640 
   3641   if (devtype & CUBEB_DEVICE_TYPE_INPUT) {
   3642     context->input_collection_changed_callback = nullptr;
   3643     context->input_collection_changed_user_ptr = nullptr;
   3644     context->input_device_array.clear();
   3645   }
   3646   if (devtype & CUBEB_DEVICE_TYPE_OUTPUT) {
   3647     context->output_collection_changed_callback = nullptr;
   3648     context->output_collection_changed_user_ptr = nullptr;
   3649     context->output_device_array.clear();
   3650   }
   3651 
   3652   if (context->input_collection_changed_callback ||
   3653       context->output_collection_changed_callback) {
   3654     return noErr;
   3655   }
   3656   /* Note: unregister a non registered cb is not a problem, not checking. */
   3657   return AudioObjectRemovePropertyListener(
   3658       kAudioObjectSystemObject, &DEVICES_PROPERTY_ADDRESS,
   3659       audiounit_collection_changed_callback, context);
   3660 }
   3661 
   3662 int
   3663 audiounit_register_device_collection_changed(
   3664     cubeb * context, cubeb_device_type devtype,
   3665     cubeb_device_collection_changed_callback collection_changed_callback,
   3666     void * user_ptr)
   3667 {
   3668   if (devtype == CUBEB_DEVICE_TYPE_UNKNOWN) {
   3669     return CUBEB_ERROR_INVALID_PARAMETER;
   3670   }
   3671   OSStatus ret;
   3672   auto_lock lock(context->mutex);
   3673   if (collection_changed_callback) {
   3674     ret = audiounit_add_device_listener(context, devtype,
   3675                                         collection_changed_callback, user_ptr);
   3676   } else {
   3677     ret = audiounit_remove_device_listener(context, devtype);
   3678   }
   3679   return (ret == noErr) ? CUBEB_OK : CUBEB_ERROR;
   3680 }
   3681 
   3682 cubeb_ops const audiounit_ops = {
   3683     /*.init =*/audiounit_init,
   3684     /*.get_backend_id =*/audiounit_get_backend_id,
   3685     /*.get_max_channel_count =*/audiounit_get_max_channel_count,
   3686     /*.get_min_latency =*/audiounit_get_min_latency,
   3687     /*.get_preferred_sample_rate =*/audiounit_get_preferred_sample_rate,
   3688     /*.get_supported_input_processing_params =*/NULL,
   3689     /*.enumerate_devices =*/audiounit_enumerate_devices,
   3690     /*.device_collection_destroy =*/audiounit_device_collection_destroy,
   3691     /*.destroy =*/audiounit_destroy,
   3692     /*.stream_init =*/audiounit_stream_init,
   3693     /*.stream_destroy =*/audiounit_stream_destroy,
   3694     /*.stream_start =*/audiounit_stream_start,
   3695     /*.stream_stop =*/audiounit_stream_stop,
   3696     /*.stream_get_position =*/audiounit_stream_get_position,
   3697     /*.stream_get_latency =*/audiounit_stream_get_latency,
   3698     /*.stream_get_input_latency =*/NULL,
   3699     /*.stream_set_volume =*/audiounit_stream_set_volume,
   3700     /*.stream_set_name =*/NULL,
   3701     /*.stream_get_current_device =*/audiounit_stream_get_current_device,
   3702     /*.stream_set_input_mute =*/NULL,
   3703     /*.stream_set_input_processing_params =*/NULL,
   3704     /*.stream_device_destroy =*/audiounit_stream_device_destroy,
   3705     /*.stream_register_device_changed_callback =*/
   3706     audiounit_stream_register_device_changed_callback,
   3707     /*.register_device_collection_changed =*/
   3708     audiounit_register_device_collection_changed};