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