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