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