Source code

Revision control

Other Tools

1
/*
2
* Copyright © 2011 Mozilla Foundation
3
*
4
* This program is made available under an ISC-style license. See the
5
* accompanying file LICENSE for details.
6
*/
7
#undef NDEBUG
8
9
#include <TargetConditionals.h>
10
#include <assert.h>
11
#include <mach/mach_time.h>
12
#include <pthread.h>
13
#include <stdlib.h>
14
#include <AudioUnit/AudioUnit.h>
15
#if !TARGET_OS_IPHONE
16
#include <AvailabilityMacros.h>
17
#include <CoreAudio/AudioHardware.h>
18
#include <CoreAudio/HostTime.h>
19
#include <CoreFoundation/CoreFoundation.h>
20
#endif
21
#include <CoreAudio/CoreAudioTypes.h>
22
#include <AudioToolbox/AudioToolbox.h>
23
#include "cubeb/cubeb.h"
24
#include "cubeb-internal.h"
25
#include "cubeb_mixer.h"
26
#include "cubeb_panner.h"
27
#if !TARGET_OS_IPHONE
28
#include "cubeb_osx_run_loop.h"
29
#endif
30
#include "cubeb_resampler.h"
31
#include "cubeb_ring_array.h"
32
#include <algorithm>
33
#include <atomic>
34
#include <vector>
35
#include <set>
36
#include <sys/time.h>
37
#include <string>
38
39
using namespace std;
40
41
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000
42
typedef UInt32 AudioFormatFlags;
43
#endif
44
45
#define AU_OUT_BUS 0
46
#define AU_IN_BUS 1
47
48
const char * DISPATCH_QUEUE_LABEL = "org.mozilla.cubeb";
49
const char * PRIVATE_AGGREGATE_DEVICE_NAME = "CubebAggregateDevice";
50
51
#ifdef ALOGV
52
#undef ALOGV
53
#endif
54
#define ALOGV(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOGV(msg, ##__VA_ARGS__);})
55
56
#ifdef ALOG
57
#undef ALOG
58
#endif
59
#define ALOG(msg, ...) dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{LOG(msg, ##__VA_ARGS__);})
60
61
/* Testing empirically, some headsets report a minimal latency that is very
62
* low, but this does not work in practice. Lie and say the minimum is 256
63
* frames. */
64
const uint32_t SAFE_MIN_LATENCY_FRAMES = 128;
65
const uint32_t SAFE_MAX_LATENCY_FRAMES = 512;
66
67
const AudioObjectPropertyAddress DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS = {
68
kAudioHardwarePropertyDefaultInputDevice,
69
kAudioObjectPropertyScopeGlobal,
70
kAudioObjectPropertyElementMaster
71
};
72
73
const AudioObjectPropertyAddress DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS = {
74
kAudioHardwarePropertyDefaultOutputDevice,
75
kAudioObjectPropertyScopeGlobal,
76
kAudioObjectPropertyElementMaster
77
};
78
79
const AudioObjectPropertyAddress DEVICE_IS_ALIVE_PROPERTY_ADDRESS = {
80
kAudioDevicePropertyDeviceIsAlive,
81
kAudioObjectPropertyScopeGlobal,
82
kAudioObjectPropertyElementMaster
83
};
84
85
const AudioObjectPropertyAddress DEVICES_PROPERTY_ADDRESS = {
86
kAudioHardwarePropertyDevices,
87
kAudioObjectPropertyScopeGlobal,
88
kAudioObjectPropertyElementMaster
89
};
90
91
const AudioObjectPropertyAddress INPUT_DATA_SOURCE_PROPERTY_ADDRESS = {
92
kAudioDevicePropertyDataSource,
93
kAudioDevicePropertyScopeInput,
94
kAudioObjectPropertyElementMaster
95
};
96
97
const AudioObjectPropertyAddress OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS = {
98
kAudioDevicePropertyDataSource,
99
kAudioDevicePropertyScopeOutput,
100
kAudioObjectPropertyElementMaster
101
};
102
103
typedef uint32_t device_flags_value;
104
105
enum device_flags {
106
DEV_UNKNOWN = 0x00, /* Unknown */
107
DEV_INPUT = 0x01, /* Record device like mic */
108
DEV_OUTPUT = 0x02, /* Playback device like speakers */
109
DEV_SYSTEM_DEFAULT = 0x04, /* System default device */
110
DEV_SELECTED_DEFAULT = 0x08, /* User selected to use the system default device */
111
};
112
113
void audiounit_stream_stop_internal(cubeb_stream * stm);
114
static int audiounit_stream_start_internal(cubeb_stream * stm);
115
static void audiounit_close_stream(cubeb_stream *stm);
116
static int audiounit_setup_stream(cubeb_stream *stm);
117
static vector<AudioObjectID>
118
audiounit_get_devices_of_type(cubeb_device_type devtype);
119
static UInt32 audiounit_get_device_presentation_latency(AudioObjectID devid, AudioObjectPropertyScope scope);
120
121
#if !TARGET_OS_IPHONE
122
static AudioObjectID audiounit_get_default_device_id(cubeb_device_type type);
123
static int audiounit_uninstall_device_changed_callback(cubeb_stream * stm);
124
static int audiounit_uninstall_system_changed_callback(cubeb_stream * stm);
125
static void audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags);
126
#endif
127
128
extern cubeb_ops const audiounit_ops;
129
130
struct cubeb {
131
cubeb_ops const * ops = &audiounit_ops;
132
owned_critical_section mutex;
133
int active_streams = 0;
134
uint32_t global_latency_frames = 0;
135
cubeb_device_collection_changed_callback input_collection_changed_callback = nullptr;
136
void * input_collection_changed_user_ptr = nullptr;
137
cubeb_device_collection_changed_callback output_collection_changed_callback = nullptr;
138
void * output_collection_changed_user_ptr = nullptr;
139
// Store list of devices to detect changes
140
vector<AudioObjectID> input_device_array;
141
vector<AudioObjectID> output_device_array;
142
// The queue should be released when it’s no longer needed.
143
dispatch_queue_t serial_queue = dispatch_queue_create(DISPATCH_QUEUE_LABEL, DISPATCH_QUEUE_SERIAL);
144
// Current used channel layout
145
atomic<cubeb_channel_layout> layout{ CUBEB_LAYOUT_UNDEFINED };
146
uint32_t channels = 0;
147
};
148
149
static unique_ptr<AudioChannelLayout, decltype(&free)>
150
make_sized_audio_channel_layout(size_t sz)
151
{
152
assert(sz >= sizeof(AudioChannelLayout));
153
AudioChannelLayout * acl = reinterpret_cast<AudioChannelLayout *>(calloc(1, sz));
154
assert(acl); // Assert the allocation works.
155
return unique_ptr<AudioChannelLayout, decltype(&free)>(acl, free);
156
}
157
158
enum class io_side {
159
INPUT,
160
OUTPUT,
161
};
162
163
static char const *
164
to_string(io_side side)
165
{
166
switch (side) {
167
case io_side::INPUT:
168
return "input";
169
case io_side::OUTPUT:
170
return "output";
171
}
172
}
173
174
struct device_info {
175
AudioDeviceID id = kAudioObjectUnknown;
176
device_flags_value flags = DEV_UNKNOWN;
177
};
178
179
struct property_listener {
180
AudioDeviceID device_id;
181
const AudioObjectPropertyAddress * property_address;
182
AudioObjectPropertyListenerProc callback;
183
cubeb_stream * stream;
184
185
property_listener(AudioDeviceID id,
186
const AudioObjectPropertyAddress * address,
187
AudioObjectPropertyListenerProc proc,
188
cubeb_stream * stm)
189
: device_id(id)
190
, property_address(address)
191
, callback(proc)
192
, stream(stm)
193
{}
194
};
195
196
struct cubeb_stream {
197
explicit cubeb_stream(cubeb * context);
198
199
/* Note: Must match cubeb_stream layout in cubeb.c. */
200
cubeb * context;
201
void * user_ptr = nullptr;
202
/**/
203
204
cubeb_data_callback data_callback = nullptr;
205
cubeb_state_callback state_callback = nullptr;
206
cubeb_device_changed_callback device_changed_callback = nullptr;
207
owned_critical_section device_changed_callback_lock;
208
/* Stream creation parameters */
209
cubeb_stream_params input_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
210
cubeb_stream_params output_stream_params = { CUBEB_SAMPLE_FLOAT32NE, 0, 0, CUBEB_LAYOUT_UNDEFINED, CUBEB_STREAM_PREF_NONE };
211
device_info input_device;
212
device_info output_device;
213
/* Format descriptions */
214
AudioStreamBasicDescription input_desc;
215
AudioStreamBasicDescription output_desc;
216
/* I/O AudioUnits */
217
AudioUnit input_unit = nullptr;
218
AudioUnit output_unit = nullptr;
219
/* I/O device sample rate */
220
Float64 input_hw_rate = 0;
221
Float64 output_hw_rate = 0;
222
/* Expected I/O thread interleave,
223
* calculated from I/O hw rate. */
224
int expected_output_callbacks_in_a_row = 0;
225
owned_critical_section mutex;
226
// Hold the input samples in every input callback iteration.
227
// Only accessed on input/output callback thread and during initial configure.
228
unique_ptr<auto_array_wrapper> input_linear_buffer;
229
/* Frame counters */
230
atomic<uint64_t> frames_played{ 0 };
231
uint64_t frames_queued = 0;
232
// How many frames got read from the input since the stream started (includes
233
// padded silence)
234
atomic<int64_t> frames_read{ 0 };
235
// How many frames got written to the output device since the stream started
236
atomic<int64_t> frames_written{ 0 };
237
atomic<bool> shutdown{ true };
238
atomic<bool> draining{ false };
239
atomic<bool> reinit_pending { false };
240
atomic<bool> destroy_pending{ false };
241
/* Latency requested by the user. */
242
uint32_t latency_frames = 0;
243
atomic<uint32_t> current_latency_frames{ 0 };
244
atomic<uint32_t> total_output_latency_frames { 0 };
245
atomic<float> panning{ 0 };
246
unique_ptr<cubeb_resampler, decltype(&cubeb_resampler_destroy)> resampler;
247
/* This is true if a device change callback is currently running. */
248
atomic<bool> switching_device{ false };
249
atomic<bool> buffer_size_change_state{ false };
250
AudioDeviceID aggregate_device_id = kAudioObjectUnknown; // the aggregate device id
251
AudioObjectID plugin_id = kAudioObjectUnknown; // used to create aggregate device
252
/* Mixer interface */
253
unique_ptr<cubeb_mixer, decltype(&cubeb_mixer_destroy)> mixer;
254
/* Buffer where remixing/resampling will occur when upmixing is required */
255
/* Only accessed from callback thread */
256
unique_ptr<uint8_t[]> temp_buffer;
257
size_t temp_buffer_size = 0; // size in bytes.
258
/* Listeners indicating what system events are monitored. */
259
unique_ptr<property_listener> default_input_listener;
260
unique_ptr<property_listener> default_output_listener;
261
unique_ptr<property_listener> input_alive_listener;
262
unique_ptr<property_listener> input_source_listener;
263
unique_ptr<property_listener> output_source_listener;
264
};
265
266
bool has_input(cubeb_stream * stm)
267
{
268
return stm->input_stream_params.rate != 0;
269
}
270
271
bool has_output(cubeb_stream * stm)
272
{
273
return stm->output_stream_params.rate != 0;
274
}
275
276
cubeb_channel
277
channel_label_to_cubeb_channel(UInt32 label)
278
{
279
switch (label) {
280
case kAudioChannelLabel_Left:
281
return CHANNEL_FRONT_LEFT;
282
case kAudioChannelLabel_Right:
283
return CHANNEL_FRONT_RIGHT;
284
case kAudioChannelLabel_Center:
285
return CHANNEL_FRONT_CENTER;
286
case kAudioChannelLabel_LFEScreen:
287
return CHANNEL_LOW_FREQUENCY;
288
case kAudioChannelLabel_LeftSurround:
289
return CHANNEL_BACK_LEFT;
290
case kAudioChannelLabel_RightSurround:
291
return CHANNEL_BACK_RIGHT;
292
case kAudioChannelLabel_LeftCenter:
293
return CHANNEL_FRONT_LEFT_OF_CENTER;
294
case kAudioChannelLabel_RightCenter:
295
return CHANNEL_FRONT_RIGHT_OF_CENTER;
296
case kAudioChannelLabel_CenterSurround:
297
return CHANNEL_BACK_CENTER;
298
case kAudioChannelLabel_LeftSurroundDirect:
299
return CHANNEL_SIDE_LEFT;
300
case kAudioChannelLabel_RightSurroundDirect:
301
return CHANNEL_SIDE_RIGHT;
302
case kAudioChannelLabel_TopCenterSurround:
303
return CHANNEL_TOP_CENTER;
304
case kAudioChannelLabel_VerticalHeightLeft:
305
return CHANNEL_TOP_FRONT_LEFT;
306
case kAudioChannelLabel_VerticalHeightCenter:
307
return CHANNEL_TOP_FRONT_CENTER;
308
case kAudioChannelLabel_VerticalHeightRight:
309
return CHANNEL_TOP_FRONT_RIGHT;
310
case kAudioChannelLabel_TopBackLeft:
311
return CHANNEL_TOP_BACK_LEFT;
312
case kAudioChannelLabel_TopBackCenter:
313
return CHANNEL_TOP_BACK_CENTER;
314
case kAudioChannelLabel_TopBackRight:
315
return CHANNEL_TOP_BACK_RIGHT;
316
default:
317
return CHANNEL_UNKNOWN;
318
}
319
}
320
321
AudioChannelLabel
322
cubeb_channel_to_channel_label(cubeb_channel channel)
323
{
324
switch (channel) {
325
case CHANNEL_FRONT_LEFT:
326
return kAudioChannelLabel_Left;
327
case CHANNEL_FRONT_RIGHT:
328
return kAudioChannelLabel_Right;
329
case CHANNEL_FRONT_CENTER:
330
return kAudioChannelLabel_Center;
331
case CHANNEL_LOW_FREQUENCY:
332
return kAudioChannelLabel_LFEScreen;
333
case CHANNEL_BACK_LEFT:
334
return kAudioChannelLabel_LeftSurround;
335
case CHANNEL_BACK_RIGHT:
336
return kAudioChannelLabel_RightSurround;
337
case CHANNEL_FRONT_LEFT_OF_CENTER:
338
return kAudioChannelLabel_LeftCenter;
339
case CHANNEL_FRONT_RIGHT_OF_CENTER:
340
return kAudioChannelLabel_RightCenter;
341
case CHANNEL_BACK_CENTER:
342
return kAudioChannelLabel_CenterSurround;
343
case CHANNEL_SIDE_LEFT:
344
return kAudioChannelLabel_LeftSurroundDirect;
345
case CHANNEL_SIDE_RIGHT:
346
return kAudioChannelLabel_RightSurroundDirect;
347
case CHANNEL_TOP_CENTER:
348
return kAudioChannelLabel_TopCenterSurround;
349
case CHANNEL_TOP_FRONT_LEFT:
350
return kAudioChannelLabel_VerticalHeightLeft;
351
case CHANNEL_TOP_FRONT_CENTER:
352
return kAudioChannelLabel_VerticalHeightCenter;
353
case CHANNEL_TOP_FRONT_RIGHT:
354
return kAudioChannelLabel_VerticalHeightRight;
355
case CHANNEL_TOP_BACK_LEFT:
356
return kAudioChannelLabel_TopBackLeft;
357
case CHANNEL_TOP_BACK_CENTER:
358
return kAudioChannelLabel_TopBackCenter;
359
case CHANNEL_TOP_BACK_RIGHT:
360
return kAudioChannelLabel_TopBackRight;
361
default:
362
return kAudioChannelLabel_Unknown;
363
}
364
}
365
366
#if TARGET_OS_IPHONE
367
typedef UInt32 AudioDeviceID;
368
typedef UInt32 AudioObjectID;
369
370
#define AudioGetCurrentHostTime mach_absolute_time
371
372
#endif
373
374
uint64_t
375
ConvertHostTimeToNanos(uint64_t host_time)
376
{
377
static struct mach_timebase_info timebase_info;
378
static bool initialized = false;
379
if (!initialized) {
380
mach_timebase_info(&timebase_info);
381
initialized = true;
382
}
383
384
long double answer = host_time;
385
if (timebase_info.numer != timebase_info.denom) {
386
answer *= timebase_info.numer;
387
answer /= timebase_info.denom;
388
}
389
return (uint64_t)answer;
390
}
391
392
static void
393
audiounit_increment_active_streams(cubeb * ctx)
394
{
395
ctx->mutex.assert_current_thread_owns();
396
ctx->active_streams += 1;
397
}
398
399
static void
400
audiounit_decrement_active_streams(cubeb * ctx)
401
{
402
ctx->mutex.assert_current_thread_owns();
403
ctx->active_streams -= 1;
404
}
405
406
static int
407
audiounit_active_streams(cubeb * ctx)
408
{
409
ctx->mutex.assert_current_thread_owns();
410
return ctx->active_streams;
411
}
412
413
static void
414
audiounit_set_global_latency(cubeb * ctx, uint32_t latency_frames)
415
{
416
ctx->mutex.assert_current_thread_owns();
417
assert(audiounit_active_streams(ctx) == 1);
418
ctx->global_latency_frames = latency_frames;
419
}
420
421
static void
422
audiounit_make_silent(AudioBuffer * ioData)
423
{
424
assert(ioData);
425
assert(ioData->mData);
426
memset(ioData->mData, 0, ioData->mDataByteSize);
427
}
428
429
static OSStatus
430
audiounit_render_input(cubeb_stream * stm,
431
AudioUnitRenderActionFlags * flags,
432
AudioTimeStamp const * tstamp,
433
UInt32 bus,
434
UInt32 input_frames)
435
{
436
/* Create the AudioBufferList to store input. */
437
AudioBufferList input_buffer_list;
438
input_buffer_list.mBuffers[0].mDataByteSize =
439
stm->input_desc.mBytesPerFrame * input_frames;
440
input_buffer_list.mBuffers[0].mData = nullptr;
441
input_buffer_list.mBuffers[0].mNumberChannels = stm->input_desc.mChannelsPerFrame;
442
input_buffer_list.mNumberBuffers = 1;
443
444
/* Render input samples */
445
OSStatus r = AudioUnitRender(stm->input_unit,
446
flags,
447
tstamp,
448
bus,
449
input_frames,
450
&input_buffer_list);
451
452
if (r != noErr) {
453
LOG("AudioUnitRender rv=%d", r);
454
if (r != kAudioUnitErr_CannotDoInCurrentContext) {
455
return r;
456
}
457
if (stm->output_unit) {
458
// kAudioUnitErr_CannotDoInCurrentContext is returned when using a BT
459
// headset and the profile is changed from A2DP to HFP/HSP. The previous
460
// output device is no longer valid and must be reset.
461
audiounit_reinit_stream_async(stm, DEV_INPUT | DEV_OUTPUT);
462
}
463
// For now state that no error occurred and feed silence, stream will be
464
// resumed once reinit has completed.
465
ALOGV("(%p) input: reinit pending feeding silence instead", stm);
466
stm->input_linear_buffer->push_silence(input_frames * stm->input_desc.mChannelsPerFrame);
467
} else {
468
/* Copy input data in linear buffer. */
469
stm->input_linear_buffer->push(input_buffer_list.mBuffers[0].mData,
470
input_frames * stm->input_desc.mChannelsPerFrame);
471
}
472
473
/* Advance input frame counter. */
474
assert(input_frames > 0);
475
stm->frames_read += input_frames;
476
477
ALOGV("(%p) input: buffers %u, size %u, channels %u, rendered frames %d, total frames %lu.",
478
stm,
479
(unsigned int) input_buffer_list.mNumberBuffers,
480
(unsigned int) input_buffer_list.mBuffers[0].mDataByteSize,
481
(unsigned int) input_buffer_list.mBuffers[0].mNumberChannels,
482
(unsigned int) input_frames,
483
stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
484
485
return noErr;
486
}
487
488
static OSStatus
489
audiounit_input_callback(void * user_ptr,
490
AudioUnitRenderActionFlags * flags,
491
AudioTimeStamp const * tstamp,
492
UInt32 bus,
493
UInt32 input_frames,
494
AudioBufferList * /* bufs */)
495
{
496
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
497
498
assert(stm->input_unit != NULL);
499
assert(AU_IN_BUS == bus);
500
501
if (stm->shutdown) {
502
ALOG("(%p) input shutdown", stm);
503
return noErr;
504
}
505
506
OSStatus r = audiounit_render_input(stm, flags, tstamp, bus, input_frames);
507
if (r != noErr) {
508
return r;
509
}
510
511
// Full Duplex. We'll call data_callback in the AudioUnit output callback.
512
if (stm->output_unit != NULL) {
513
return noErr;
514
}
515
516
/* Input only. Call the user callback through resampler.
517
Resampler will deliver input buffer in the correct rate. */
518
assert(input_frames <= stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame);
519
long total_input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
520
long outframes = cubeb_resampler_fill(stm->resampler.get(),
521
stm->input_linear_buffer->data(),
522
&total_input_frames,
523
NULL,
524
0);
525
if (outframes < total_input_frames) {
526
OSStatus r = AudioOutputUnitStop(stm->input_unit);
527
assert(r == 0);
528
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
529
return noErr;
530
}
531
532
// Reset input buffer
533
stm->input_linear_buffer->clear();
534
535
return noErr;
536
}
537
538
static void
539
audiounit_mix_output_buffer(cubeb_stream * stm,
540
size_t output_frames,
541
void * input_buffer,
542
size_t input_buffer_size,
543
void * output_buffer,
544
size_t output_buffer_size)
545
{
546
assert(input_buffer_size >=
547
cubeb_sample_size(stm->output_stream_params.format) *
548
stm->output_stream_params.channels * output_frames);
549
assert(output_buffer_size >= stm->output_desc.mBytesPerFrame * output_frames);
550
551
int r = cubeb_mixer_mix(stm->mixer.get(),
552
output_frames,
553
input_buffer,
554
input_buffer_size,
555
output_buffer,
556
output_buffer_size);
557
if (r != 0) {
558
LOG("Remix error = %d", r);
559
}
560
}
561
562
// Return how many input frames (sampled at input_hw_rate) are needed to provide
563
// output_frames (sampled at output_stream_params.rate)
564
static int64_t
565
minimum_resampling_input_frames(cubeb_stream * stm, uint32_t output_frames)
566
{
567
if (stm->input_hw_rate == stm->output_stream_params.rate) {
568
// Fast path.
569
return output_frames;
570
}
571
return ceil(stm->input_hw_rate * output_frames /
572
stm->output_stream_params.rate);
573
}
574
575
static OSStatus
576
audiounit_output_callback(void * user_ptr,
577
AudioUnitRenderActionFlags * /* flags */,
578
AudioTimeStamp const * tstamp,
579
UInt32 bus,
580
UInt32 output_frames,
581
AudioBufferList * outBufferList)
582
{
583
assert(AU_OUT_BUS == bus);
584
assert(outBufferList->mNumberBuffers == 1);
585
586
cubeb_stream * stm = static_cast<cubeb_stream *>(user_ptr);
587
588
uint64_t now = ConvertHostTimeToNanos(mach_absolute_time());
589
uint64_t audio_output_time = ConvertHostTimeToNanos(tstamp->mHostTime);
590
uint64_t output_latency_ns = audio_output_time - now;
591
592
const int ns2s = 1e9;
593
// The total output latency is the timestamp difference + the stream latency +
594
// the hardware latency.
595
stm->total_output_latency_frames = output_latency_ns * stm->output_hw_rate / ns2s + stm->current_latency_frames;
596
597
ALOGV("(%p) output: buffers %u, size %u, channels %u, frames %u, total input frames %lu.",
598
stm,
599
(unsigned int) outBufferList->mNumberBuffers,
600
(unsigned int) outBufferList->mBuffers[0].mDataByteSize,
601
(unsigned int) outBufferList->mBuffers[0].mNumberChannels,
602
(unsigned int) output_frames,
603
has_input(stm) ? stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame : 0);
604
605
long input_frames = 0;
606
void * output_buffer = NULL, * input_buffer = NULL;
607
608
if (stm->shutdown) {
609
ALOG("(%p) output shutdown.", stm);
610
audiounit_make_silent(&outBufferList->mBuffers[0]);
611
return noErr;
612
}
613
614
if (stm->draining) {
615
OSStatus r = AudioOutputUnitStop(stm->output_unit);
616
assert(r == 0);
617
if (stm->input_unit) {
618
r = AudioOutputUnitStop(stm->input_unit);
619
assert(r == 0);
620
}
621
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
622
audiounit_make_silent(&outBufferList->mBuffers[0]);
623
return noErr;
624
}
625
626
/* Get output buffer. */
627
if (stm->mixer) {
628
// If remixing needs to occur, we can't directly work in our final
629
// destination buffer as data may be overwritten or too small to start with.
630
size_t size_needed = output_frames * stm->output_stream_params.channels *
631
cubeb_sample_size(stm->output_stream_params.format);
632
if (stm->temp_buffer_size < size_needed) {
633
stm->temp_buffer.reset(new uint8_t[size_needed]);
634
stm->temp_buffer_size = size_needed;
635
}
636
output_buffer = stm->temp_buffer.get();
637
} else {
638
output_buffer = outBufferList->mBuffers[0].mData;
639
}
640
641
stm->frames_written += output_frames;
642
643
/* If Full duplex get also input buffer */
644
if (stm->input_unit != NULL) {
645
/* If the output callback came first and this is a duplex stream, we need to
646
* fill in some additional silence in the resampler.
647
* Otherwise, if we had more than expected callbacks in a row, or we're
648
* currently switching, we add some silence as well to compensate for the
649
* fact that we're lacking some input data. */
650
uint32_t input_frames_needed =
651
minimum_resampling_input_frames(stm, stm->frames_written);
652
long missing_frames = input_frames_needed - stm->frames_read;
653
if (missing_frames > 0) {
654
stm->input_linear_buffer->push_silence(missing_frames * stm->input_desc.mChannelsPerFrame);
655
stm->frames_read = input_frames_needed;
656
657
ALOG("(%p) %s pushed %ld frames of input silence.", stm, stm->frames_read == 0 ? "Input hasn't started," :
658
stm->switching_device ? "Device switching," : "Drop out,", missing_frames);
659
}
660
input_buffer = stm->input_linear_buffer->data();
661
// Number of input frames in the buffer. It will change to actually used frames
662
// inside fill
663
input_frames = stm->input_linear_buffer->length() / stm->input_desc.mChannelsPerFrame;
664
}
665
666
/* Call user callback through resampler. */
667
long outframes = cubeb_resampler_fill(stm->resampler.get(),
668
input_buffer,
669
input_buffer ? &input_frames : NULL,
670
output_buffer,
671
output_frames);
672
673
if (input_buffer) {
674
// Pop from the buffer the frames used by the the resampler.
675
stm->input_linear_buffer->pop(input_frames * stm->input_desc.mChannelsPerFrame);
676
}
677
678
if (outframes < 0 || outframes > output_frames) {
679
stm->shutdown = true;
680
OSStatus r = AudioOutputUnitStop(stm->output_unit);
681
assert(r == 0);
682
if (stm->input_unit) {
683
r = AudioOutputUnitStop(stm->input_unit);
684
assert(r == 0);
685
}
686
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
687
audiounit_make_silent(&outBufferList->mBuffers[0]);
688
return noErr;
689
}
690
691
stm->draining = (UInt32) outframes < output_frames;
692
stm->frames_played = stm->frames_queued;
693
stm->frames_queued += outframes;
694
695
AudioFormatFlags outaff = stm->output_desc.mFormatFlags;
696
float panning = (stm->output_desc.mChannelsPerFrame == 2) ?
697
stm->panning.load(memory_order_relaxed) : 0.0f;
698
699
/* Post process output samples. */
700
if (stm->draining) {
701
size_t outbpf = cubeb_sample_size(stm->output_stream_params.format);
702
/* Clear missing frames (silence) */
703
memset((uint8_t*)output_buffer + outframes * outbpf, 0, (output_frames - outframes) * outbpf);
704
}
705
706
/* Mixing */
707
if (stm->mixer) {
708
audiounit_mix_output_buffer(stm,
709
output_frames,
710
output_buffer,
711
stm->temp_buffer_size,
712
outBufferList->mBuffers[0].mData,
713
outBufferList->mBuffers[0].mDataByteSize);
714
} else {
715
/* Pan stereo. */
716
if (panning != 0.0f) {
717
if (outaff & kAudioFormatFlagIsFloat) {
718
cubeb_pan_stereo_buffer_float(
719
(float*)output_buffer, outframes, panning);
720
} else if (outaff & kAudioFormatFlagIsSignedInteger) {
721
cubeb_pan_stereo_buffer_int((short*)output_buffer, outframes, panning);
722
}
723
}
724
}
725
726
return noErr;
727
}
728
729
extern "C" {
730
int
731
audiounit_init(cubeb ** context, char const * /* context_name */)
732
{
733
#if !TARGET_OS_IPHONE
734
cubeb_set_coreaudio_notification_runloop();
735
#endif
736
737
*context = new cubeb;
738
739
return CUBEB_OK;
740
}
741
}
742
743
static char const *
744
audiounit_get_backend_id(cubeb * /* ctx */)
745
{
746
return "audiounit";
747
}
748
749
#if !TARGET_OS_IPHONE
750
751
static int audiounit_stream_get_volume(cubeb_stream * stm, float * volume);
752
static int audiounit_stream_set_volume(cubeb_stream * stm, float volume);
753
754
static int
755
audiounit_set_device_info(cubeb_stream * stm, AudioDeviceID id, io_side side)
756
{
757
assert(stm);
758
759
device_info * info = nullptr;
760
cubeb_device_type type = CUBEB_DEVICE_TYPE_UNKNOWN;
761
762
if (side == io_side::INPUT) {
763
info = &stm->input_device;
764
type = CUBEB_DEVICE_TYPE_INPUT;
765
} else if (side == io_side::OUTPUT) {
766
info = &stm->output_device;
767
type = CUBEB_DEVICE_TYPE_OUTPUT;
768
}
769
memset(info, 0, sizeof(device_info));
770
info->id = id;
771
772
if (side == io_side::INPUT) {
773
info->flags |= DEV_INPUT;
774
} else if (side == io_side::OUTPUT) {
775
info->flags |= DEV_OUTPUT;
776
}
777
778
AudioDeviceID default_device_id = audiounit_get_default_device_id(type);
779
if (default_device_id == kAudioObjectUnknown) {
780
return CUBEB_ERROR;
781
}
782
if (id == kAudioObjectUnknown) {
783
info->id = default_device_id;
784
info->flags |= DEV_SELECTED_DEFAULT;
785
}
786
787
if (info->id == default_device_id) {
788
info->flags |= DEV_SYSTEM_DEFAULT;
789
}
790
791
assert(info->id);
792
assert(info->flags & DEV_INPUT && !(info->flags & DEV_OUTPUT) ||
793
!(info->flags & DEV_INPUT) && info->flags & DEV_OUTPUT);
794
795
return CUBEB_OK;
796
}
797
798
799
static int
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 = flags & DEV_INPUT ? stm->input_device.id : kAudioObjectUnknown;
831
if (flags & DEV_INPUT) {
832
r = audiounit_set_device_info(stm, input_device, io_side::INPUT);
833
if (r != CUBEB_OK) {
834
LOG("(%p) Set input device info failed. This can happen when last media device is unplugged", stm);
835
return CUBEB_ERROR;
836
}
837
}
838
839
/* Always use the default output on reinit. This is not correct in every
840
* case but it is sufficient for Firefox and prevent reinit from reporting
841
* failures. It will change soon when reinit mechanism will be updated. */
842
r = audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::OUTPUT);
843
if (r != CUBEB_OK) {
844
LOG("(%p) Set output device info failed. This can happen when last media device is unplugged", stm);
845
return CUBEB_ERROR;
846
}
847
848
if (audiounit_setup_stream(stm) != CUBEB_OK) {
849
LOG("(%p) Stream reinit failed.", stm);
850
if (flags & DEV_INPUT && input_device != kAudioObjectUnknown) {
851
// Attempt to re-use the same device-id failed, so attempt again with
852
// default input device.
853
audiounit_close_stream(stm);
854
if (audiounit_set_device_info(stm, kAudioObjectUnknown, io_side::INPUT) != CUBEB_OK ||
855
audiounit_setup_stream(stm) != CUBEB_OK) {
856
LOG("(%p) Second stream reinit failed.", stm);
857
return CUBEB_ERROR;
858
}
859
}
860
}
861
862
if (vol_rv == CUBEB_OK) {
863
audiounit_stream_set_volume(stm, volume);
864
}
865
866
// If the stream was running, start it again.
867
if (!stm->shutdown) {
868
r = audiounit_stream_start_internal(stm);
869
if (r != CUBEB_OK) {
870
return CUBEB_ERROR;
871
}
872
}
873
}
874
return CUBEB_OK;
875
}
876
877
static void
878
audiounit_reinit_stream_async(cubeb_stream * stm, device_flags_value flags)
879
{
880
if (std::atomic_exchange(&stm->reinit_pending, true)) {
881
// A reinit task is already pending, nothing more to do.
882
ALOG("(%p) re-init stream task already pending, cancelling request", stm);
883
return;
884
}
885
886
// Use a new thread, through the queue, to avoid deadlock when calling
887
// Get/SetProperties method from inside notify callback
888
dispatch_async(stm->context->serial_queue, ^() {
889
if (stm->destroy_pending) {
890
ALOG("(%p) stream pending destroy, cancelling reinit task", stm);
891
return;
892
}
893
894
if (audiounit_reinit_stream(stm, flags) != CUBEB_OK) {
895
if (audiounit_uninstall_system_changed_callback(stm) != CUBEB_OK) {
896
LOG("(%p) Could not uninstall system changed callback", stm);
897
}
898
stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_ERROR);
899
LOG("(%p) Could not reopen the stream after switching.", stm);
900
}
901
stm->switching_device = false;
902
stm->reinit_pending = false;
903
});
904
}
905
906
static char const *
907
event_addr_to_string(AudioObjectPropertySelector selector)
908
{
909
switch(selector) {
910
case kAudioHardwarePropertyDefaultOutputDevice:
911
return "kAudioHardwarePropertyDefaultOutputDevice";
912
case kAudioHardwarePropertyDefaultInputDevice:
913
return "kAudioHardwarePropertyDefaultInputDevice";
914
case kAudioDevicePropertyDeviceIsAlive:
915
return "kAudioDevicePropertyDeviceIsAlive";
916
case kAudioDevicePropertyDataSource:
917
return "kAudioDevicePropertyDataSource";
918
default:
919
return "Unknown";
920
}
921
}
922
923
static OSStatus
924
audiounit_property_listener_callback(AudioObjectID id, UInt32 address_count,
925
const AudioObjectPropertyAddress * addresses,
926
void * user)
927
{
928
cubeb_stream * stm = (cubeb_stream*) user;
929
if (stm->switching_device) {
930
LOG("Switching is already taking place. Skip Event %s for id=%d", event_addr_to_string(addresses[0].mSelector), id);
931
return noErr;
932
}
933
stm->switching_device = true;
934
935
LOG("(%p) Audio device changed, %u events.", stm, (unsigned int) address_count);
936
for (UInt32 i = 0; i < address_count; i++) {
937
switch(addresses[i].mSelector) {
938
case kAudioHardwarePropertyDefaultOutputDevice: {
939
LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultOutputDevice for id=%d", (unsigned int) i, id);
940
}
941
break;
942
case kAudioHardwarePropertyDefaultInputDevice: {
943
LOG("Event[%u] - mSelector == kAudioHardwarePropertyDefaultInputDevice for id=%d", (unsigned int) i, id);
944
}
945
break;
946
case kAudioDevicePropertyDeviceIsAlive: {
947
LOG("Event[%u] - mSelector == kAudioDevicePropertyDeviceIsAlive for id=%d", (unsigned int) i, id);
948
// If this is the default input device ignore the event,
949
// kAudioHardwarePropertyDefaultInputDevice will take care of the switch
950
if (stm->input_device.flags & DEV_SYSTEM_DEFAULT) {
951
LOG("It's the default input device, ignore the event");
952
stm->switching_device = false;
953
return noErr;
954
}
955
}
956
break;
957
case kAudioDevicePropertyDataSource: {
958
LOG("Event[%u] - mSelector == kAudioDevicePropertyDataSource for id=%d", (unsigned int) i, id);
959
}
960
break;
961
default:
962
LOG("Event[%u] - mSelector == Unexpected Event id %d, return", (unsigned int) i, addresses[i].mSelector);
963
stm->switching_device = false;
964
return noErr;
965
}
966
}
967
968
// Allow restart to choose the new default
969
device_flags_value switch_side = DEV_UNKNOWN;
970
if (has_input(stm)) {
971
switch_side |= DEV_INPUT;
972
}
973
if (has_output(stm)) {
974
switch_side |= DEV_OUTPUT;
975
}
976
977
for (UInt32 i = 0; i < address_count; i++) {
978
switch(addresses[i].mSelector) {
979
case kAudioHardwarePropertyDefaultOutputDevice:
980
case kAudioHardwarePropertyDefaultInputDevice:
981
case kAudioDevicePropertyDeviceIsAlive:
982
/* fall through */
983
case kAudioDevicePropertyDataSource: {
984
auto_lock dev_cb_lock(stm->device_changed_callback_lock);
985
if (stm->device_changed_callback) {
986
stm->device_changed_callback(stm->user_ptr);
987
}
988
break;
989
}
990
}
991
}
992
993
audiounit_reinit_stream_async(stm, switch_side);
994
995
return noErr;
996
}
997
998
OSStatus
999
audiounit_add_listener(const property_listener * listener)
1000
{
1001
assert(listener);
1002
return AudioObjectAddPropertyListener(listener->device_id,
1003
listener->property_address,
1004
listener->callback,
1005
listener->stream);
1006
}
1007
1008
OSStatus
1009
audiounit_remove_listener(const property_listener * listener)
1010
{
1011
assert(listener);
1012
return AudioObjectRemovePropertyListener(listener->device_id,
1013
listener->property_address,
1014
listener->callback,
1015
listener->stream);
1016
}
1017
1018
static int
1019
audiounit_install_device_changed_callback(cubeb_stream * stm)
1020
{
1021
OSStatus rv;
1022
int r = CUBEB_OK;
1023
1024
if (stm->output_unit) {
1025
/* This event will notify us when the data source on the same device changes,
1026
* for example when the user plugs in a normal (non-usb) headset in the
1027
* headphone jack. */
1028
stm->output_source_listener.reset(new property_listener(
1029
stm->output_device.id, &OUTPUT_DATA_SOURCE_PROPERTY_ADDRESS,
1030
&audiounit_property_listener_callback, stm));
1031
rv = audiounit_add_listener(stm->output_source_listener.get());
1032
if (rv != noErr) {
1033
stm->output_source_listener.reset();
1034
LOG("AudioObjectAddPropertyListener/output/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->output_device.id);
1035
r = CUBEB_ERROR;
1036
}
1037
}
1038
1039
if (stm->input_unit) {
1040
/* This event will notify us when the data source on the input device changes. */
1041
stm->input_source_listener.reset(new property_listener(
1042
stm->input_device.id, &INPUT_DATA_SOURCE_PROPERTY_ADDRESS,
1043
&audiounit_property_listener_callback, stm));
1044
rv = audiounit_add_listener(stm->input_source_listener.get());
1045
if (rv != noErr) {
1046
stm->input_source_listener.reset();
1047
LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->input_device.id);
1048
r = CUBEB_ERROR;
1049
}
1050
1051
/* Event to notify when the input is going away. */
1052
stm->input_alive_listener.reset(new property_listener(
1053
stm->input_device.id, &DEVICE_IS_ALIVE_PROPERTY_ADDRESS,
1054
&audiounit_property_listener_callback, stm));
1055
rv = audiounit_add_listener(stm->input_alive_listener.get());
1056
if (rv != noErr) {
1057
stm->input_alive_listener.reset();
1058
LOG("AudioObjectAddPropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv=%d, device id =%d", rv, stm->input_device.id);
1059
r = CUBEB_ERROR;
1060
}
1061
}
1062
1063
return r;
1064
}
1065
1066
static int
1067
audiounit_install_system_changed_callback(cubeb_stream * stm)
1068
{
1069
OSStatus r;
1070
1071
if (stm->output_unit) {
1072
/* This event will notify us when the default audio device changes,
1073
* for example when the user plugs in a USB headset and the system chooses it
1074
* automatically as the default, or when another device is chosen in the
1075
* dropdown list. */
1076
stm->default_output_listener.reset(new property_listener(
1077
kAudioObjectSystemObject, &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS,
1078
&audiounit_property_listener_callback, stm));
1079
r = audiounit_add_listener(stm->default_output_listener.get());
1080
if (r != noErr) {
1081
stm->default_output_listener.reset();
1082
LOG("AudioObjectAddPropertyListener/output/kAudioHardwarePropertyDefaultOutputDevice rv=%d", r);
1083
return CUBEB_ERROR;
1084
}
1085
}
1086
1087
if (stm->input_unit) {
1088
/* This event will notify us when the default input device changes. */
1089
stm->default_input_listener.reset(new property_listener(
1090
kAudioObjectSystemObject, &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS,
1091
&audiounit_property_listener_callback, stm));
1092
r = audiounit_add_listener(stm->default_input_listener.get());
1093
if (r != noErr) {
1094
stm->default_input_listener.reset();
1095
LOG("AudioObjectAddPropertyListener/input/kAudioHardwarePropertyDefaultInputDevice rv=%d", r);
1096
return CUBEB_ERROR;
1097
}
1098
}
1099
1100
return CUBEB_OK;
1101
}
1102
1103
static int
1104
audiounit_uninstall_device_changed_callback(cubeb_stream * stm)
1105
{
1106
OSStatus rv;
1107
// Failing to uninstall listeners is not a fatal error.
1108
int r = CUBEB_OK;
1109
1110
if (stm->output_source_listener) {
1111
rv = audiounit_remove_listener(stm->output_source_listener.get());
1112
if (rv != noErr) {
1113
LOG("AudioObjectRemovePropertyListener/output/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->output_device.id);
1114
r = CUBEB_ERROR;
1115
}
1116
stm->output_source_listener.reset();
1117
}
1118
1119
if (stm->input_source_listener) {
1120
rv = audiounit_remove_listener(stm->input_source_listener.get());
1121
if (rv != noErr) {
1122
LOG("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDataSource rv=%d, device id=%d", rv, stm->input_device.id);
1123
r = CUBEB_ERROR;
1124
}
1125
stm->input_source_listener.reset();
1126
}
1127
1128
if (stm->input_alive_listener) {
1129
rv = audiounit_remove_listener(stm->input_alive_listener.get());
1130
if (rv != noErr) {
1131
LOG("AudioObjectRemovePropertyListener/input/kAudioDevicePropertyDeviceIsAlive rv=%d, device id=%d", rv, stm->input_device.id);
1132
r = CUBEB_ERROR;
1133
}
1134
stm->input_alive_listener.reset();
1135
}
1136
1137
return r;
1138
}
1139
1140
static int
1141
audiounit_uninstall_system_changed_callback(cubeb_stream * stm)
1142
{
1143
OSStatus r;
1144
1145
if (stm->default_output_listener) {
1146
r = audiounit_remove_listener(stm->default_output_listener.get());
1147
if (r != noErr) {
1148
return CUBEB_ERROR;
1149
}
1150
stm->default_output_listener.reset();
1151
}
1152
1153
if (stm->default_input_listener) {
1154
r = audiounit_remove_listener(stm->default_input_listener.get());
1155
if (r != noErr) {
1156
return CUBEB_ERROR;
1157
}
1158
stm->default_input_listener.reset();
1159
}
1160
return CUBEB_OK;
1161
}
1162
1163
/* Get the acceptable buffer size (in frames) that this device can work with. */
1164
static int
1165
audiounit_get_acceptable_latency_range(AudioValueRange * latency_range)
1166
{
1167
UInt32 size;
1168
OSStatus r;
1169
AudioDeviceID output_device_id;
1170
AudioObjectPropertyAddress output_device_buffer_size_range = {
1171
kAudioDevicePropertyBufferFrameSizeRange,
1172
kAudioDevicePropertyScopeOutput,
1173
kAudioObjectPropertyElementMaster
1174
};
1175
1176
output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
1177
if (output_device_id == kAudioObjectUnknown) {
1178
LOG("Could not get default output device id.");
1179
return CUBEB_ERROR;
1180
}
1181
1182
/* Get the buffer size range this device supports */
1183
size = sizeof(*latency_range);
1184
1185
r = AudioObjectGetPropertyData(output_device_id,
1186
&output_device_buffer_size_range,
1187
0,
1188
NULL,
1189
&size,
1190
latency_range);
1191
if (r != noErr) {
1192
LOG("AudioObjectGetPropertyData/buffer size range rv=%d", r);
1193
return CUBEB_ERROR;
1194
}
1195
1196
return CUBEB_OK;
1197
}
1198
#endif /* !TARGET_OS_IPHONE */
1199
1200
static AudioObjectID
1201
audiounit_get_default_device_id(cubeb_device_type type)
1202
{
1203
const AudioObjectPropertyAddress * adr;
1204
if (type == CUBEB_DEVICE_TYPE_OUTPUT) {
1205
adr = &DEFAULT_OUTPUT_DEVICE_PROPERTY_ADDRESS;
1206
} else if (type == CUBEB_DEVICE_TYPE_INPUT) {
1207
adr = &DEFAULT_INPUT_DEVICE_PROPERTY_ADDRESS;
1208
} else {
1209
return kAudioObjectUnknown;
1210
}
1211
1212
AudioDeviceID devid;
1213
UInt32 size = sizeof(AudioDeviceID);
1214
if (AudioObjectGetPropertyData(kAudioObjectSystemObject,
1215
adr, 0, NULL, &size, &devid) != noErr) {
1216
return kAudioObjectUnknown;
1217
}
1218
1219
return devid;
1220
}
1221
1222
int
1223
audiounit_get_max_channel_count(cubeb * ctx, uint32_t * max_channels)
1224
{
1225
#if TARGET_OS_IPHONE
1226
//TODO: [[AVAudioSession sharedInstance] maximumOutputNumberOfChannels]
1227
*max_channels = 2;
1228
#else
1229
UInt32 size;
1230
OSStatus r;
1231
AudioDeviceID output_device_id;
1232
AudioStreamBasicDescription stream_format;
1233
AudioObjectPropertyAddress stream_format_address = {
1234
kAudioDevicePropertyStreamFormat,
1235
kAudioDevicePropertyScopeOutput,
1236
kAudioObjectPropertyElementMaster
1237
};
1238
1239
assert(ctx && max_channels);
1240
1241
output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
1242
if (output_device_id == kAudioObjectUnknown) {
1243
return CUBEB_ERROR;
1244
}
1245
1246
size = sizeof(stream_format);
1247
1248
r = AudioObjectGetPropertyData(output_device_id,
1249
&stream_format_address,
1250
0,
1251
NULL,
1252
&size,
1253
&stream_format);
1254
if (r != noErr) {
1255
LOG("AudioObjectPropertyAddress/StreamFormat rv=%d", r);
1256
return CUBEB_ERROR;
1257
}
1258
1259
*max_channels = stream_format.mChannelsPerFrame;
1260
#endif
1261
return CUBEB_OK;
1262
}
1263
1264
static int
1265
audiounit_get_min_latency(cubeb * /* ctx */,
1266
cubeb_stream_params /* params */,
1267
uint32_t * latency_frames)
1268
{
1269
#if TARGET_OS_IPHONE
1270
//TODO: [[AVAudioSession sharedInstance] inputLatency]
1271
return CUBEB_ERROR_NOT_SUPPORTED;
1272
#else
1273
AudioValueRange latency_range;
1274
if (audiounit_get_acceptable_latency_range(&latency_range) != CUBEB_OK) {
1275
LOG("Could not get acceptable latency range.");
1276
return CUBEB_ERROR;
1277
}
1278
1279
*latency_frames = max<uint32_t>(latency_range.mMinimum,
1280
SAFE_MIN_LATENCY_FRAMES);
1281
#endif
1282
1283
return CUBEB_OK;
1284
}
1285
1286
static int
1287
audiounit_get_preferred_sample_rate(cubeb * /* ctx */, uint32_t * rate)
1288
{
1289
#if TARGET_OS_IPHONE
1290
//TODO
1291
return CUBEB_ERROR_NOT_SUPPORTED;
1292
#else
1293
UInt32 size;
1294
OSStatus r;
1295
Float64 fsamplerate;
1296
AudioDeviceID output_device_id;
1297
AudioObjectPropertyAddress samplerate_address = {
1298
kAudioDevicePropertyNominalSampleRate,
1299
kAudioObjectPropertyScopeGlobal,
1300
kAudioObjectPropertyElementMaster
1301
};
1302
1303
output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
1304
if (output_device_id == kAudioObjectUnknown) {
1305
return CUBEB_ERROR;
1306
}
1307
1308
size = sizeof(fsamplerate);
1309
r = AudioObjectGetPropertyData(output_device_id,
1310
&samplerate_address,
1311
0,
1312
NULL,
1313
&size,
1314
&fsamplerate);
1315
1316
if (r != noErr) {
1317
return CUBEB_ERROR;
1318
}
1319
1320
*rate = static_cast<uint32_t>(fsamplerate);
1321
#endif
1322
return CUBEB_OK;
1323
}
1324
1325
static cubeb_channel_layout
1326
audiounit_convert_channel_layout(AudioChannelLayout * layout)
1327
{
1328
// When having one or two channel, force mono or stereo. Some devices (namely,
1329
// Bose QC35, mark 1 and 2), expose a single channel mapped to the right for
1330
// some reason.
1331
if (layout->mNumberChannelDescriptions == 1) {
1332
return CUBEB_LAYOUT_MONO;
1333
} else if (layout->mNumberChannelDescriptions == 2) {
1334
return CUBEB_LAYOUT_STEREO;
1335
}
1336
1337
if (layout->mChannelLayoutTag != kAudioChannelLayoutTag_UseChannelDescriptions) {
1338
// kAudioChannelLayoutTag_UseChannelBitmap
1339
// kAudioChannelLayoutTag_Mono
1340
// kAudioChannelLayoutTag_Stereo
1341
// ....
1342
LOG("Only handle UseChannelDescriptions for now.\n");
1343
return CUBEB_LAYOUT_UNDEFINED;
1344
}
1345
1346
cubeb_channel_layout cl = 0;
1347
for (UInt32 i = 0; i < layout->mNumberChannelDescriptions; ++i) {
1348
cubeb_channel cc = channel_label_to_cubeb_channel(
1349
layout->mChannelDescriptions[i].mChannelLabel);
1350
if (cc == CHANNEL_UNKNOWN) {
1351
return CUBEB_LAYOUT_UNDEFINED;
1352
}
1353
cl |= cc;
1354
}
1355
1356
return cl;
1357
}
1358
1359
static cubeb_channel_layout
1360
audiounit_get_preferred_channel_layout(AudioUnit output_unit)
1361
{
1362
OSStatus rv = noErr;
1363
UInt32 size = 0;
1364
rv = AudioUnitGetPropertyInfo(output_unit,
1365
kAudioDevicePropertyPreferredChannelLayout,
1366
kAudioUnitScope_Output,
1367
AU_OUT_BUS,
1368
&size,
1369
nullptr);
1370
if (rv != noErr) {
1371
LOG("AudioUnitGetPropertyInfo/kAudioDevicePropertyPreferredChannelLayout rv=%d", rv);
1372
return CUBEB_LAYOUT_UNDEFINED;
1373
}
1374
assert(size > 0);
1375
1376
auto layout = make_sized_audio_channel_layout(size);
1377
rv = AudioUnitGetProperty(output_unit,
1378
kAudioDevicePropertyPreferredChannelLayout,
1379
kAudioUnitScope_Output,
1380
AU_OUT_BUS,
1381
layout.get(),
1382
&size);
1383
if (rv != noErr) {
1384
LOG("AudioUnitGetProperty/kAudioDevicePropertyPreferredChannelLayout rv=%d", rv);
1385
return CUBEB_LAYOUT_UNDEFINED;
1386
}
1387
1388
return audiounit_convert_channel_layout(layout.get());
1389
}
1390
1391
static cubeb_channel_layout
1392
audiounit_get_current_channel_layout(AudioUnit output_unit)
1393
{
1394
OSStatus rv = noErr;
1395
UInt32 size = 0;
1396
rv = AudioUnitGetPropertyInfo(output_unit,
1397
kAudioUnitProperty_AudioChannelLayout,
1398
kAudioUnitScope_Output,
1399
AU_OUT_BUS,
1400
&size,
1401
nullptr);
1402
if (rv != noErr) {
1403
LOG("AudioUnitGetPropertyInfo/kAudioUnitProperty_AudioChannelLayout rv=%d", rv);
1404
// This property isn't known before macOS 10.12, attempt another method.
1405
return audiounit_get_preferred_channel_layout(output_unit);
1406
}
1407
assert(size > 0);
1408
1409
auto layout = make_sized_audio_channel_layout(size);
1410
rv = AudioUnitGetProperty(output_unit,
1411
kAudioUnitProperty_AudioChannelLayout,
1412
kAudioUnitScope_Output,
1413
AU_OUT_BUS,
1414
layout.get(),
1415
&size);
1416
if (rv != noErr) {
1417
LOG("AudioUnitGetProperty/kAudioUnitProperty_AudioChannelLayout rv=%d", rv);
1418
return CUBEB_LAYOUT_UNDEFINED;
1419
}
1420
1421
return audiounit_convert_channel_layout(layout.get());
1422
}
1423
1424
static int audiounit_create_unit(AudioUnit * unit, device_info * device);
1425
1426
static OSStatus audiounit_remove_device_listener(cubeb * context, cubeb_device_type devtype);
1427
1428
static void
1429
audiounit_destroy(cubeb * ctx)
1430
{
1431
{
1432
auto_lock lock(ctx->mutex);
1433
1434
// Disabling this assert for bug 1083664 -- we seem to leak a stream
1435
// assert(ctx->active_streams == 0);
1436
if (audiounit_active_streams(ctx) > 0) {
1437
LOG("(%p) API misuse, %d streams active when context destroyed!", ctx, audiounit_active_streams(ctx));
1438
}
1439
1440
/* Unregister the callback if necessary. */
1441
if (ctx->input_collection_changed_callback) {
1442
audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_INPUT);
1443
}
1444
if (ctx->output_collection_changed_callback) {
1445
audiounit_remove_device_listener(ctx, CUBEB_DEVICE_TYPE_OUTPUT);
1446
}
1447
}
1448
1449
dispatch_release(ctx->serial_queue);
1450
1451
delete ctx;
1452
}
1453
1454
static void audiounit_stream_destroy(cubeb_stream * stm);
1455
1456
static int
1457
audio_stream_desc_init(AudioStreamBasicDescription * ss,
1458
const cubeb_stream_params * stream_params)
1459
{
1460
switch (stream_params->format) {
1461
case CUBEB_SAMPLE_S16LE:
1462
ss->mBitsPerChannel = 16;
1463
ss->mFormatFlags = kAudioFormatFlagIsSignedInteger;
1464
break;
1465
case CUBEB_SAMPLE_S16BE:
1466
ss->mBitsPerChannel = 16;
1467
ss->mFormatFlags = kAudioFormatFlagIsSignedInteger |
1468
kAudioFormatFlagIsBigEndian;
1469
break;
1470
case CUBEB_SAMPLE_FLOAT32LE:
1471
ss->mBitsPerChannel = 32;
1472
ss->mFormatFlags = kAudioFormatFlagIsFloat;
1473
break;
1474
case CUBEB_SAMPLE_FLOAT32BE:
1475
ss->mBitsPerChannel = 32;
1476
ss->mFormatFlags = kAudioFormatFlagIsFloat |
1477
kAudioFormatFlagIsBigEndian;
1478
break;
1479
default:
1480
return CUBEB_ERROR_INVALID_FORMAT;
1481
}
1482
1483
ss->mFormatID = kAudioFormatLinearPCM;
1484
ss->mFormatFlags |= kLinearPCMFormatFlagIsPacked;
1485
ss->mSampleRate = stream_params->rate;
1486
ss->mChannelsPerFrame = stream_params->channels;
1487
1488
ss->mBytesPerFrame = (ss->mBitsPerChannel / 8) * ss->mChannelsPerFrame;
1489
ss->mFramesPerPacket = 1;
1490
ss->mBytesPerPacket = ss->mBytesPerFrame * ss->mFramesPerPacket;
1491
1492
ss->mReserved = 0;
1493
1494
return CUBEB_OK;
1495
}
1496
1497
void
1498
audiounit_init_mixer(cubeb_stream * stm)
1499
{
1500
// We can't rely on macOS' AudioUnit to properly downmix (or upmix) the audio
1501
// data, it silently drop the channels so we need to remix the
1502
// audio data by ourselves to keep all the information.
1503
stm->mixer.reset(cubeb_mixer_create(stm->output_stream_params.format,
1504
stm->output_stream_params.channels,
1505
stm->output_stream_params.layout,
1506
stm->context->channels,
1507
stm->context->layout));
1508
assert(stm->mixer);
1509
}
1510
1511
static int
1512
audiounit_set_channel_layout(AudioUnit unit,
1513
io_side side,
1514
cubeb_channel_layout layout)
1515
{
1516
if (side != io_side::OUTPUT) {
1517
return CUBEB_ERROR;
1518
}
1519
1520
if (layout == CUBEB_LAYOUT_UNDEFINED) {
1521
// We leave everything as-is...
1522
return CUBEB_OK;
1523
}
1524
1525
1526
OSStatus r;
1527
uint32_t nb_channels = cubeb_channel_layout_nb_channels(layout);
1528
1529
// We do not use CoreAudio standard layout for lack of documentation on what
1530
// the actual channel orders are. So we set a custom layout.
1531
size_t size = offsetof(AudioChannelLayout, mChannelDescriptions[nb_channels]);
1532
auto au_layout = make_sized_audio_channel_layout(size);
1533
au_layout->mChannelLayoutTag = kAudioChannelLayoutTag_UseChannelDescriptions;
1534
au_layout->mNumberChannelDescriptions = nb_channels;
1535
1536
uint32_t channels = 0;
1537
cubeb_channel_layout channelMap = layout;
1538
for (uint32_t i = 0; channelMap != 0; ++i) {
1539
XASSERT(channels < nb_channels);
1540
uint32_t channel = (channelMap & 1) << i;
1541
if (channel != 0) {
1542
au_layout->mChannelDescriptions[channels].mChannelLabel =
1543
cubeb_channel_to_channel_label(static_cast<cubeb_channel>(channel));
1544
au_layout->mChannelDescriptions[channels].mChannelFlags = kAudioChannelFlags_AllOff;
1545
channels++;
1546
}
1547
channelMap = channelMap >> 1;
1548
}
1549
1550
r = AudioUnitSetProperty(unit,
1551
kAudioUnitProperty_AudioChannelLayout,
1552
kAudioUnitScope_Input,
1553
AU_OUT_BUS,
1554
au_layout.get(),
1555
size);
1556
if (r != noErr) {
1557
LOG("AudioUnitSetProperty/%s/kAudioUnitProperty_AudioChannelLayout rv=%d", to_string(side), r);
1558
return CUBEB_ERROR;
1559
}
1560
1561
return CUBEB_OK;
1562
}
1563
1564
void
1565
audiounit_layout_init(cubeb_stream * stm, io_side side)
1566
{
1567
// We currently don't support the input layout setting.
1568
if (side == io_side::INPUT) {
1569
return;
1570
}
1571
1572
stm->context->layout = audiounit_get_current_channel_layout(stm->output_unit);
1573
1574
audiounit_set_channel_layout(stm->output_unit, io_side::OUTPUT, stm->context->layout);
1575
}
1576
1577
static vector<AudioObjectID>
1578
audiounit_get_sub_devices(AudioDeviceID device_id)
1579
{
1580
vector<AudioDeviceID> sub_devices;
1581
AudioObjectPropertyAddress property_address = { kAudioAggregateDevicePropertyActiveSubDeviceList,
1582
kAudioObjectPropertyScopeGlobal,
1583
kAudioObjectPropertyElementMaster };
1584
UInt32 size = 0;
1585
OSStatus rv = AudioObjectGetPropertyDataSize(device_id,
1586
&property_address,
1587
0,
1588
nullptr,
1589
&size);
1590
1591
if (rv != noErr) {
1592
sub_devices.push_back(device_id);
1593
return sub_devices;
1594
}
1595
1596
uint32_t count = static_cast<uint32_t>(size / sizeof(AudioObjectID));
1597
sub_devices.resize(count);
1598
rv = AudioObjectGetPropertyData(device_id,
1599
&property_address,
1600
0,
1601
nullptr,
1602
&size,
1603
sub_devices.data());
1604
if (rv != noErr) {
1605
sub_devices.clear();
1606
sub_devices.push_back(device_id);
1607
} else {
1608
LOG("Found %u sub-devices", count);
1609
}
1610
return sub_devices;
1611
}
1612
1613
static int
1614
audiounit_create_blank_aggregate_device(AudioObjectID * plugin_id, AudioDeviceID * aggregate_device_id)
1615
{
1616
AudioObjectPropertyAddress address_plugin_bundle_id = { kAudioHardwarePropertyPlugInForBundleID,
1617
kAudioObjectPropertyScopeGlobal,
1618
kAudioObjectPropertyElementMaster };
1619
UInt32 size = 0;
1620
OSStatus r = AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
1621
&address_plugin_bundle_id,
1622
0, NULL,
1623
&size);
1624
if (r != noErr) {
1625
LOG("AudioObjectGetPropertyDataSize/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
1626
return CUBEB_ERROR;
1627
}
1628
1629
AudioValueTranslation translation_value;
1630
CFStringRef in_bundle_ref = CFSTR("com.apple.audio.CoreAudio");
1631
translation_value.mInputData = &in_bundle_ref;
1632
translation_value.mInputDataSize = sizeof(in_bundle_ref);
1633
translation_value.mOutputData = plugin_id;
1634
translation_value.mOutputDataSize = sizeof(*plugin_id);
1635
1636
r = AudioObjectGetPropertyData(kAudioObjectSystemObject,
1637
&address_plugin_bundle_id,
1638
0,
1639
nullptr,
1640
&size,
1641
&translation_value);
1642
if (r != noErr) {
1643
LOG("AudioObjectGetPropertyData/kAudioHardwarePropertyPlugInForBundleID, rv=%d", r);
1644
return CUBEB_ERROR;
1645
}
1646
1647
AudioObjectPropertyAddress create_aggregate_device_address = { kAudioPlugInCreateAggregateDevice,
1648
kAudioObjectPropertyScopeGlobal,
1649
kAudioObjectPropertyElementMaster };
1650
r = AudioObjectGetPropertyDataSize(*plugin_id,
1651
&create_aggregate_device_address,
1652
0,
1653
nullptr,
1654
&size);
1655
if (r != noErr) {
1656
LOG("AudioObjectGetPropertyDataSize/kAudioPlugInCreateAggregateDevice, rv=%d", r);
1657
return CUBEB_ERROR;
1658
}
1659
1660
CFMutableDictionaryRef aggregate_device_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
1661
&kCFTypeDictionaryKeyCallBacks,
1662
&kCFTypeDictionaryValueCallBacks);
1663
struct timeval timestamp;
1664
gettimeofday(&timestamp, NULL);
1665
long long int time_id = timestamp.tv_sec * 1000000LL + timestamp.tv_usec;
1666
CFStringRef aggregate_device_name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
1667
CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceNameKey), aggregate_device_name);
1668
CFRelease(aggregate_device_name);
1669
1670
CFStringRef aggregate_device_UID = CFStringCreateWithFormat(NULL, NULL, CFSTR("org.mozilla.%s_%llx"), PRIVATE_AGGREGATE_DEVICE_NAME, time_id);
1671
CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceUIDKey), aggregate_device_UID);
1672
CFRelease(aggregate_device_UID);
1673
1674
int private_value = 1;
1675
CFNumberRef aggregate_device_private_key = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &private_value);
1676
CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceIsPrivateKey), aggregate_device_private_key);
1677
CFRelease(aggregate_device_private_key);
1678
1679
int stacked_value = 0;
1680
CFNumberRef aggregate_device_stacked_key = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &stacked_value);
1681
CFDictionaryAddValue(aggregate_device_dict, CFSTR(kAudioAggregateDeviceIsStackedKey), aggregate_device_stacked_key);
1682
CFRelease(aggregate_device_stacked_key);
1683
1684
r = AudioObjectGetPropertyData(*plugin_id,
1685
&create_aggregate_device_address,
1686
sizeof(aggregate_device_dict),
1687
&aggregate_device_dict,
1688
&size,
1689
aggregate_device_id);
1690
CFRelease(aggregate_device_dict);
1691
if (r != noErr) {
1692
LOG("AudioObjectGetPropertyData/kAudioPlugInCreateAggregateDevice, rv=%d", r);
1693
return CUBEB_ERROR;
1694
}
1695
LOG("New aggregate device %u", *aggregate_device_id);
1696
1697
return CUBEB_OK;
1698
}
1699
1700
// The returned CFStringRef object needs to be released (via CFRelease)
1701
// if it's not NULL, since the reference count of the returned CFStringRef
1702
// object is increased.
1703
static CFStringRef
1704
get_device_name(AudioDeviceID id)
1705
{
1706
UInt32 size = sizeof(CFStringRef);
1707
CFStringRef UIname = nullptr;
1708
AudioObjectPropertyAddress address_uuid = { kAudioDevicePropertyDeviceUID,
1709
kAudioObjectPropertyScopeGlobal,
1710
kAudioObjectPropertyElementMaster };
1711
OSStatus err = AudioObjectGetPropertyData(id, &address_uuid, 0, nullptr, &size, &UIname);
1712
return (err == noErr) ? UIname : NULL;
1713
}
1714
1715
static int
1716
audiounit_set_aggregate_sub_device_list(AudioDeviceID aggregate_device_id,
1717
AudioDeviceID input_device_id,
1718
AudioDeviceID output_device_id)
1719
{
1720
LOG("Add devices input %u and output %u into aggregate device %u",
1721
input_device_id, output_device_id, aggregate_device_id);
1722
const vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices(output_device_id);
1723
const vector<AudioDeviceID> input_sub_devices = audiounit_get_sub_devices(input_device_id);
1724
1725
CFMutableArrayRef aggregate_sub_devices_array = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
1726
/* The order of the items in the array is significant and is used to determine the order of the streams
1727
of the AudioAggregateDevice. */
1728
for (UInt32 i = 0; i < output_sub_devices.size(); i++) {
1729
CFStringRef ref = get_device_name(output_sub_devices[i]);
1730
if (ref == NULL) {
1731
CFRelease(aggregate_sub_devices_array);
1732
return CUBEB_ERROR;
1733
}
1734
CFArrayAppendValue(aggregate_sub_devices_array, ref);
1735
CFRelease(ref);
1736
}
1737
for (UInt32 i = 0; i < input_sub_devices.size(); i++) {
1738
CFStringRef ref = get_device_name(input_sub_devices[i]);
1739
if (ref == NULL) {
1740
CFRelease(aggregate_sub_devices_array);
1741
return CUBEB_ERROR;
1742
}
1743
CFArrayAppendValue(aggregate_sub_devices_array, ref);
1744
CFRelease(ref);
1745
}
1746
1747
AudioObjectPropertyAddress aggregate_sub_device_list = { kAudioAggregateDevicePropertyFullSubDeviceList,
1748
kAudioObjectPropertyScopeGlobal,
1749
kAudioObjectPropertyElementMaster };
1750
UInt32 size = sizeof(CFMutableArrayRef);
1751
OSStatus rv = AudioObjectSetPropertyData(aggregate_device_id,
1752
&aggregate_sub_device_list,
1753
0,
1754
nullptr,
1755
size,
1756
&aggregate_sub_devices_array);
1757
CFRelease(aggregate_sub_devices_array);
1758
if (rv != noErr) {
1759
LOG("AudioObjectSetPropertyData/kAudioAggregateDevicePropertyFullSubDeviceList, rv=%d", rv);
1760
return CUBEB_ERROR;
1761
}
1762
1763
return CUBEB_OK;
1764
}
1765
1766
static int
1767
audiounit_set_master_aggregate_device(const AudioDeviceID aggregate_device_id)
1768
{
1769
assert(aggregate_device_id != kAudioObjectUnknown);
1770
AudioObjectPropertyAddress master_aggregate_sub_device = { kAudioAggregateDevicePropertyMasterSubDevice,
1771
kAudioObjectPropertyScopeGlobal,
1772
kAudioObjectPropertyElementMaster };
1773
1774
// Master become the 1st output sub device
1775
AudioDeviceID output_device_id = audiounit_get_default_device_id(CUBEB_DEVICE_TYPE_OUTPUT);
1776
const vector<AudioDeviceID> output_sub_devices = audiounit_get_sub_devices(output_device_id);
1777
CFStringRef master_sub_device = get_device_name(output_sub_devices[0]);
1778
1779
UInt32 size = sizeof(CFStringRef);
1780
OSStatus rv = AudioObjectSetPropertyData(aggregate_device_id,
1781
&master_aggregate_sub_device,
1782
0,
1783
NULL,
1784
size,
1785
&master_sub_device);
1786
if (master_sub_device) {
1787
CFRelease(master_sub_device);
1788
}
1789
if (rv != noErr) {
1790
LOG("AudioObjectSetPropertyData/kAudioAggregateDevicePropertyMasterSubDevice, rv=%d", rv);
1791
return CUBEB_ERROR;
1792
}
1793
1794
return CUBEB_OK;
1795
}
1796
1797
static int
1798
audiounit_activate_clock_drift_compensation(const AudioDeviceID aggregate_device_id)
1799
{
1800
assert(aggregate_device_id != kAudioObjectUnknown);
1801
AudioObjectPropertyAddress address_owned = { kAudioObjectPropertyOwnedObjects,
1802
kAudioObjectPropertyScopeGlobal,
1803
kAudioObjectPropertyElementMaster };
1804
1805
UInt32 qualifier_data_size = sizeof(AudioObjectID);
1806
AudioClassID class_id = kAudioSubDeviceClassID;
1807
void * qualifier_data = &class_id;
1808
UInt32 size = 0;
1809
OSStatus rv = AudioObjectGetPropertyDataSize(aggregate_device_id,
1810
&address_owned,
1811
qualifier_data_size,
1812
qualifier_data,
1813
&size);
1814
if (rv != noErr) {
1815
LOG("AudioObjectGetPropertyDataSize/kAudioObjectPropertyOwnedObjects, rv=%d", rv);
1816
return CUBEB_ERROR;
1817
}
1818
1819
UInt32 subdevices_num = 0;
1820
subdevices_num = size / sizeof(AudioObjectID);
1821
AudioObjectID sub_devices[subdevices_num];
1822
size = sizeof(sub_devices);
1823
1824
rv = AudioObjectGetPropertyData(aggregate_device_id,
1825
&address_owned,
1826
qualifier_data_size,
1827
qualifier_data,
1828
&size,
1829
sub_devices);
1830
if (rv != noErr) {
1831
LOG("AudioObjectGetPropertyData/kAudioObjectPropertyOwnedObjects, rv=%d", rv);
1832
return CUBEB_ERROR;
1833
}
1834
1835
AudioObjectPropertyAddress address_drift = { kAudioSubDevicePropertyDriftCompensation,
1836
kAudioObjectPropertyScopeGlobal,
1837
kAudioObjectPropertyElementMaster };
1838
1839
// Start from the second device since the first is the master clock
1840
for (UInt32 i = 1; i < subdevices_num; ++i) {
1841
UInt32 drift_compensation_value = 1;
1842
rv = AudioObjectSetPropertyData(sub_devices[i],
1843
&address_drift,
1844
0,
1845
nullptr,
1846
sizeof(UInt32),
1847
&drift_compensation_value);
1848
if (rv != noErr) {
1849
LOG("AudioObjectSetPropertyData/kAudioSubDevicePropertyDriftCompensation, rv=%d", rv);
1850
return CUBEB_OK;
1851
}
1852
}
1853
return CUBEB_OK;
1854
}
1855
1856
static int audiounit_destroy_aggregate_device(AudioObjectID plugin_id, AudioDeviceID * aggregate_device_id);
1857
static void audiounit_get_available_samplerate(AudioObjectID devid, AudioObjectPropertyScope scope,
1858
uint32_t * min, uint32_t * max, uint32_t * def);
1859
static int
1860
audiounit_create_device_from_hwdev(cubeb_device_info * dev_info, AudioObjectID devid, cubeb_device_type type);
1861
static void audiounit_device_destroy(cubeb_device_info * device);
1862
1863
static void
1864
audiounit_workaround_for_airpod(cubeb_stream * stm)
1865
{
1866
cubeb_device_info input_device_info;
1867
audiounit_create_device_from_hwdev(&input_device_info, stm->input_device.id, CUBEB_DEVICE_TYPE_INPUT);
1868
1869
cubeb_device_info output_device_info;
1870
audiounit_create_device_from_hwdev(&output_device_info, stm->output_device.id, CUBEB_DEVICE_TYPE_OUTPUT);
1871
1872
std::string input_name_str(input_device_info.friendly_name);
1873
std::string output_name_str(output_device_info.friendly_name);
1874
1875
if(input_name_str.find("AirPods") != std::string::npos &&
1876
output_name_str.find("AirPods") != std::string::npos) {
1877
uint32_t input_min_rate = 0;
1878
uint32_t input_max_rate = 0;
1879
uint32_t input_nominal_rate = 0;
1880
audiounit_get_available_samplerate(stm->input_device.id, kAudioObjectPropertyScopeGlobal,
1881
&input_min_rate, &input_max_rate, &input_nominal_rate);
1882
LOG("(%p) Input device %u, name: %s, min: %u, max: %u, nominal rate: %u", stm, stm->input_device.id
1883
, input_device_info.friendly_name, input_min_rate, input_max_rate, input_nominal_rate);
1884
uint32_t output_min_rate = 0;
1885
uint32_t output_max_rate = 0;
1886
uint32_t output_nominal_rate = 0;
1887
audiounit_get_available_samplerate(stm->output_device.id, kAudioObjectPropertyScopeGlobal,
1888
&output_min_rate, &output_max_rate, &output_nominal_rate);
1889
LOG("(%p) Output device %u, name: %s, min: %u, max: %u, nominal rate: %u", stm, stm->output_device.id
1890
, output_device_info.friendly_name, output_min_rate, output_max_rate, output_nominal_rate);
1891
1892
Float64 rate = input_nominal_rate;
1893
AudioObjectPropertyAddress addr = {kAudioDevicePropertyNominalSampleRate,
1894
kAudioObjectPropertyScopeGlobal,
1895
kAudioObjectPropertyElementMaster};
1896
1897
OSStatus rv = AudioObjectSetPropertyData(stm->aggregate_device_id,
1898
&addr,
1899
0,
1900
nullptr,
1901
sizeof(Float64),
1902
&rate);
1903
if (rv != noErr) {
1904
LOG("Non fatal error, AudioObjectSetPropertyData/kAudioDevicePropertyNominalSampleRate, rv=%d", rv);
1905
}
1906
}
1907
audiounit_device_destroy(&input_device_info);
1908
audiounit_device_destroy(&output_device_info);
1909
}
1910
1911
/*
1912
* Aggregate Device is a virtual audio interface which utilizes inputs and outputs
1913
* of one or more physical audio interfaces. It is possible to use the clock of
1914
* one of the devices as a master clock for all the combined devices and enable
1915
* drift compensation for the devices that are not designated clock master.
1916
*
1917
* Creating a new aggregate device programmatically requires [0][1]: