3232#include < algorithm>
3333#include < atomic>
3434#include < vector>
35+ #include < set>
3536#include < sys/time.h>
3637#include < string>
3738
39+ using namespace std ;
40+
3841#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
3942typedef UInt32 AudioFormatFlags;
4043#endif
@@ -65,34 +68,34 @@ void audiounit_stream_stop_internal(cubeb_stream * stm);
6568void audiounit_stream_start_internal (cubeb_stream * stm);
6669static void audiounit_close_stream (cubeb_stream *stm);
6770static int audiounit_setup_stream (cubeb_stream *stm);
68- static std:: vector<AudioObjectID>
71+ static vector<AudioObjectID>
6972audiounit_get_devices_of_type (cubeb_device_type devtype);
7073
7174extern cubeb_ops const audiounit_ops;
7275
7376struct cubeb {
7477 cubeb_ops const * ops = &audiounit_ops;
7578 owned_critical_section mutex;
76- std:: atomic<int > active_streams{ 0 };
79+ atomic<int > active_streams{ 0 };
7780 uint32_t global_latency_frames = 0 ;
7881 cubeb_device_collection_changed_callback collection_changed_callback = nullptr ;
7982 void * collection_changed_user_ptr = nullptr ;
8083 /* Differentiate input from output devices. */
8184 cubeb_device_type collection_changed_devtype = CUBEB_DEVICE_TYPE_UNKNOWN ;
82- std:: vector<AudioObjectID> devtype_device_array;
85+ vector<AudioObjectID> devtype_device_array;
8386 // The queue is asynchronously deallocated once all references to it are released
8487 dispatch_queue_t serial_queue = dispatch_queue_create(DISPATCH_QUEUE_LABEL , DISPATCH_QUEUE_SERIAL );
8588 // Current used channel layout
86- std:: atomic<cubeb_channel_layout> layout{ CUBEB_LAYOUT_UNDEFINED };
89+ atomic<cubeb_channel_layout> layout{ CUBEB_LAYOUT_UNDEFINED };
8790};
8891
89- static std:: unique_ptr<AudioChannelLayout, decltype (&free)>
92+ static unique_ptr<AudioChannelLayout, decltype (&free)>
9093make_sized_audio_channel_layout (size_t sz)
9194{
9295 assert (sz >= sizeof (AudioChannelLayout));
9396 AudioChannelLayout * acl = reinterpret_cast <AudioChannelLayout *>(calloc (1 , sz));
9497 assert (acl); // Assert the allocation works.
95- return std:: unique_ptr<AudioChannelLayout, decltype (&free)>(acl, free);
98+ return unique_ptr<AudioChannelLayout, decltype (&free)>(acl, free);
9699}
97100
98101enum io_side {
@@ -156,33 +159,33 @@ struct cubeb_stream {
156159 owned_critical_section mutex;
157160 /* Hold the input samples in every
158161 * input callback iteration */
159- std:: unique_ptr<auto_array_wrapper> input_linear_buffer;
162+ unique_ptr<auto_array_wrapper> input_linear_buffer;
160163 owned_critical_section input_linear_buffer_lock;
161164 // After the resampling some input data remains stored inside
162165 // the resampler. This number is used in order to calculate
163166 // the number of extra silence frames in input.
164- std:: atomic<uint32_t > available_input_frames{ 0 };
167+ atomic<uint32_t > available_input_frames{ 0 };
165168 /* Frames on input buffer */
166- std:: atomic<uint32_t > input_buffer_frames{ 0 };
169+ atomic<uint32_t > input_buffer_frames{ 0 };
167170 /* Frame counters */
168- std:: atomic<uint64_t > frames_played{ 0 };
171+ atomic<uint64_t > frames_played{ 0 };
169172 uint64_t frames_queued = 0 ;
170- std:: atomic<int64_t > frames_read{ 0 };
171- std:: atomic<bool > shutdown{ true };
172- std:: atomic<bool > draining{ false };
173+ atomic<int64_t > frames_read{ 0 };
174+ atomic<bool > shutdown{ true };
175+ atomic<bool > draining{ false };
173176 /* Latency requested by the user. */
174177 uint32_t latency_frames = 0 ;
175- std:: atomic<uint64_t > current_latency_frames{ 0 };
178+ atomic<uint64_t > current_latency_frames{ 0 };
176179 uint64_t hw_latency_frames = UINT64_MAX ;
177- std:: atomic<float > panning{ 0 };
178- std:: unique_ptr<cubeb_resampler, decltype (&cubeb_resampler_destroy)> resampler;
180+ atomic<float > panning{ 0 };
181+ unique_ptr<cubeb_resampler, decltype (&cubeb_resampler_destroy)> resampler;
179182 /* This is true if a device change callback is currently running. */
180- std:: atomic<bool > switching_device{ false };
181- std:: atomic<bool > buffer_size_change_state{ false };
183+ atomic<bool > switching_device{ false };
184+ atomic<bool > buffer_size_change_state{ false };
182185 AudioDeviceID aggregate_device_id = 0 ; // the aggregate device id
183186 AudioObjectID plugin_id = 0 ; // used to create aggregate device
184187 /* Mixer interface */
185- std:: unique_ptr<cubeb_mixer, decltype (&cubeb_mixer_destroy)> mixer;
188+ unique_ptr<cubeb_mixer, decltype (&cubeb_mixer_destroy)> mixer;
186189};
187190
188191bool has_input (cubeb_stream * stm)
@@ -526,7 +529,7 @@ audiounit_output_callback(void * user_ptr,
526529
527530 AudioFormatFlags outaff = stm->output_desc .mFormatFlags ;
528531 float panning = (stm->output_desc .mChannelsPerFrame == 2 ) ?
529- stm->panning .load (std:: memory_order_relaxed) : 0 .0f ;
532+ stm->panning .load (memory_order_relaxed) : 0 .0f ;
530533
531534 /* Post process output samples. */
532535 if (stm->draining ) {
@@ -1065,7 +1068,7 @@ audiounit_get_min_latency(cubeb * /* ctx */,
10651068 return CUBEB_ERROR ;
10661069 }
10671070
1068- *latency_frames = std:: max<uint32_t >(latency_range.mMinimum ,
1071+ *latency_frames = max<uint32_t >(latency_range.mMinimum ,
10691072 SAFE_MIN_LATENCY_FRAMES );
10701073#endif
10711074
@@ -1409,10 +1412,10 @@ audiounit_layout_init(cubeb_stream * stm, io_side side)
14091412 stm->context ->layout = audiounit_get_current_channel_layout (stm->output_unit );
14101413}
14111414
1412- static std:: vector<AudioObjectID>
1415+ static vector<AudioObjectID>
14131416audiounit_get_sub_devices (AudioDeviceID device_id)
14141417{
1415- std:: vector<AudioDeviceID> sub_devices;
1418+ vector<AudioDeviceID> sub_devices;
14161419 AudioObjectPropertyAddress property_address = { kAudioAggregateDevicePropertyActiveSubDeviceList ,
14171420 kAudioObjectPropertyScopeGlobal ,
14181421 kAudioObjectPropertyElementMaster };
@@ -1551,8 +1554,8 @@ audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id,
15511554{
15521555 LOG (" Add devices input %u and output %u into aggregate device %u" ,
15531556 input_device_id, output_device_id, aggregate_device_id);
1554- const std:: vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices (output_device_id);
1555- const std:: vector<AudioDeviceID> input_sub_devices = audiounit_get_sub_devices (input_device_id);
1557+ const vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices (output_device_id);
1558+ const vector<AudioDeviceID> input_sub_devices = audiounit_get_sub_devices (input_device_id);
15561559
15571560 CFMutableArrayRef aggregate_sub_devices_array = CFArrayCreateMutable (NULL , 0 , &kCFTypeArrayCallBacks );
15581561 /* The order of the items in the array is significant and is used to determine the order of the streams
@@ -1603,7 +1606,7 @@ audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id)
16031606
16041607 // Master become the 1st output sub device
16051608 AudioDeviceID output_device_id = audiounit_get_default_device_id (CUBEB_DEVICE_TYPE_OUTPUT );
1606- const std:: vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices (output_device_id);
1609+ const vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices (output_device_id);
16071610 CFStringRef master_sub_device = get_device_name (output_sub_devices[0 ]);
16081611
16091612 UInt32 size = sizeof (CFStringRef);
@@ -1964,7 +1967,7 @@ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
19641967 // For the 1st stream set anything within safe min-max
19651968 assert (stm->context ->active_streams > 0 );
19661969 if (stm->context ->active_streams == 1 ) {
1967- return std:: max (std:: min<uint32_t >(latency_frames, SAFE_MAX_LATENCY_FRAMES ),
1970+ return max (min<uint32_t >(latency_frames, SAFE_MAX_LATENCY_FRAMES ),
19681971 SAFE_MIN_LATENCY_FRAMES );
19691972 }
19701973 assert (stm->output_unit );
@@ -1986,7 +1989,7 @@ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
19861989 return 0 ;
19871990 }
19881991
1989- output_buffer_size = std:: max (std:: min<uint32_t >(output_buffer_size, SAFE_MAX_LATENCY_FRAMES ),
1992+ output_buffer_size = max (min<uint32_t >(output_buffer_size, SAFE_MAX_LATENCY_FRAMES ),
19901993 SAFE_MIN_LATENCY_FRAMES );
19911994 }
19921995
@@ -2003,14 +2006,14 @@ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
20032006 return 0 ;
20042007 }
20052008
2006- input_buffer_size = std:: max (std:: min<uint32_t >(input_buffer_size, SAFE_MAX_LATENCY_FRAMES ),
2009+ input_buffer_size = max (min<uint32_t >(input_buffer_size, SAFE_MAX_LATENCY_FRAMES ),
20072010 SAFE_MIN_LATENCY_FRAMES );
20082011 }
20092012
20102013 // Every following active streams can only set smaller latency
20112014 UInt32 upper_latency_limit = 0 ;
20122015 if (input_buffer_size != 0 && output_buffer_size != 0 ) {
2013- upper_latency_limit = std:: min<uint32_t >(input_buffer_size, output_buffer_size);
2016+ upper_latency_limit = min<uint32_t >(input_buffer_size, output_buffer_size);
20142017 } else if (input_buffer_size != 0 ) {
20152018 upper_latency_limit = input_buffer_size;
20162019 } else if (output_buffer_size != 0 ) {
@@ -2019,7 +2022,7 @@ audiounit_clamp_latency(cubeb_stream * stm, uint32_t latency_frames)
20192022 upper_latency_limit = SAFE_MAX_LATENCY_FRAMES ;
20202023 }
20212024
2022- return std:: max (std:: min<uint32_t >(latency_frames, upper_latency_limit),
2025+ return max (min<uint32_t >(latency_frames, upper_latency_limit),
20232026 SAFE_MIN_LATENCY_FRAMES );
20242027}
20252028
@@ -2565,8 +2568,8 @@ audiounit_stream_init(cubeb * context,
25652568 cubeb_state_callback state_callback,
25662569 void * user_ptr)
25672570{
2568- std:: unique_ptr<cubeb_stream, decltype (&audiounit_stream_destroy)> stm (new cubeb_stream (context),
2569- audiounit_stream_destroy);
2571+ unique_ptr<cubeb_stream, decltype (&audiounit_stream_destroy)> stm (new cubeb_stream (context),
2572+ audiounit_stream_destroy);
25702573 context->active_streams += 1 ;
25712574 int r;
25722575
@@ -2669,8 +2672,10 @@ audiounit_stream_destroy(cubeb_stream * stm)
26692672 LOG (" (%p) Could not uninstall all device change listeners" , stm);
26702673 }
26712674
2672- auto_lock context_lock (stm->context ->mutex );
2673- audiounit_stream_stop_internal (stm);
2675+ {
2676+ auto_lock context_lock (stm->context ->mutex );
2677+ audiounit_stream_stop_internal (stm);
2678+ }
26742679
26752680 // Execute close in serial queue to avoid collision
26762681 // with reinit when un/plug devices
@@ -2679,6 +2684,7 @@ audiounit_stream_destroy(cubeb_stream * stm)
26792684 audiounit_close_stream (stm);
26802685 });
26812686
2687+ auto_lock context_lock (stm->context ->mutex );
26822688 assert (stm->context ->active_streams >= 1 );
26832689 stm->context ->active_streams -= 1 ;
26842690
@@ -2868,7 +2874,7 @@ int audiounit_stream_set_panning(cubeb_stream * stm, float panning)
28682874 return CUBEB_ERROR_INVALID_PARAMETER ;
28692875 }
28702876
2871- stm->panning .store (panning, std:: memory_order_relaxed);
2877+ stm->panning .store (panning, memory_order_relaxed);
28722878 return CUBEB_OK ;
28732879}
28742880
@@ -3049,7 +3055,7 @@ audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope
30493055 if (AudioObjectHasProperty (devid, &adr) &&
30503056 AudioObjectGetPropertyDataSize (devid, &adr, 0 , NULL , &size) == noErr) {
30513057 uint32_t count = size / sizeof (AudioValueRange);
3052- std:: vector<AudioValueRange> ranges (count);
3058+ vector<AudioValueRange> ranges (count);
30533059 range.mMinimum = 9999999999.0 ;
30543060 range.mMaximum = 0.0 ;
30553061 if (AudioObjectGetPropertyData (devid, &adr, 0 , NULL , &size, ranges.data ()) == noErr) {
@@ -3199,8 +3205,8 @@ static int
31993205audiounit_enumerate_devices (cubeb * /* context */ , cubeb_device_type type,
32003206 cubeb_device_collection * collection)
32013207{
3202- std:: vector<AudioObjectID> input_devs;
3203- std:: vector<AudioObjectID> output_devs;
3208+ vector<AudioObjectID> input_devs;
3209+ vector<AudioObjectID> output_devs;
32043210
32053211 // Count number of input and output devices. This is not
32063212 // necessarily the same as the count of raw devices supported by the
@@ -3265,7 +3271,7 @@ audiounit_device_collection_destroy(cubeb * /* context */,
32653271 return CUBEB_OK ;
32663272}
32673273
3268- static std:: vector<AudioObjectID>
3274+ static vector<AudioObjectID>
32693275audiounit_get_devices_of_type (cubeb_device_type devtype)
32703276{
32713277 AudioObjectPropertyAddress adr = { kAudioHardwarePropertyDevices ,
@@ -3274,18 +3280,18 @@ audiounit_get_devices_of_type(cubeb_device_type devtype)
32743280 UInt32 size = 0 ;
32753281 OSStatus ret = AudioObjectGetPropertyDataSize (kAudioObjectSystemObject , &adr, 0 , NULL , &size);
32763282 if (ret != noErr) {
3277- return std:: vector<AudioObjectID>();
3283+ return vector<AudioObjectID>();
32783284 }
32793285 /* Total number of input and output devices. */
32803286 uint32_t count = (uint32_t )(size / sizeof (AudioObjectID));
32813287
3282- std:: vector<AudioObjectID> devices (count);
3288+ vector<AudioObjectID> devices (count);
32833289 ret = AudioObjectGetPropertyData (kAudioObjectSystemObject , &adr, 0 , NULL , &size, devices.data ());
32843290 if (ret != noErr) {
3285- return std:: vector<AudioObjectID>();
3291+ return vector<AudioObjectID>();
32863292 }
32873293 /* Expected sorted but did not find anything in the docs. */
3288- std:: sort (devices.begin (), devices.end (), [](AudioObjectID a, AudioObjectID b) {
3294+ sort (devices.begin (), devices.end (), [](AudioObjectID a, AudioObjectID b) {
32893295 return a < b;
32903296 });
32913297
@@ -3297,7 +3303,7 @@ audiounit_get_devices_of_type(cubeb_device_type devtype)
32973303 kAudioDevicePropertyScopeInput :
32983304 kAudioDevicePropertyScopeOutput ;
32993305
3300- std:: vector<AudioObjectID> devices_in_scope;
3306+ vector<AudioObjectID> devices_in_scope;
33013307 for (uint32_t i = 0 ; i < count; ++i) {
33023308 /* For device in the given scope channel must be > 0. */
33033309 if (audiounit_get_channel_count (devices[i], scope) > 0 ) {
@@ -3315,27 +3321,58 @@ audiounit_collection_changed_callback(AudioObjectID /* inObjectID */,
33153321 void * inClientData)
33163322{
33173323 cubeb * context = static_cast <cubeb *>(inClientData);
3318- auto_lock lock (context->mutex );
33193324
3320- if (context->collection_changed_callback == NULL ) {
3321- /* Listener removed while waiting in mutex, abort. */
3322- return noErr;
3323- }
3325+ // This can be called from inside an AudioUnit function, dispatch to another queue.
3326+ dispatch_async (context->serial_queue , ^() {
3327+ auto_lock lock (context->mutex );
3328+ if (context->collection_changed_callback == NULL ) {
3329+ /* Listener removed while waiting in mutex, abort. */
3330+ return ;
3331+ }
3332+
3333+ /* Differentiate input from output changes. */
3334+ if (context->collection_changed_devtype == CUBEB_DEVICE_TYPE_INPUT ||
3335+ context->collection_changed_devtype == CUBEB_DEVICE_TYPE_OUTPUT ) {
3336+ vector<AudioObjectID> devices = audiounit_get_devices_of_type (context->collection_changed_devtype );
3337+ /* When count is the same examine the devid for the case of coalescing. */
3338+ if (context->devtype_device_array == devices) {
3339+ /* Device changed for the other scope, ignore. */
3340+ return ;
3341+ } else {
3342+ /* Also don't trigger the user callback if the new added device is private
3343+ * aggregate device: compute the set of new devices, and remove those
3344+ * with the name of our private aggregate devices. */
3345+ set<AudioObjectID> current_devices (devices.begin (), devices.end ());
3346+ set<AudioObjectID> previous_devices (context->devtype_device_array .begin (),
3347+ context->devtype_device_array .end ());
3348+ set<AudioObjectID> new_devices;
3349+ set_difference (current_devices.begin (), current_devices.end (),
3350+ previous_devices.begin (), previous_devices.end (),
3351+ inserter (new_devices, new_devices.begin ()));
3352+
3353+ for (auto it = new_devices.begin (); it != new_devices.end ();) {
3354+ CFStringRef name = get_device_name (*it);
3355+ if (CFStringFind (name, CFSTR (" CubebAggregateDevice" ), 0 ).location !=
3356+ kCFNotFound ) {
3357+ it = new_devices.erase (it);
3358+ } else {
3359+ it++;
3360+ }
3361+ }
33243362
3325- /* Differentiate input from output changes. */
3326- if (context->collection_changed_devtype == CUBEB_DEVICE_TYPE_INPUT ||
3327- context->collection_changed_devtype == CUBEB_DEVICE_TYPE_OUTPUT ) {
3328- std::vector<AudioObjectID> devices = audiounit_get_devices_of_type (context->collection_changed_devtype );
3329- /* When count is the same examine the devid for the case of coalescing. */
3330- if (context->devtype_device_array == devices) {
3331- /* Device changed for the other scope, ignore. */
3332- return noErr;
3363+ // If this set of new devices is empty, it means this was triggerd
3364+ // solely by creating an aggregate device, no need to trigger the user
3365+ // callback.
3366+ if (new_devices.empty ()) {
3367+ return ;
3368+ }
3369+ }
3370+ /* Device on desired scope changed. */
3371+ context->devtype_device_array = devices;
33333372 }
3334- /* Device on desired scope changed. */
3335- context->devtype_device_array = devices;
3336- }
33373373
3338- context->collection_changed_callback (context, context->collection_changed_user_ptr );
3374+ context->collection_changed_callback (context, context->collection_changed_user_ptr );
3375+ });
33393376 return noErr;
33403377}
33413378
0 commit comments