tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

cubeb_audiounit.cpp (125665B)


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