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