1 /*
2 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "webrtc/base/arraysize.h"
12 #include "webrtc/base/checks.h"
13 #include "webrtc/base/platform_thread.h"
14 #include "webrtc/modules/audio_device/audio_device_config.h"
15 #include "webrtc/modules/audio_device/mac/audio_device_mac.h"
16 #include "webrtc/modules/audio_device/mac/portaudio/pa_ringbuffer.h"
17 #include "webrtc/system_wrappers/include/event_wrapper.h"
18 #include "webrtc/system_wrappers/include/trace.h"
19
20 #include <ApplicationServices/ApplicationServices.h>
21 #include <libkern/OSAtomic.h> // OSAtomicCompareAndSwap()
22 #include <mach/mach.h> // mach_task_self()
23 #include <sys/sysctl.h> // sysctlbyname()
24
25 namespace webrtc {
26
27 #define WEBRTC_CA_RETURN_ON_ERR(expr) \
28 do { \
29 err = expr; \
30 if (err != noErr) { \
31 logCAMsg(kTraceError, kTraceAudioDevice, _id, "Error in " #expr, \
32 (const char*) & err); \
33 return -1; \
34 } \
35 } while (0)
36
37 #define WEBRTC_CA_LOG_ERR(expr) \
38 do { \
39 err = expr; \
40 if (err != noErr) { \
41 logCAMsg(kTraceError, kTraceAudioDevice, _id, "Error in " #expr, \
42 (const char*) & err); \
43 } \
44 } while (0)
45
46 #define WEBRTC_CA_LOG_WARN(expr) \
47 do { \
48 err = expr; \
49 if (err != noErr) { \
50 logCAMsg(kTraceWarning, kTraceAudioDevice, _id, "Error in " #expr, \
51 (const char*) & err); \
52 } \
53 } while (0)
54
55 enum { MaxNumberDevices = 64 };
56
AtomicSet32(int32_t * theValue,int32_t newValue)57 void AudioDeviceMac::AtomicSet32(int32_t* theValue, int32_t newValue) {
58 while (1) {
59 int32_t oldValue = *theValue;
60 if (OSAtomicCompareAndSwap32Barrier(oldValue, newValue, theValue) == true) {
61 return;
62 }
63 }
64 }
65
AtomicGet32(int32_t * theValue)66 int32_t AudioDeviceMac::AtomicGet32(int32_t* theValue) {
67 while (1) {
68 int32_t value = *theValue;
69 if (OSAtomicCompareAndSwap32Barrier(value, value, theValue) == true) {
70 return value;
71 }
72 }
73 }
74
75 // CoreAudio errors are best interpreted as four character strings.
logCAMsg(const TraceLevel level,const TraceModule module,const int32_t id,const char * msg,const char * err)76 void AudioDeviceMac::logCAMsg(const TraceLevel level,
77 const TraceModule module,
78 const int32_t id,
79 const char* msg,
80 const char* err) {
81 RTC_DCHECK(msg != NULL);
82 RTC_DCHECK(err != NULL);
83
84 #ifdef WEBRTC_ARCH_BIG_ENDIAN
85 WEBRTC_TRACE(level, module, id, "%s: %.4s", msg, err);
86 #else
87 // We need to flip the characters in this case.
88 WEBRTC_TRACE(level, module, id, "%s: %.1s%.1s%.1s%.1s", msg, err + 3, err + 2,
89 err + 1, err);
90 #endif
91 }
92
AudioDeviceMac(const int32_t id)93 AudioDeviceMac::AudioDeviceMac(const int32_t id)
94 : _ptrAudioBuffer(NULL),
95 _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
96 _stopEventRec(*EventWrapper::Create()),
97 _stopEvent(*EventWrapper::Create()),
98 _id(id),
99 _mixerManager(id),
100 _inputDeviceIndex(0),
101 _outputDeviceIndex(0),
102 _inputDeviceID(kAudioObjectUnknown),
103 _outputDeviceID(kAudioObjectUnknown),
104 _inputDeviceIsSpecified(false),
105 _outputDeviceIsSpecified(false),
106 _recChannels(N_REC_CHANNELS),
107 _playChannels(N_PLAY_CHANNELS),
108 _captureBufData(NULL),
109 _renderBufData(NULL),
110 _playBufType(AudioDeviceModule::kFixedBufferSize),
111 _initialized(false),
112 _isShutDown(false),
113 _recording(false),
114 _playing(false),
115 _recIsInitialized(false),
116 _playIsInitialized(false),
117 _AGC(false),
118 _renderDeviceIsAlive(1),
119 _captureDeviceIsAlive(1),
120 _twoDevices(true),
121 _doStop(false),
122 _doStopRec(false),
123 _macBookPro(false),
124 _macBookProPanRight(false),
125 _captureLatencyUs(0),
126 _renderLatencyUs(0),
127 _captureDelayUs(0),
128 _renderDelayUs(0),
129 _renderDelayOffsetSamples(0),
130 _playBufDelayFixed(20),
131 _playWarning(0),
132 _playError(0),
133 _recWarning(0),
134 _recError(0),
135 _paCaptureBuffer(NULL),
136 _paRenderBuffer(NULL),
137 _captureBufSizeSamples(0),
138 _renderBufSizeSamples(0),
139 prev_key_state_(),
140 get_mic_volume_counter_ms_(0) {
141 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created", __FUNCTION__);
142
143 RTC_DCHECK(&_stopEvent != NULL);
144 RTC_DCHECK(&_stopEventRec != NULL);
145
146 memset(_renderConvertData, 0, sizeof(_renderConvertData));
147 memset(&_outStreamFormat, 0, sizeof(AudioStreamBasicDescription));
148 memset(&_outDesiredFormat, 0, sizeof(AudioStreamBasicDescription));
149 memset(&_inStreamFormat, 0, sizeof(AudioStreamBasicDescription));
150 memset(&_inDesiredFormat, 0, sizeof(AudioStreamBasicDescription));
151 }
152
~AudioDeviceMac()153 AudioDeviceMac::~AudioDeviceMac() {
154 WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed",
155 __FUNCTION__);
156
157 if (!_isShutDown) {
158 Terminate();
159 }
160
161 RTC_DCHECK(!capture_worker_thread_.get());
162 RTC_DCHECK(!render_worker_thread_.get());
163
164 if (_paRenderBuffer) {
165 delete _paRenderBuffer;
166 _paRenderBuffer = NULL;
167 }
168
169 if (_paCaptureBuffer) {
170 delete _paCaptureBuffer;
171 _paCaptureBuffer = NULL;
172 }
173
174 if (_renderBufData) {
175 delete[] _renderBufData;
176 _renderBufData = NULL;
177 }
178
179 if (_captureBufData) {
180 delete[] _captureBufData;
181 _captureBufData = NULL;
182 }
183
184 kern_return_t kernErr = KERN_SUCCESS;
185 kernErr = semaphore_destroy(mach_task_self(), _renderSemaphore);
186 if (kernErr != KERN_SUCCESS) {
187 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
188 " semaphore_destroy() error: %d", kernErr);
189 }
190
191 kernErr = semaphore_destroy(mach_task_self(), _captureSemaphore);
192 if (kernErr != KERN_SUCCESS) {
193 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
194 " semaphore_destroy() error: %d", kernErr);
195 }
196
197 delete &_stopEvent;
198 delete &_stopEventRec;
199 delete &_critSect;
200 }
201
202 // ============================================================================
203 // API
204 // ============================================================================
205
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)206 void AudioDeviceMac::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
207 CriticalSectionScoped lock(&_critSect);
208
209 _ptrAudioBuffer = audioBuffer;
210
211 // inform the AudioBuffer about default settings for this implementation
212 _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC);
213 _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC);
214 _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS);
215 _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS);
216 }
217
ActiveAudioLayer(AudioDeviceModule::AudioLayer & audioLayer) const218 int32_t AudioDeviceMac::ActiveAudioLayer(
219 AudioDeviceModule::AudioLayer& audioLayer) const {
220 audioLayer = AudioDeviceModule::kPlatformDefaultAudio;
221 return 0;
222 }
223
Init()224 int32_t AudioDeviceMac::Init() {
225 CriticalSectionScoped lock(&_critSect);
226
227 if (_initialized) {
228 return 0;
229 }
230
231 OSStatus err = noErr;
232
233 _isShutDown = false;
234
235 // PortAudio ring buffers require an elementCount which is a power of two.
236 if (_renderBufData == NULL) {
237 UInt32 powerOfTwo = 1;
238 while (powerOfTwo < PLAY_BUF_SIZE_IN_SAMPLES) {
239 powerOfTwo <<= 1;
240 }
241 _renderBufSizeSamples = powerOfTwo;
242 _renderBufData = new SInt16[_renderBufSizeSamples];
243 }
244
245 if (_paRenderBuffer == NULL) {
246 _paRenderBuffer = new PaUtilRingBuffer;
247 PaRingBufferSize bufSize = -1;
248 bufSize = PaUtil_InitializeRingBuffer(
249 _paRenderBuffer, sizeof(SInt16), _renderBufSizeSamples, _renderBufData);
250 if (bufSize == -1) {
251 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
252 " PaUtil_InitializeRingBuffer() error");
253 return -1;
254 }
255 }
256
257 if (_captureBufData == NULL) {
258 UInt32 powerOfTwo = 1;
259 while (powerOfTwo < REC_BUF_SIZE_IN_SAMPLES) {
260 powerOfTwo <<= 1;
261 }
262 _captureBufSizeSamples = powerOfTwo;
263 _captureBufData = new Float32[_captureBufSizeSamples];
264 }
265
266 if (_paCaptureBuffer == NULL) {
267 _paCaptureBuffer = new PaUtilRingBuffer;
268 PaRingBufferSize bufSize = -1;
269 bufSize =
270 PaUtil_InitializeRingBuffer(_paCaptureBuffer, sizeof(Float32),
271 _captureBufSizeSamples, _captureBufData);
272 if (bufSize == -1) {
273 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
274 " PaUtil_InitializeRingBuffer() error");
275 return -1;
276 }
277 }
278
279 kern_return_t kernErr = KERN_SUCCESS;
280 kernErr = semaphore_create(mach_task_self(), &_renderSemaphore,
281 SYNC_POLICY_FIFO, 0);
282 if (kernErr != KERN_SUCCESS) {
283 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
284 " semaphore_create() error: %d", kernErr);
285 return -1;
286 }
287
288 kernErr = semaphore_create(mach_task_self(), &_captureSemaphore,
289 SYNC_POLICY_FIFO, 0);
290 if (kernErr != KERN_SUCCESS) {
291 WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
292 " semaphore_create() error: %d", kernErr);
293 return -1;
294 }
295
296 // Setting RunLoop to NULL here instructs HAL to manage its own thread for
297 // notifications. This was the default behaviour on OS X 10.5 and earlier,
298 // but now must be explicitly specified. HAL would otherwise try to use the
299 // main thread to issue notifications.
300 AudioObjectPropertyAddress propertyAddress = {
301 kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal,
302 kAudioObjectPropertyElementMaster};
303 CFRunLoopRef runLoop = NULL;
304 UInt32 size = sizeof(CFRunLoopRef);
305 WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
306 kAudioObjectSystemObject, &propertyAddress, 0, NULL, size, &runLoop));
307
308 // Listen for any device changes.
309 propertyAddress.mSelector = kAudioHardwarePropertyDevices;
310 WEBRTC_CA_LOG_ERR(AudioObjectAddPropertyListener(
311 kAudioObjectSystemObject, &propertyAddress, &objectListenerProc, this));
312
313 // Determine if this is a MacBook Pro
314 _macBookPro = false;
315 _macBookProPanRight = false;
316 char buf[128];
317 size_t length = sizeof(buf);
318 memset(buf, 0, length);
319
320 int intErr = sysctlbyname("hw.model", buf, &length, NULL, 0);
321 if (intErr != 0) {
322 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
323 " Error in sysctlbyname(): %d", err);
324 } else {
325 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " Hardware model: %s",
326 buf);
327 if (strncmp(buf, "MacBookPro", 10) == 0) {
328 _macBookPro = true;
329 }
330 }
331
332 _playWarning = 0;
333 _playError = 0;
334 _recWarning = 0;
335 _recError = 0;
336
337 get_mic_volume_counter_ms_ = 0;
338
339 _initialized = true;
340
341 return 0;
342 }
343
Terminate()344 int32_t AudioDeviceMac::Terminate() {
345 if (!_initialized) {
346 return 0;
347 }
348
349 if (_recording) {
350 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
351 " Recording must be stopped");
352 return -1;
353 }
354
355 if (_playing) {
356 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
357 " Playback must be stopped");
358 return -1;
359 }
360
361 _critSect.Enter();
362
363 _mixerManager.Close();
364
365 OSStatus err = noErr;
366 int retVal = 0;
367
368 AudioObjectPropertyAddress propertyAddress = {
369 kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
370 kAudioObjectPropertyElementMaster};
371 WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
372 kAudioObjectSystemObject, &propertyAddress, &objectListenerProc, this));
373
374 err = AudioHardwareUnload();
375 if (err != noErr) {
376 logCAMsg(kTraceError, kTraceAudioDevice, _id,
377 "Error in AudioHardwareUnload()", (const char*)&err);
378 retVal = -1;
379 }
380
381 _isShutDown = true;
382 _initialized = false;
383 _outputDeviceIsSpecified = false;
384 _inputDeviceIsSpecified = false;
385
386 _critSect.Leave();
387
388 return retVal;
389 }
390
Initialized() const391 bool AudioDeviceMac::Initialized() const {
392 return (_initialized);
393 }
394
SpeakerIsAvailable(bool & available)395 int32_t AudioDeviceMac::SpeakerIsAvailable(bool& available) {
396 bool wasInitialized = _mixerManager.SpeakerIsInitialized();
397
398 // Make an attempt to open up the
399 // output mixer corresponding to the currently selected output device.
400 //
401 if (!wasInitialized && InitSpeaker() == -1) {
402 available = false;
403 return 0;
404 }
405
406 // Given that InitSpeaker was successful, we know that a valid speaker
407 // exists.
408 available = true;
409
410 // Close the initialized output mixer
411 //
412 if (!wasInitialized) {
413 _mixerManager.CloseSpeaker();
414 }
415
416 return 0;
417 }
418
InitSpeaker()419 int32_t AudioDeviceMac::InitSpeaker() {
420 CriticalSectionScoped lock(&_critSect);
421
422 if (_playing) {
423 return -1;
424 }
425
426 if (InitDevice(_outputDeviceIndex, _outputDeviceID, false) == -1) {
427 return -1;
428 }
429
430 if (_inputDeviceID == _outputDeviceID) {
431 _twoDevices = false;
432 } else {
433 _twoDevices = true;
434 }
435
436 if (_mixerManager.OpenSpeaker(_outputDeviceID) == -1) {
437 return -1;
438 }
439
440 return 0;
441 }
442
MicrophoneIsAvailable(bool & available)443 int32_t AudioDeviceMac::MicrophoneIsAvailable(bool& available) {
444 bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
445
446 // Make an attempt to open up the
447 // input mixer corresponding to the currently selected output device.
448 //
449 if (!wasInitialized && InitMicrophone() == -1) {
450 available = false;
451 return 0;
452 }
453
454 // Given that InitMicrophone was successful, we know that a valid microphone
455 // exists.
456 available = true;
457
458 // Close the initialized input mixer
459 //
460 if (!wasInitialized) {
461 _mixerManager.CloseMicrophone();
462 }
463
464 return 0;
465 }
466
InitMicrophone()467 int32_t AudioDeviceMac::InitMicrophone() {
468 CriticalSectionScoped lock(&_critSect);
469
470 if (_recording) {
471 return -1;
472 }
473
474 if (InitDevice(_inputDeviceIndex, _inputDeviceID, true) == -1) {
475 return -1;
476 }
477
478 if (_inputDeviceID == _outputDeviceID) {
479 _twoDevices = false;
480 } else {
481 _twoDevices = true;
482 }
483
484 if (_mixerManager.OpenMicrophone(_inputDeviceID) == -1) {
485 return -1;
486 }
487
488 return 0;
489 }
490
SpeakerIsInitialized() const491 bool AudioDeviceMac::SpeakerIsInitialized() const {
492 return (_mixerManager.SpeakerIsInitialized());
493 }
494
MicrophoneIsInitialized() const495 bool AudioDeviceMac::MicrophoneIsInitialized() const {
496 return (_mixerManager.MicrophoneIsInitialized());
497 }
498
SpeakerVolumeIsAvailable(bool & available)499 int32_t AudioDeviceMac::SpeakerVolumeIsAvailable(bool& available) {
500 bool wasInitialized = _mixerManager.SpeakerIsInitialized();
501
502 // Make an attempt to open up the
503 // output mixer corresponding to the currently selected output device.
504 //
505 if (!wasInitialized && InitSpeaker() == -1) {
506 // If we end up here it means that the selected speaker has no volume
507 // control.
508 available = false;
509 return 0;
510 }
511
512 // Given that InitSpeaker was successful, we know that a volume control exists
513 //
514 available = true;
515
516 // Close the initialized output mixer
517 //
518 if (!wasInitialized) {
519 _mixerManager.CloseSpeaker();
520 }
521
522 return 0;
523 }
524
SetSpeakerVolume(uint32_t volume)525 int32_t AudioDeviceMac::SetSpeakerVolume(uint32_t volume) {
526 return (_mixerManager.SetSpeakerVolume(volume));
527 }
528
SpeakerVolume(uint32_t & volume) const529 int32_t AudioDeviceMac::SpeakerVolume(uint32_t& volume) const {
530 uint32_t level(0);
531
532 if (_mixerManager.SpeakerVolume(level) == -1) {
533 return -1;
534 }
535
536 volume = level;
537 return 0;
538 }
539
SetWaveOutVolume(uint16_t volumeLeft,uint16_t volumeRight)540 int32_t AudioDeviceMac::SetWaveOutVolume(uint16_t volumeLeft,
541 uint16_t volumeRight) {
542 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
543 " API call not supported on this platform");
544 return -1;
545 }
546
WaveOutVolume(uint16_t &,uint16_t &) const547 int32_t AudioDeviceMac::WaveOutVolume(uint16_t& /*volumeLeft*/,
548 uint16_t& /*volumeRight*/) const {
549 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
550 " API call not supported on this platform");
551 return -1;
552 }
553
MaxSpeakerVolume(uint32_t & maxVolume) const554 int32_t AudioDeviceMac::MaxSpeakerVolume(uint32_t& maxVolume) const {
555 uint32_t maxVol(0);
556
557 if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) {
558 return -1;
559 }
560
561 maxVolume = maxVol;
562 return 0;
563 }
564
MinSpeakerVolume(uint32_t & minVolume) const565 int32_t AudioDeviceMac::MinSpeakerVolume(uint32_t& minVolume) const {
566 uint32_t minVol(0);
567
568 if (_mixerManager.MinSpeakerVolume(minVol) == -1) {
569 return -1;
570 }
571
572 minVolume = minVol;
573 return 0;
574 }
575
SpeakerVolumeStepSize(uint16_t & stepSize) const576 int32_t AudioDeviceMac::SpeakerVolumeStepSize(uint16_t& stepSize) const {
577 uint16_t delta(0);
578
579 if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) {
580 return -1;
581 }
582
583 stepSize = delta;
584 return 0;
585 }
586
SpeakerMuteIsAvailable(bool & available)587 int32_t AudioDeviceMac::SpeakerMuteIsAvailable(bool& available) {
588 bool isAvailable(false);
589 bool wasInitialized = _mixerManager.SpeakerIsInitialized();
590
591 // Make an attempt to open up the
592 // output mixer corresponding to the currently selected output device.
593 //
594 if (!wasInitialized && InitSpeaker() == -1) {
595 // If we end up here it means that the selected speaker has no volume
596 // control, hence it is safe to state that there is no mute control
597 // already at this stage.
598 available = false;
599 return 0;
600 }
601
602 // Check if the selected speaker has a mute control
603 //
604 _mixerManager.SpeakerMuteIsAvailable(isAvailable);
605
606 available = isAvailable;
607
608 // Close the initialized output mixer
609 //
610 if (!wasInitialized) {
611 _mixerManager.CloseSpeaker();
612 }
613
614 return 0;
615 }
616
SetSpeakerMute(bool enable)617 int32_t AudioDeviceMac::SetSpeakerMute(bool enable) {
618 return (_mixerManager.SetSpeakerMute(enable));
619 }
620
SpeakerMute(bool & enabled) const621 int32_t AudioDeviceMac::SpeakerMute(bool& enabled) const {
622 bool muted(0);
623
624 if (_mixerManager.SpeakerMute(muted) == -1) {
625 return -1;
626 }
627
628 enabled = muted;
629 return 0;
630 }
631
MicrophoneMuteIsAvailable(bool & available)632 int32_t AudioDeviceMac::MicrophoneMuteIsAvailable(bool& available) {
633 bool isAvailable(false);
634 bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
635
636 // Make an attempt to open up the
637 // input mixer corresponding to the currently selected input device.
638 //
639 if (!wasInitialized && InitMicrophone() == -1) {
640 // If we end up here it means that the selected microphone has no volume
641 // control, hence it is safe to state that there is no boost control
642 // already at this stage.
643 available = false;
644 return 0;
645 }
646
647 // Check if the selected microphone has a mute control
648 //
649 _mixerManager.MicrophoneMuteIsAvailable(isAvailable);
650 available = isAvailable;
651
652 // Close the initialized input mixer
653 //
654 if (!wasInitialized) {
655 _mixerManager.CloseMicrophone();
656 }
657
658 return 0;
659 }
660
SetMicrophoneMute(bool enable)661 int32_t AudioDeviceMac::SetMicrophoneMute(bool enable) {
662 return (_mixerManager.SetMicrophoneMute(enable));
663 }
664
MicrophoneMute(bool & enabled) const665 int32_t AudioDeviceMac::MicrophoneMute(bool& enabled) const {
666 bool muted(0);
667
668 if (_mixerManager.MicrophoneMute(muted) == -1) {
669 return -1;
670 }
671
672 enabled = muted;
673 return 0;
674 }
675
MicrophoneBoostIsAvailable(bool & available)676 int32_t AudioDeviceMac::MicrophoneBoostIsAvailable(bool& available) {
677 bool isAvailable(false);
678 bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
679
680 // Enumerate all avaliable microphone and make an attempt to open up the
681 // input mixer corresponding to the currently selected input device.
682 //
683 if (!wasInitialized && InitMicrophone() == -1) {
684 // If we end up here it means that the selected microphone has no volume
685 // control, hence it is safe to state that there is no boost control
686 // already at this stage.
687 available = false;
688 return 0;
689 }
690
691 // Check if the selected microphone has a boost control
692 //
693 _mixerManager.MicrophoneBoostIsAvailable(isAvailable);
694 available = isAvailable;
695
696 // Close the initialized input mixer
697 //
698 if (!wasInitialized) {
699 _mixerManager.CloseMicrophone();
700 }
701
702 return 0;
703 }
704
SetMicrophoneBoost(bool enable)705 int32_t AudioDeviceMac::SetMicrophoneBoost(bool enable) {
706 return (_mixerManager.SetMicrophoneBoost(enable));
707 }
708
MicrophoneBoost(bool & enabled) const709 int32_t AudioDeviceMac::MicrophoneBoost(bool& enabled) const {
710 bool onOff(0);
711
712 if (_mixerManager.MicrophoneBoost(onOff) == -1) {
713 return -1;
714 }
715
716 enabled = onOff;
717 return 0;
718 }
719
StereoRecordingIsAvailable(bool & available)720 int32_t AudioDeviceMac::StereoRecordingIsAvailable(bool& available) {
721 bool isAvailable(false);
722 bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
723
724 if (!wasInitialized && InitMicrophone() == -1) {
725 // Cannot open the specified device
726 available = false;
727 return 0;
728 }
729
730 // Check if the selected microphone can record stereo
731 //
732 _mixerManager.StereoRecordingIsAvailable(isAvailable);
733 available = isAvailable;
734
735 // Close the initialized input mixer
736 //
737 if (!wasInitialized) {
738 _mixerManager.CloseMicrophone();
739 }
740
741 return 0;
742 }
743
SetStereoRecording(bool enable)744 int32_t AudioDeviceMac::SetStereoRecording(bool enable) {
745 if (enable)
746 _recChannels = 2;
747 else
748 _recChannels = 1;
749
750 return 0;
751 }
752
StereoRecording(bool & enabled) const753 int32_t AudioDeviceMac::StereoRecording(bool& enabled) const {
754 if (_recChannels == 2)
755 enabled = true;
756 else
757 enabled = false;
758
759 return 0;
760 }
761
StereoPlayoutIsAvailable(bool & available)762 int32_t AudioDeviceMac::StereoPlayoutIsAvailable(bool& available) {
763 bool isAvailable(false);
764 bool wasInitialized = _mixerManager.SpeakerIsInitialized();
765
766 if (!wasInitialized && InitSpeaker() == -1) {
767 // Cannot open the specified device
768 available = false;
769 return 0;
770 }
771
772 // Check if the selected microphone can record stereo
773 //
774 _mixerManager.StereoPlayoutIsAvailable(isAvailable);
775 available = isAvailable;
776
777 // Close the initialized input mixer
778 //
779 if (!wasInitialized) {
780 _mixerManager.CloseSpeaker();
781 }
782
783 return 0;
784 }
785
SetStereoPlayout(bool enable)786 int32_t AudioDeviceMac::SetStereoPlayout(bool enable) {
787 if (enable)
788 _playChannels = 2;
789 else
790 _playChannels = 1;
791
792 return 0;
793 }
794
StereoPlayout(bool & enabled) const795 int32_t AudioDeviceMac::StereoPlayout(bool& enabled) const {
796 if (_playChannels == 2)
797 enabled = true;
798 else
799 enabled = false;
800
801 return 0;
802 }
803
SetAGC(bool enable)804 int32_t AudioDeviceMac::SetAGC(bool enable) {
805 _AGC = enable;
806
807 return 0;
808 }
809
AGC() const810 bool AudioDeviceMac::AGC() const {
811 return _AGC;
812 }
813
MicrophoneVolumeIsAvailable(bool & available)814 int32_t AudioDeviceMac::MicrophoneVolumeIsAvailable(bool& available) {
815 bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
816
817 // Make an attempt to open up the
818 // input mixer corresponding to the currently selected output device.
819 //
820 if (!wasInitialized && InitMicrophone() == -1) {
821 // If we end up here it means that the selected microphone has no volume
822 // control.
823 available = false;
824 return 0;
825 }
826
827 // Given that InitMicrophone was successful, we know that a volume control
828 // exists
829 //
830 available = true;
831
832 // Close the initialized input mixer
833 //
834 if (!wasInitialized) {
835 _mixerManager.CloseMicrophone();
836 }
837
838 return 0;
839 }
840
SetMicrophoneVolume(uint32_t volume)841 int32_t AudioDeviceMac::SetMicrophoneVolume(uint32_t volume) {
842 return (_mixerManager.SetMicrophoneVolume(volume));
843 }
844
MicrophoneVolume(uint32_t & volume) const845 int32_t AudioDeviceMac::MicrophoneVolume(uint32_t& volume) const {
846 uint32_t level(0);
847
848 if (_mixerManager.MicrophoneVolume(level) == -1) {
849 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
850 " failed to retrive current microphone level");
851 return -1;
852 }
853
854 volume = level;
855 return 0;
856 }
857
MaxMicrophoneVolume(uint32_t & maxVolume) const858 int32_t AudioDeviceMac::MaxMicrophoneVolume(uint32_t& maxVolume) const {
859 uint32_t maxVol(0);
860
861 if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) {
862 return -1;
863 }
864
865 maxVolume = maxVol;
866 return 0;
867 }
868
MinMicrophoneVolume(uint32_t & minVolume) const869 int32_t AudioDeviceMac::MinMicrophoneVolume(uint32_t& minVolume) const {
870 uint32_t minVol(0);
871
872 if (_mixerManager.MinMicrophoneVolume(minVol) == -1) {
873 return -1;
874 }
875
876 minVolume = minVol;
877 return 0;
878 }
879
MicrophoneVolumeStepSize(uint16_t & stepSize) const880 int32_t AudioDeviceMac::MicrophoneVolumeStepSize(uint16_t& stepSize) const {
881 uint16_t delta(0);
882
883 if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) {
884 return -1;
885 }
886
887 stepSize = delta;
888 return 0;
889 }
890
PlayoutDevices()891 int16_t AudioDeviceMac::PlayoutDevices() {
892 AudioDeviceID playDevices[MaxNumberDevices];
893 return GetNumberDevices(kAudioDevicePropertyScopeOutput, playDevices,
894 MaxNumberDevices);
895 }
896
SetPlayoutDevice(uint16_t index)897 int32_t AudioDeviceMac::SetPlayoutDevice(uint16_t index) {
898 CriticalSectionScoped lock(&_critSect);
899
900 if (_playIsInitialized) {
901 return -1;
902 }
903
904 AudioDeviceID playDevices[MaxNumberDevices];
905 uint32_t nDevices = GetNumberDevices(kAudioDevicePropertyScopeOutput,
906 playDevices, MaxNumberDevices);
907 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
908 " number of availiable waveform-audio output devices is %u",
909 nDevices);
910
911 if (index > (nDevices - 1)) {
912 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
913 " device index is out of range [0,%u]", (nDevices - 1));
914 return -1;
915 }
916
917 _outputDeviceIndex = index;
918 _outputDeviceIsSpecified = true;
919
920 return 0;
921 }
922
SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType)923 int32_t AudioDeviceMac::SetPlayoutDevice(
924 AudioDeviceModule::WindowsDeviceType /*device*/) {
925 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
926 "WindowsDeviceType not supported");
927 return -1;
928 }
929
PlayoutDeviceName(uint16_t index,char name[kAdmMaxDeviceNameSize],char guid[kAdmMaxGuidSize])930 int32_t AudioDeviceMac::PlayoutDeviceName(uint16_t index,
931 char name[kAdmMaxDeviceNameSize],
932 char guid[kAdmMaxGuidSize]) {
933 const uint16_t nDevices(PlayoutDevices());
934
935 if ((index > (nDevices - 1)) || (name == NULL)) {
936 return -1;
937 }
938
939 memset(name, 0, kAdmMaxDeviceNameSize);
940
941 if (guid != NULL) {
942 memset(guid, 0, kAdmMaxGuidSize);
943 }
944
945 return GetDeviceName(kAudioDevicePropertyScopeOutput, index, name);
946 }
947
RecordingDeviceName(uint16_t index,char name[kAdmMaxDeviceNameSize],char guid[kAdmMaxGuidSize])948 int32_t AudioDeviceMac::RecordingDeviceName(uint16_t index,
949 char name[kAdmMaxDeviceNameSize],
950 char guid[kAdmMaxGuidSize]) {
951 const uint16_t nDevices(RecordingDevices());
952
953 if ((index > (nDevices - 1)) || (name == NULL)) {
954 return -1;
955 }
956
957 memset(name, 0, kAdmMaxDeviceNameSize);
958
959 if (guid != NULL) {
960 memset(guid, 0, kAdmMaxGuidSize);
961 }
962
963 return GetDeviceName(kAudioDevicePropertyScopeInput, index, name);
964 }
965
RecordingDevices()966 int16_t AudioDeviceMac::RecordingDevices() {
967 AudioDeviceID recDevices[MaxNumberDevices];
968 return GetNumberDevices(kAudioDevicePropertyScopeInput, recDevices,
969 MaxNumberDevices);
970 }
971
SetRecordingDevice(uint16_t index)972 int32_t AudioDeviceMac::SetRecordingDevice(uint16_t index) {
973 if (_recIsInitialized) {
974 return -1;
975 }
976
977 AudioDeviceID recDevices[MaxNumberDevices];
978 uint32_t nDevices = GetNumberDevices(kAudioDevicePropertyScopeInput,
979 recDevices, MaxNumberDevices);
980 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
981 " number of availiable waveform-audio input devices is %u",
982 nDevices);
983
984 if (index > (nDevices - 1)) {
985 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
986 " device index is out of range [0,%u]", (nDevices - 1));
987 return -1;
988 }
989
990 _inputDeviceIndex = index;
991 _inputDeviceIsSpecified = true;
992
993 return 0;
994 }
995
SetRecordingDevice(AudioDeviceModule::WindowsDeviceType)996 int32_t AudioDeviceMac::SetRecordingDevice(
997 AudioDeviceModule::WindowsDeviceType /*device*/) {
998 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
999 "WindowsDeviceType not supported");
1000 return -1;
1001 }
1002
PlayoutIsAvailable(bool & available)1003 int32_t AudioDeviceMac::PlayoutIsAvailable(bool& available) {
1004 available = true;
1005
1006 // Try to initialize the playout side
1007 if (InitPlayout() == -1) {
1008 available = false;
1009 }
1010
1011 // We destroy the IOProc created by InitPlayout() in implDeviceIOProc().
1012 // We must actually start playout here in order to have the IOProc
1013 // deleted by calling StopPlayout().
1014 if (StartPlayout() == -1) {
1015 available = false;
1016 }
1017
1018 // Cancel effect of initialization
1019 if (StopPlayout() == -1) {
1020 available = false;
1021 }
1022
1023 return 0;
1024 }
1025
RecordingIsAvailable(bool & available)1026 int32_t AudioDeviceMac::RecordingIsAvailable(bool& available) {
1027 available = true;
1028
1029 // Try to initialize the recording side
1030 if (InitRecording() == -1) {
1031 available = false;
1032 }
1033
1034 // We destroy the IOProc created by InitRecording() in implInDeviceIOProc().
1035 // We must actually start recording here in order to have the IOProc
1036 // deleted by calling StopRecording().
1037 if (StartRecording() == -1) {
1038 available = false;
1039 }
1040
1041 // Cancel effect of initialization
1042 if (StopRecording() == -1) {
1043 available = false;
1044 }
1045
1046 return 0;
1047 }
1048
InitPlayout()1049 int32_t AudioDeviceMac::InitPlayout() {
1050 CriticalSectionScoped lock(&_critSect);
1051
1052 if (_playing) {
1053 return -1;
1054 }
1055
1056 if (!_outputDeviceIsSpecified) {
1057 return -1;
1058 }
1059
1060 if (_playIsInitialized) {
1061 return 0;
1062 }
1063
1064 // Initialize the speaker (devices might have been added or removed)
1065 if (InitSpeaker() == -1) {
1066 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1067 " InitSpeaker() failed");
1068 }
1069
1070 if (!MicrophoneIsInitialized()) {
1071 // Make this call to check if we are using
1072 // one or two devices (_twoDevices)
1073 bool available = false;
1074 if (MicrophoneIsAvailable(available) == -1) {
1075 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1076 " MicrophoneIsAvailable() failed");
1077 }
1078 }
1079
1080 PaUtil_FlushRingBuffer(_paRenderBuffer);
1081
1082 OSStatus err = noErr;
1083 UInt32 size = 0;
1084 _renderDelayOffsetSamples = 0;
1085 _renderDelayUs = 0;
1086 _renderLatencyUs = 0;
1087 _renderDeviceIsAlive = 1;
1088 _doStop = false;
1089
1090 // The internal microphone of a MacBook Pro is located under the left speaker
1091 // grille. When the internal speakers are in use, we want to fully stereo
1092 // pan to the right.
1093 AudioObjectPropertyAddress propertyAddress = {
1094 kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput, 0};
1095 if (_macBookPro) {
1096 _macBookProPanRight = false;
1097 Boolean hasProperty =
1098 AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
1099 if (hasProperty) {
1100 UInt32 dataSource = 0;
1101 size = sizeof(dataSource);
1102 WEBRTC_CA_LOG_WARN(AudioObjectGetPropertyData(
1103 _outputDeviceID, &propertyAddress, 0, NULL, &size, &dataSource));
1104
1105 if (dataSource == 'ispk') {
1106 _macBookProPanRight = true;
1107 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1108 "MacBook Pro using internal speakers; stereo"
1109 " panning right");
1110 } else {
1111 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1112 "MacBook Pro not using internal speakers");
1113 }
1114
1115 // Add a listener to determine if the status changes.
1116 WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(
1117 _outputDeviceID, &propertyAddress, &objectListenerProc, this));
1118 }
1119 }
1120
1121 // Get current stream description
1122 propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
1123 memset(&_outStreamFormat, 0, sizeof(_outStreamFormat));
1124 size = sizeof(_outStreamFormat);
1125 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
1126 _outputDeviceID, &propertyAddress, 0, NULL, &size, &_outStreamFormat));
1127
1128 if (_outStreamFormat.mFormatID != kAudioFormatLinearPCM) {
1129 logCAMsg(kTraceError, kTraceAudioDevice, _id,
1130 "Unacceptable output stream format -> mFormatID",
1131 (const char*)&_outStreamFormat.mFormatID);
1132 return -1;
1133 }
1134
1135 if (_outStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS) {
1136 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1137 "Too many channels on output device (mChannelsPerFrame = %d)",
1138 _outStreamFormat.mChannelsPerFrame);
1139 return -1;
1140 }
1141
1142 if (_outStreamFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) {
1143 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1144 "Non-interleaved audio data is not supported.",
1145 "AudioHardware streams should not have this format.");
1146 return -1;
1147 }
1148
1149 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Ouput stream format:");
1150 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1151 "mSampleRate = %f, mChannelsPerFrame = %u",
1152 _outStreamFormat.mSampleRate,
1153 _outStreamFormat.mChannelsPerFrame);
1154 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1155 "mBytesPerPacket = %u, mFramesPerPacket = %u",
1156 _outStreamFormat.mBytesPerPacket,
1157 _outStreamFormat.mFramesPerPacket);
1158 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1159 "mBytesPerFrame = %u, mBitsPerChannel = %u",
1160 _outStreamFormat.mBytesPerFrame,
1161 _outStreamFormat.mBitsPerChannel);
1162 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "mFormatFlags = %u",
1163 _outStreamFormat.mFormatFlags);
1164 logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
1165 (const char*)&_outStreamFormat.mFormatID);
1166
1167 // Our preferred format to work with.
1168 if (_outStreamFormat.mChannelsPerFrame < 2) {
1169 // Disable stereo playout when we only have one channel on the device.
1170 _playChannels = 1;
1171 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1172 "Stereo playout unavailable on this device");
1173 }
1174 WEBRTC_CA_RETURN_ON_ERR(SetDesiredPlayoutFormat());
1175
1176 // Listen for format changes.
1177 propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
1178 WEBRTC_CA_RETURN_ON_ERR(AudioObjectAddPropertyListener(
1179 _outputDeviceID, &propertyAddress, &objectListenerProc, this));
1180
1181 // Listen for processor overloads.
1182 propertyAddress.mSelector = kAudioDeviceProcessorOverload;
1183 WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(
1184 _outputDeviceID, &propertyAddress, &objectListenerProc, this));
1185
1186 if (_twoDevices || !_recIsInitialized) {
1187 WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(
1188 _outputDeviceID, deviceIOProc, this, &_deviceIOProcID));
1189 }
1190
1191 _playIsInitialized = true;
1192
1193 return 0;
1194 }
1195
InitRecording()1196 int32_t AudioDeviceMac::InitRecording() {
1197 CriticalSectionScoped lock(&_critSect);
1198
1199 if (_recording) {
1200 return -1;
1201 }
1202
1203 if (!_inputDeviceIsSpecified) {
1204 return -1;
1205 }
1206
1207 if (_recIsInitialized) {
1208 return 0;
1209 }
1210
1211 // Initialize the microphone (devices might have been added or removed)
1212 if (InitMicrophone() == -1) {
1213 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1214 " InitMicrophone() failed");
1215 }
1216
1217 if (!SpeakerIsInitialized()) {
1218 // Make this call to check if we are using
1219 // one or two devices (_twoDevices)
1220 bool available = false;
1221 if (SpeakerIsAvailable(available) == -1) {
1222 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1223 " SpeakerIsAvailable() failed");
1224 }
1225 }
1226
1227 OSStatus err = noErr;
1228 UInt32 size = 0;
1229
1230 PaUtil_FlushRingBuffer(_paCaptureBuffer);
1231
1232 _captureDelayUs = 0;
1233 _captureLatencyUs = 0;
1234 _captureDeviceIsAlive = 1;
1235 _doStopRec = false;
1236
1237 // Get current stream description
1238 AudioObjectPropertyAddress propertyAddress = {
1239 kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput, 0};
1240 memset(&_inStreamFormat, 0, sizeof(_inStreamFormat));
1241 size = sizeof(_inStreamFormat);
1242 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
1243 _inputDeviceID, &propertyAddress, 0, NULL, &size, &_inStreamFormat));
1244
1245 if (_inStreamFormat.mFormatID != kAudioFormatLinearPCM) {
1246 logCAMsg(kTraceError, kTraceAudioDevice, _id,
1247 "Unacceptable input stream format -> mFormatID",
1248 (const char*)&_inStreamFormat.mFormatID);
1249 return -1;
1250 }
1251
1252 if (_inStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS) {
1253 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1254 "Too many channels on input device (mChannelsPerFrame = %d)",
1255 _inStreamFormat.mChannelsPerFrame);
1256 return -1;
1257 }
1258
1259 const int io_block_size_samples = _inStreamFormat.mChannelsPerFrame *
1260 _inStreamFormat.mSampleRate / 100 *
1261 N_BLOCKS_IO;
1262 if (io_block_size_samples > _captureBufSizeSamples) {
1263 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1264 "Input IO block size (%d) is larger than ring buffer (%u)",
1265 io_block_size_samples, _captureBufSizeSamples);
1266 return -1;
1267 }
1268
1269 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " Input stream format:");
1270 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1271 " mSampleRate = %f, mChannelsPerFrame = %u",
1272 _inStreamFormat.mSampleRate, _inStreamFormat.mChannelsPerFrame);
1273 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1274 " mBytesPerPacket = %u, mFramesPerPacket = %u",
1275 _inStreamFormat.mBytesPerPacket,
1276 _inStreamFormat.mFramesPerPacket);
1277 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1278 " mBytesPerFrame = %u, mBitsPerChannel = %u",
1279 _inStreamFormat.mBytesPerFrame, _inStreamFormat.mBitsPerChannel);
1280 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " mFormatFlags = %u",
1281 _inStreamFormat.mFormatFlags);
1282 logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
1283 (const char*)&_inStreamFormat.mFormatID);
1284
1285 // Our preferred format to work with
1286 if (_inStreamFormat.mChannelsPerFrame >= 2 && (_recChannels == 2)) {
1287 _inDesiredFormat.mChannelsPerFrame = 2;
1288 } else {
1289 // Disable stereo recording when we only have one channel on the device.
1290 _inDesiredFormat.mChannelsPerFrame = 1;
1291 _recChannels = 1;
1292 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
1293 "Stereo recording unavailable on this device");
1294 }
1295
1296 if (_ptrAudioBuffer) {
1297 // Update audio buffer with the selected parameters
1298 _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC);
1299 _ptrAudioBuffer->SetRecordingChannels((uint8_t)_recChannels);
1300 }
1301
1302 _inDesiredFormat.mSampleRate = N_REC_SAMPLES_PER_SEC;
1303 _inDesiredFormat.mBytesPerPacket =
1304 _inDesiredFormat.mChannelsPerFrame * sizeof(SInt16);
1305 _inDesiredFormat.mFramesPerPacket = 1;
1306 _inDesiredFormat.mBytesPerFrame =
1307 _inDesiredFormat.mChannelsPerFrame * sizeof(SInt16);
1308 _inDesiredFormat.mBitsPerChannel = sizeof(SInt16) * 8;
1309
1310 _inDesiredFormat.mFormatFlags =
1311 kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
1312 #ifdef WEBRTC_ARCH_BIG_ENDIAN
1313 _inDesiredFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
1314 #endif
1315 _inDesiredFormat.mFormatID = kAudioFormatLinearPCM;
1316
1317 WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(&_inStreamFormat, &_inDesiredFormat,
1318 &_captureConverter));
1319
1320 // First try to set buffer size to desired value (10 ms * N_BLOCKS_IO)
1321 // TODO(xians): investigate this block.
1322 UInt32 bufByteCount =
1323 (UInt32)((_inStreamFormat.mSampleRate / 1000.0) * 10.0 * N_BLOCKS_IO *
1324 _inStreamFormat.mChannelsPerFrame * sizeof(Float32));
1325 if (_inStreamFormat.mFramesPerPacket != 0) {
1326 if (bufByteCount % _inStreamFormat.mFramesPerPacket != 0) {
1327 bufByteCount =
1328 ((UInt32)(bufByteCount / _inStreamFormat.mFramesPerPacket) + 1) *
1329 _inStreamFormat.mFramesPerPacket;
1330 }
1331 }
1332
1333 // Ensure the buffer size is within the acceptable range provided by the
1334 // device.
1335 propertyAddress.mSelector = kAudioDevicePropertyBufferSizeRange;
1336 AudioValueRange range;
1337 size = sizeof(range);
1338 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
1339 _inputDeviceID, &propertyAddress, 0, NULL, &size, &range));
1340 if (range.mMinimum > bufByteCount) {
1341 bufByteCount = range.mMinimum;
1342 } else if (range.mMaximum < bufByteCount) {
1343 bufByteCount = range.mMaximum;
1344 }
1345
1346 propertyAddress.mSelector = kAudioDevicePropertyBufferSize;
1347 size = sizeof(bufByteCount);
1348 WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
1349 _inputDeviceID, &propertyAddress, 0, NULL, size, &bufByteCount));
1350
1351 // Get capture device latency
1352 propertyAddress.mSelector = kAudioDevicePropertyLatency;
1353 UInt32 latency = 0;
1354 size = sizeof(UInt32);
1355 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
1356 _inputDeviceID, &propertyAddress, 0, NULL, &size, &latency));
1357 _captureLatencyUs = (UInt32)((1.0e6 * latency) / _inStreamFormat.mSampleRate);
1358
1359 // Get capture stream latency
1360 propertyAddress.mSelector = kAudioDevicePropertyStreams;
1361 AudioStreamID stream = 0;
1362 size = sizeof(AudioStreamID);
1363 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
1364 _inputDeviceID, &propertyAddress, 0, NULL, &size, &stream));
1365 propertyAddress.mSelector = kAudioStreamPropertyLatency;
1366 size = sizeof(UInt32);
1367 latency = 0;
1368 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
1369 _inputDeviceID, &propertyAddress, 0, NULL, &size, &latency));
1370 _captureLatencyUs +=
1371 (UInt32)((1.0e6 * latency) / _inStreamFormat.mSampleRate);
1372
1373 // Listen for format changes
1374 // TODO(xians): should we be using kAudioDevicePropertyDeviceHasChanged?
1375 propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
1376 WEBRTC_CA_RETURN_ON_ERR(AudioObjectAddPropertyListener(
1377 _inputDeviceID, &propertyAddress, &objectListenerProc, this));
1378
1379 // Listen for processor overloads
1380 propertyAddress.mSelector = kAudioDeviceProcessorOverload;
1381 WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(
1382 _inputDeviceID, &propertyAddress, &objectListenerProc, this));
1383
1384 if (_twoDevices) {
1385 WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(
1386 _inputDeviceID, inDeviceIOProc, this, &_inDeviceIOProcID));
1387 } else if (!_playIsInitialized) {
1388 WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(
1389 _inputDeviceID, deviceIOProc, this, &_deviceIOProcID));
1390 }
1391
1392 // Mark recording side as initialized
1393 _recIsInitialized = true;
1394
1395 return 0;
1396 }
1397
StartRecording()1398 int32_t AudioDeviceMac::StartRecording() {
1399 CriticalSectionScoped lock(&_critSect);
1400
1401 if (!_recIsInitialized) {
1402 return -1;
1403 }
1404
1405 if (_recording) {
1406 return 0;
1407 }
1408
1409 if (!_initialized) {
1410 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1411 " Recording worker thread has not been started");
1412 return -1;
1413 }
1414
1415 RTC_DCHECK(!capture_worker_thread_.get());
1416 capture_worker_thread_.reset(
1417 new rtc::PlatformThread(RunCapture, this, "CaptureWorkerThread"));
1418 RTC_DCHECK(capture_worker_thread_.get());
1419 capture_worker_thread_->Start();
1420 capture_worker_thread_->SetPriority(rtc::kRealtimePriority);
1421
1422 OSStatus err = noErr;
1423 if (_twoDevices) {
1424 WEBRTC_CA_RETURN_ON_ERR(
1425 AudioDeviceStart(_inputDeviceID, _inDeviceIOProcID));
1426 } else if (!_playing) {
1427 WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_inputDeviceID, _deviceIOProcID));
1428 }
1429
1430 _recording = true;
1431
1432 return 0;
1433 }
1434
StopRecording()1435 int32_t AudioDeviceMac::StopRecording() {
1436 CriticalSectionScoped lock(&_critSect);
1437
1438 if (!_recIsInitialized) {
1439 return 0;
1440 }
1441
1442 OSStatus err = noErr;
1443
1444 // Stop device
1445 int32_t captureDeviceIsAlive = AtomicGet32(&_captureDeviceIsAlive);
1446 if (_twoDevices) {
1447 if (_recording && captureDeviceIsAlive == 1) {
1448 _recording = false;
1449 _doStopRec = true; // Signal to io proc to stop audio device
1450 _critSect.Leave(); // Cannot be under lock, risk of deadlock
1451 if (kEventTimeout == _stopEventRec.Wait(2000)) {
1452 CriticalSectionScoped critScoped(&_critSect);
1453 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1454 " Timed out stopping the capture IOProc. "
1455 "We may have failed to detect a device removal.");
1456
1457 WEBRTC_CA_LOG_WARN(AudioDeviceStop(_inputDeviceID, _inDeviceIOProcID));
1458 WEBRTC_CA_LOG_WARN(
1459 AudioDeviceDestroyIOProcID(_inputDeviceID, _inDeviceIOProcID));
1460 }
1461 _critSect.Enter();
1462 _doStopRec = false;
1463 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " Recording stopped");
1464 }
1465 } else {
1466 // We signal a stop for a shared device even when rendering has
1467 // not yet ended. This is to ensure the IOProc will return early as
1468 // intended (by checking |_recording|) before accessing
1469 // resources we free below (e.g. the capture converter).
1470 //
1471 // In the case of a shared devcie, the IOProc will verify
1472 // rendering has ended before stopping itself.
1473 if (_recording && captureDeviceIsAlive == 1) {
1474 _recording = false;
1475 _doStop = true; // Signal to io proc to stop audio device
1476 _critSect.Leave(); // Cannot be under lock, risk of deadlock
1477 if (kEventTimeout == _stopEvent.Wait(2000)) {
1478 CriticalSectionScoped critScoped(&_critSect);
1479 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1480 " Timed out stopping the shared IOProc. "
1481 "We may have failed to detect a device removal.");
1482
1483 // We assume rendering on a shared device has stopped as well if
1484 // the IOProc times out.
1485 WEBRTC_CA_LOG_WARN(AudioDeviceStop(_outputDeviceID, _deviceIOProcID));
1486 WEBRTC_CA_LOG_WARN(
1487 AudioDeviceDestroyIOProcID(_outputDeviceID, _deviceIOProcID));
1488 }
1489 _critSect.Enter();
1490 _doStop = false;
1491 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
1492 " Recording stopped (shared)");
1493 }
1494 }
1495
1496 // Setting this signal will allow the worker thread to be stopped.
1497 AtomicSet32(&_captureDeviceIsAlive, 0);
1498
1499 if (capture_worker_thread_.get()) {
1500 _critSect.Leave();
1501 capture_worker_thread_->Stop();
1502 capture_worker_thread_.reset();
1503 _critSect.Enter();
1504 }
1505
1506 WEBRTC_CA_LOG_WARN(AudioConverterDispose(_captureConverter));
1507
1508 // Remove listeners.
1509 AudioObjectPropertyAddress propertyAddress = {
1510 kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput, 0};
1511 WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
1512 _inputDeviceID, &propertyAddress, &objectListenerProc, this));
1513
1514 propertyAddress.mSelector = kAudioDeviceProcessorOverload;
1515 WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
1516 _inputDeviceID, &propertyAddress, &objectListenerProc, this));
1517
1518 _recIsInitialized = false;
1519 _recording = false;
1520
1521 return 0;
1522 }
1523
RecordingIsInitialized() const1524 bool AudioDeviceMac::RecordingIsInitialized() const {
1525 return (_recIsInitialized);
1526 }
1527
Recording() const1528 bool AudioDeviceMac::Recording() const {
1529 return (_recording);
1530 }
1531
PlayoutIsInitialized() const1532 bool AudioDeviceMac::PlayoutIsInitialized() const {
1533 return (_playIsInitialized);
1534 }
1535
StartPlayout()1536 int32_t AudioDeviceMac::StartPlayout() {
1537 CriticalSectionScoped lock(&_critSect);
1538
1539 if (!_playIsInitialized) {
1540 return -1;
1541 }
1542
1543 if (_playing) {
1544 return 0;
1545 }
1546
1547 RTC_DCHECK(!render_worker_thread_.get());
1548 render_worker_thread_.reset(
1549 new rtc::PlatformThread(RunRender, this, "RenderWorkerThread"));
1550 render_worker_thread_->Start();
1551 render_worker_thread_->SetPriority(rtc::kRealtimePriority);
1552
1553 if (_twoDevices || !_recording) {
1554 OSStatus err = noErr;
1555 WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_outputDeviceID, _deviceIOProcID));
1556 }
1557 _playing = true;
1558
1559 return 0;
1560 }
1561
StopPlayout()1562 int32_t AudioDeviceMac::StopPlayout() {
1563 CriticalSectionScoped lock(&_critSect);
1564
1565 if (!_playIsInitialized) {
1566 return 0;
1567 }
1568
1569 OSStatus err = noErr;
1570
1571 int32_t renderDeviceIsAlive = AtomicGet32(&_renderDeviceIsAlive);
1572 if (_playing && renderDeviceIsAlive == 1) {
1573 // We signal a stop for a shared device even when capturing has not
1574 // yet ended. This is to ensure the IOProc will return early as
1575 // intended (by checking |_playing|) before accessing resources we
1576 // free below (e.g. the render converter).
1577 //
1578 // In the case of a shared device, the IOProc will verify capturing
1579 // has ended before stopping itself.
1580 _playing = false;
1581 _doStop = true; // Signal to io proc to stop audio device
1582 _critSect.Leave(); // Cannot be under lock, risk of deadlock
1583 if (kEventTimeout == _stopEvent.Wait(2000)) {
1584 CriticalSectionScoped critScoped(&_critSect);
1585 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1586 " Timed out stopping the render IOProc. "
1587 "We may have failed to detect a device removal.");
1588
1589 // We assume capturing on a shared device has stopped as well if the
1590 // IOProc times out.
1591 WEBRTC_CA_LOG_WARN(AudioDeviceStop(_outputDeviceID, _deviceIOProcID));
1592 WEBRTC_CA_LOG_WARN(
1593 AudioDeviceDestroyIOProcID(_outputDeviceID, _deviceIOProcID));
1594 }
1595 _critSect.Enter();
1596 _doStop = false;
1597 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, "Playout stopped");
1598 }
1599
1600 // Setting this signal will allow the worker thread to be stopped.
1601 AtomicSet32(&_renderDeviceIsAlive, 0);
1602 if (render_worker_thread_.get()) {
1603 _critSect.Leave();
1604 render_worker_thread_->Stop();
1605 render_worker_thread_.reset();
1606 _critSect.Enter();
1607 }
1608
1609 WEBRTC_CA_LOG_WARN(AudioConverterDispose(_renderConverter));
1610
1611 // Remove listeners.
1612 AudioObjectPropertyAddress propertyAddress = {
1613 kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput, 0};
1614 WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
1615 _outputDeviceID, &propertyAddress, &objectListenerProc, this));
1616
1617 propertyAddress.mSelector = kAudioDeviceProcessorOverload;
1618 WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
1619 _outputDeviceID, &propertyAddress, &objectListenerProc, this));
1620
1621 if (_macBookPro) {
1622 Boolean hasProperty =
1623 AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
1624 if (hasProperty) {
1625 propertyAddress.mSelector = kAudioDevicePropertyDataSource;
1626 WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
1627 _outputDeviceID, &propertyAddress, &objectListenerProc, this));
1628 }
1629 }
1630
1631 _playIsInitialized = false;
1632 _playing = false;
1633
1634 return 0;
1635 }
1636
PlayoutDelay(uint16_t & delayMS) const1637 int32_t AudioDeviceMac::PlayoutDelay(uint16_t& delayMS) const {
1638 int32_t renderDelayUs = AtomicGet32(&_renderDelayUs);
1639 delayMS =
1640 static_cast<uint16_t>(1e-3 * (renderDelayUs + _renderLatencyUs) + 0.5);
1641 return 0;
1642 }
1643
RecordingDelay(uint16_t & delayMS) const1644 int32_t AudioDeviceMac::RecordingDelay(uint16_t& delayMS) const {
1645 int32_t captureDelayUs = AtomicGet32(&_captureDelayUs);
1646 delayMS =
1647 static_cast<uint16_t>(1e-3 * (captureDelayUs + _captureLatencyUs) + 0.5);
1648 return 0;
1649 }
1650
Playing() const1651 bool AudioDeviceMac::Playing() const {
1652 return (_playing);
1653 }
1654
SetPlayoutBuffer(const AudioDeviceModule::BufferType type,uint16_t sizeMS)1655 int32_t AudioDeviceMac::SetPlayoutBuffer(
1656 const AudioDeviceModule::BufferType type,
1657 uint16_t sizeMS) {
1658 if (type != AudioDeviceModule::kFixedBufferSize) {
1659 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1660 " Adaptive buffer size not supported on this platform");
1661 return -1;
1662 }
1663
1664 _playBufType = type;
1665 _playBufDelayFixed = sizeMS;
1666 return 0;
1667 }
1668
PlayoutBuffer(AudioDeviceModule::BufferType & type,uint16_t & sizeMS) const1669 int32_t AudioDeviceMac::PlayoutBuffer(AudioDeviceModule::BufferType& type,
1670 uint16_t& sizeMS) const {
1671 type = _playBufType;
1672 sizeMS = _playBufDelayFixed;
1673
1674 return 0;
1675 }
1676
1677 // Not implemented for Mac.
CPULoad(uint16_t &) const1678 int32_t AudioDeviceMac::CPULoad(uint16_t& /*load*/) const {
1679 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1680 " API call not supported on this platform");
1681
1682 return -1;
1683 }
1684
PlayoutWarning() const1685 bool AudioDeviceMac::PlayoutWarning() const {
1686 return (_playWarning > 0);
1687 }
1688
PlayoutError() const1689 bool AudioDeviceMac::PlayoutError() const {
1690 return (_playError > 0);
1691 }
1692
RecordingWarning() const1693 bool AudioDeviceMac::RecordingWarning() const {
1694 return (_recWarning > 0);
1695 }
1696
RecordingError() const1697 bool AudioDeviceMac::RecordingError() const {
1698 return (_recError > 0);
1699 }
1700
ClearPlayoutWarning()1701 void AudioDeviceMac::ClearPlayoutWarning() {
1702 _playWarning = 0;
1703 }
1704
ClearPlayoutError()1705 void AudioDeviceMac::ClearPlayoutError() {
1706 _playError = 0;
1707 }
1708
ClearRecordingWarning()1709 void AudioDeviceMac::ClearRecordingWarning() {
1710 _recWarning = 0;
1711 }
1712
ClearRecordingError()1713 void AudioDeviceMac::ClearRecordingError() {
1714 _recError = 0;
1715 }
1716
1717 // ============================================================================
1718 // Private Methods
1719 // ============================================================================
1720
GetNumberDevices(const AudioObjectPropertyScope scope,AudioDeviceID scopedDeviceIds[],const uint32_t deviceListLength)1721 int32_t AudioDeviceMac::GetNumberDevices(const AudioObjectPropertyScope scope,
1722 AudioDeviceID scopedDeviceIds[],
1723 const uint32_t deviceListLength) {
1724 OSStatus err = noErr;
1725
1726 AudioObjectPropertyAddress propertyAddress = {
1727 kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
1728 kAudioObjectPropertyElementMaster};
1729 UInt32 size = 0;
1730 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyDataSize(
1731 kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size));
1732 if (size == 0) {
1733 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "No devices");
1734 return 0;
1735 }
1736
1737 AudioDeviceID* deviceIds = (AudioDeviceID*)malloc(size);
1738 UInt32 numberDevices = size / sizeof(AudioDeviceID);
1739 AudioBufferList* bufferList = NULL;
1740 UInt32 numberScopedDevices = 0;
1741
1742 // First check if there is a default device and list it
1743 UInt32 hardwareProperty = 0;
1744 if (scope == kAudioDevicePropertyScopeOutput) {
1745 hardwareProperty = kAudioHardwarePropertyDefaultOutputDevice;
1746 } else {
1747 hardwareProperty = kAudioHardwarePropertyDefaultInputDevice;
1748 }
1749
1750 AudioObjectPropertyAddress propertyAddressDefault = {
1751 hardwareProperty, kAudioObjectPropertyScopeGlobal,
1752 kAudioObjectPropertyElementMaster};
1753
1754 AudioDeviceID usedID;
1755 UInt32 uintSize = sizeof(UInt32);
1756 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(kAudioObjectSystemObject,
1757 &propertyAddressDefault, 0,
1758 NULL, &uintSize, &usedID));
1759 if (usedID != kAudioDeviceUnknown) {
1760 scopedDeviceIds[numberScopedDevices] = usedID;
1761 numberScopedDevices++;
1762 } else {
1763 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1764 "GetNumberDevices(): Default device unknown");
1765 }
1766
1767 // Then list the rest of the devices
1768 bool listOK = true;
1769
1770 WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyData(
1771 kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, deviceIds));
1772 if (err != noErr) {
1773 listOK = false;
1774 } else {
1775 propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
1776 propertyAddress.mScope = scope;
1777 propertyAddress.mElement = 0;
1778 for (UInt32 i = 0; i < numberDevices; i++) {
1779 // Check for input channels
1780 WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyDataSize(
1781 deviceIds[i], &propertyAddress, 0, NULL, &size));
1782 if (err == kAudioHardwareBadDeviceError) {
1783 // This device doesn't actually exist; continue iterating.
1784 continue;
1785 } else if (err != noErr) {
1786 listOK = false;
1787 break;
1788 }
1789
1790 bufferList = (AudioBufferList*)malloc(size);
1791 WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyData(
1792 deviceIds[i], &propertyAddress, 0, NULL, &size, bufferList));
1793 if (err != noErr) {
1794 listOK = false;
1795 break;
1796 }
1797
1798 if (bufferList->mNumberBuffers > 0) {
1799 if (numberScopedDevices >= deviceListLength) {
1800 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1801 "Device list is not long enough");
1802 listOK = false;
1803 break;
1804 }
1805
1806 scopedDeviceIds[numberScopedDevices] = deviceIds[i];
1807 numberScopedDevices++;
1808 }
1809
1810 free(bufferList);
1811 bufferList = NULL;
1812 } // for
1813 }
1814
1815 if (!listOK) {
1816 if (deviceIds) {
1817 free(deviceIds);
1818 deviceIds = NULL;
1819 }
1820
1821 if (bufferList) {
1822 free(bufferList);
1823 bufferList = NULL;
1824 }
1825
1826 return -1;
1827 }
1828
1829 // Happy ending
1830 if (deviceIds) {
1831 free(deviceIds);
1832 deviceIds = NULL;
1833 }
1834
1835 return numberScopedDevices;
1836 }
1837
GetDeviceName(const AudioObjectPropertyScope scope,const uint16_t index,char * name)1838 int32_t AudioDeviceMac::GetDeviceName(const AudioObjectPropertyScope scope,
1839 const uint16_t index,
1840 char* name) {
1841 OSStatus err = noErr;
1842 UInt32 len = kAdmMaxDeviceNameSize;
1843 AudioDeviceID deviceIds[MaxNumberDevices];
1844
1845 int numberDevices = GetNumberDevices(scope, deviceIds, MaxNumberDevices);
1846 if (numberDevices < 0) {
1847 return -1;
1848 } else if (numberDevices == 0) {
1849 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "No devices");
1850 return -1;
1851 }
1852
1853 // If the number is below the number of devices, assume it's "WEBRTC ID"
1854 // otherwise assume it's a CoreAudio ID
1855 AudioDeviceID usedID;
1856
1857 // Check if there is a default device
1858 bool isDefaultDevice = false;
1859 if (index == 0) {
1860 UInt32 hardwareProperty = 0;
1861 if (scope == kAudioDevicePropertyScopeOutput) {
1862 hardwareProperty = kAudioHardwarePropertyDefaultOutputDevice;
1863 } else {
1864 hardwareProperty = kAudioHardwarePropertyDefaultInputDevice;
1865 }
1866 AudioObjectPropertyAddress propertyAddress = {
1867 hardwareProperty, kAudioObjectPropertyScopeGlobal,
1868 kAudioObjectPropertyElementMaster};
1869 UInt32 size = sizeof(UInt32);
1870 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
1871 kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &usedID));
1872 if (usedID == kAudioDeviceUnknown) {
1873 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1874 "GetDeviceName(): Default device unknown");
1875 } else {
1876 isDefaultDevice = true;
1877 }
1878 }
1879
1880 AudioObjectPropertyAddress propertyAddress = {kAudioDevicePropertyDeviceName,
1881 scope, 0};
1882
1883 if (isDefaultDevice) {
1884 char devName[len];
1885
1886 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(usedID, &propertyAddress,
1887 0, NULL, &len, devName));
1888
1889 sprintf(name, "default (%s)", devName);
1890 } else {
1891 if (index < numberDevices) {
1892 usedID = deviceIds[index];
1893 } else {
1894 usedID = index;
1895 }
1896
1897 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(usedID, &propertyAddress,
1898 0, NULL, &len, name));
1899 }
1900
1901 return 0;
1902 }
1903
InitDevice(const uint16_t userDeviceIndex,AudioDeviceID & deviceId,const bool isInput)1904 int32_t AudioDeviceMac::InitDevice(const uint16_t userDeviceIndex,
1905 AudioDeviceID& deviceId,
1906 const bool isInput) {
1907 OSStatus err = noErr;
1908 UInt32 size = 0;
1909 AudioObjectPropertyScope deviceScope;
1910 AudioObjectPropertySelector defaultDeviceSelector;
1911 AudioDeviceID deviceIds[MaxNumberDevices];
1912
1913 if (isInput) {
1914 deviceScope = kAudioDevicePropertyScopeInput;
1915 defaultDeviceSelector = kAudioHardwarePropertyDefaultInputDevice;
1916 } else {
1917 deviceScope = kAudioDevicePropertyScopeOutput;
1918 defaultDeviceSelector = kAudioHardwarePropertyDefaultOutputDevice;
1919 }
1920
1921 AudioObjectPropertyAddress propertyAddress = {
1922 defaultDeviceSelector, kAudioObjectPropertyScopeGlobal,
1923 kAudioObjectPropertyElementMaster};
1924
1925 // Get the actual device IDs
1926 int numberDevices =
1927 GetNumberDevices(deviceScope, deviceIds, MaxNumberDevices);
1928 if (numberDevices < 0) {
1929 return -1;
1930 } else if (numberDevices == 0) {
1931 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1932 "InitDevice(): No devices");
1933 return -1;
1934 }
1935
1936 bool isDefaultDevice = false;
1937 deviceId = kAudioDeviceUnknown;
1938 if (userDeviceIndex == 0) {
1939 // Try to use default system device
1940 size = sizeof(AudioDeviceID);
1941 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
1942 kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &deviceId));
1943 if (deviceId == kAudioDeviceUnknown) {
1944 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
1945 " No default device exists");
1946 } else {
1947 isDefaultDevice = true;
1948 }
1949 }
1950
1951 if (!isDefaultDevice) {
1952 deviceId = deviceIds[userDeviceIndex];
1953 }
1954
1955 // Obtain device name and manufacturer for logging.
1956 // Also use this as a test to ensure a user-set device ID is valid.
1957 char devName[128];
1958 char devManf[128];
1959 memset(devName, 0, sizeof(devName));
1960 memset(devManf, 0, sizeof(devManf));
1961
1962 propertyAddress.mSelector = kAudioDevicePropertyDeviceName;
1963 propertyAddress.mScope = deviceScope;
1964 propertyAddress.mElement = 0;
1965 size = sizeof(devName);
1966 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(deviceId, &propertyAddress,
1967 0, NULL, &size, devName));
1968
1969 propertyAddress.mSelector = kAudioDevicePropertyDeviceManufacturer;
1970 size = sizeof(devManf);
1971 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(deviceId, &propertyAddress,
1972 0, NULL, &size, devManf));
1973
1974 if (isInput) {
1975 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " Input device: %s %s",
1976 devManf, devName);
1977 } else {
1978 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " Output device: %s %s",
1979 devManf, devName);
1980 }
1981
1982 return 0;
1983 }
1984
SetDesiredPlayoutFormat()1985 OSStatus AudioDeviceMac::SetDesiredPlayoutFormat() {
1986 // Our preferred format to work with.
1987 _outDesiredFormat.mSampleRate = N_PLAY_SAMPLES_PER_SEC;
1988 _outDesiredFormat.mChannelsPerFrame = _playChannels;
1989
1990 if (_ptrAudioBuffer) {
1991 // Update audio buffer with the selected parameters.
1992 _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC);
1993 _ptrAudioBuffer->SetPlayoutChannels((uint8_t)_playChannels);
1994 }
1995
1996 _renderDelayOffsetSamples = _renderBufSizeSamples -
1997 N_BUFFERS_OUT * ENGINE_PLAY_BUF_SIZE_IN_SAMPLES *
1998 _outDesiredFormat.mChannelsPerFrame;
1999
2000 _outDesiredFormat.mBytesPerPacket =
2001 _outDesiredFormat.mChannelsPerFrame * sizeof(SInt16);
2002 // In uncompressed audio, a packet is one frame.
2003 _outDesiredFormat.mFramesPerPacket = 1;
2004 _outDesiredFormat.mBytesPerFrame =
2005 _outDesiredFormat.mChannelsPerFrame * sizeof(SInt16);
2006 _outDesiredFormat.mBitsPerChannel = sizeof(SInt16) * 8;
2007
2008 _outDesiredFormat.mFormatFlags =
2009 kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
2010 #ifdef WEBRTC_ARCH_BIG_ENDIAN
2011 _outDesiredFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
2012 #endif
2013 _outDesiredFormat.mFormatID = kAudioFormatLinearPCM;
2014
2015 OSStatus err = noErr;
2016 WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(
2017 &_outDesiredFormat, &_outStreamFormat, &_renderConverter));
2018
2019 // Try to set buffer size to desired value (_playBufDelayFixed).
2020 UInt32 bufByteCount = static_cast<UInt32>(
2021 (_outStreamFormat.mSampleRate / 1000.0) * _playBufDelayFixed *
2022 _outStreamFormat.mChannelsPerFrame * sizeof(Float32));
2023 if (_outStreamFormat.mFramesPerPacket != 0) {
2024 if (bufByteCount % _outStreamFormat.mFramesPerPacket != 0) {
2025 bufByteCount = (static_cast<UInt32>(bufByteCount /
2026 _outStreamFormat.mFramesPerPacket) +
2027 1) *
2028 _outStreamFormat.mFramesPerPacket;
2029 }
2030 }
2031
2032 // Ensure the buffer size is within the range provided by the device.
2033 AudioObjectPropertyAddress propertyAddress = {
2034 kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput, 0};
2035 propertyAddress.mSelector = kAudioDevicePropertyBufferSizeRange;
2036 AudioValueRange range;
2037 UInt32 size = sizeof(range);
2038 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
2039 _outputDeviceID, &propertyAddress, 0, NULL, &size, &range));
2040 if (range.mMinimum > bufByteCount) {
2041 bufByteCount = range.mMinimum;
2042 } else if (range.mMaximum < bufByteCount) {
2043 bufByteCount = range.mMaximum;
2044 }
2045
2046 propertyAddress.mSelector = kAudioDevicePropertyBufferSize;
2047 size = sizeof(bufByteCount);
2048 WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
2049 _outputDeviceID, &propertyAddress, 0, NULL, size, &bufByteCount));
2050
2051 // Get render device latency.
2052 propertyAddress.mSelector = kAudioDevicePropertyLatency;
2053 UInt32 latency = 0;
2054 size = sizeof(UInt32);
2055 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
2056 _outputDeviceID, &propertyAddress, 0, NULL, &size, &latency));
2057 _renderLatencyUs =
2058 static_cast<uint32_t>((1.0e6 * latency) / _outStreamFormat.mSampleRate);
2059
2060 // Get render stream latency.
2061 propertyAddress.mSelector = kAudioDevicePropertyStreams;
2062 AudioStreamID stream = 0;
2063 size = sizeof(AudioStreamID);
2064 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
2065 _outputDeviceID, &propertyAddress, 0, NULL, &size, &stream));
2066 propertyAddress.mSelector = kAudioStreamPropertyLatency;
2067 size = sizeof(UInt32);
2068 latency = 0;
2069 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
2070 _outputDeviceID, &propertyAddress, 0, NULL, &size, &latency));
2071 _renderLatencyUs +=
2072 static_cast<uint32_t>((1.0e6 * latency) / _outStreamFormat.mSampleRate);
2073
2074 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
2075 " initial playout status: _renderDelayOffsetSamples=%d,"
2076 " _renderDelayUs=%d, _renderLatencyUs=%d",
2077 _renderDelayOffsetSamples, _renderDelayUs, _renderLatencyUs);
2078 return 0;
2079 }
2080
objectListenerProc(AudioObjectID objectId,UInt32 numberAddresses,const AudioObjectPropertyAddress addresses[],void * clientData)2081 OSStatus AudioDeviceMac::objectListenerProc(
2082 AudioObjectID objectId,
2083 UInt32 numberAddresses,
2084 const AudioObjectPropertyAddress addresses[],
2085 void* clientData) {
2086 AudioDeviceMac* ptrThis = (AudioDeviceMac*)clientData;
2087 RTC_DCHECK(ptrThis != NULL);
2088
2089 ptrThis->implObjectListenerProc(objectId, numberAddresses, addresses);
2090
2091 // AudioObjectPropertyListenerProc functions are supposed to return 0
2092 return 0;
2093 }
2094
implObjectListenerProc(const AudioObjectID objectId,const UInt32 numberAddresses,const AudioObjectPropertyAddress addresses[])2095 OSStatus AudioDeviceMac::implObjectListenerProc(
2096 const AudioObjectID objectId,
2097 const UInt32 numberAddresses,
2098 const AudioObjectPropertyAddress addresses[]) {
2099 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
2100 "AudioDeviceMac::implObjectListenerProc()");
2101
2102 for (UInt32 i = 0; i < numberAddresses; i++) {
2103 if (addresses[i].mSelector == kAudioHardwarePropertyDevices) {
2104 HandleDeviceChange();
2105 } else if (addresses[i].mSelector == kAudioDevicePropertyStreamFormat) {
2106 HandleStreamFormatChange(objectId, addresses[i]);
2107 } else if (addresses[i].mSelector == kAudioDevicePropertyDataSource) {
2108 HandleDataSourceChange(objectId, addresses[i]);
2109 } else if (addresses[i].mSelector == kAudioDeviceProcessorOverload) {
2110 HandleProcessorOverload(addresses[i]);
2111 }
2112 }
2113
2114 return 0;
2115 }
2116
HandleDeviceChange()2117 int32_t AudioDeviceMac::HandleDeviceChange() {
2118 OSStatus err = noErr;
2119
2120 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
2121 "kAudioHardwarePropertyDevices");
2122
2123 // A device has changed. Check if our registered devices have been removed.
2124 // Ensure the devices have been initialized, meaning the IDs are valid.
2125 if (MicrophoneIsInitialized()) {
2126 AudioObjectPropertyAddress propertyAddress = {
2127 kAudioDevicePropertyDeviceIsAlive, kAudioDevicePropertyScopeInput, 0};
2128 UInt32 deviceIsAlive = 1;
2129 UInt32 size = sizeof(UInt32);
2130 err = AudioObjectGetPropertyData(_inputDeviceID, &propertyAddress, 0, NULL,
2131 &size, &deviceIsAlive);
2132
2133 if (err == kAudioHardwareBadDeviceError || deviceIsAlive == 0) {
2134 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
2135 "Capture device is not alive (probably removed)");
2136 AtomicSet32(&_captureDeviceIsAlive, 0);
2137 _mixerManager.CloseMicrophone();
2138 if (_recError == 1) {
2139 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
2140 " pending recording error exists");
2141 }
2142 _recError = 1; // triggers callback from module process thread
2143 } else if (err != noErr) {
2144 logCAMsg(kTraceError, kTraceAudioDevice, _id,
2145 "Error in AudioDeviceGetProperty()", (const char*)&err);
2146 return -1;
2147 }
2148 }
2149
2150 if (SpeakerIsInitialized()) {
2151 AudioObjectPropertyAddress propertyAddress = {
2152 kAudioDevicePropertyDeviceIsAlive, kAudioDevicePropertyScopeOutput, 0};
2153 UInt32 deviceIsAlive = 1;
2154 UInt32 size = sizeof(UInt32);
2155 err = AudioObjectGetPropertyData(_outputDeviceID, &propertyAddress, 0, NULL,
2156 &size, &deviceIsAlive);
2157
2158 if (err == kAudioHardwareBadDeviceError || deviceIsAlive == 0) {
2159 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
2160 "Render device is not alive (probably removed)");
2161 AtomicSet32(&_renderDeviceIsAlive, 0);
2162 _mixerManager.CloseSpeaker();
2163 if (_playError == 1) {
2164 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
2165 " pending playout error exists");
2166 }
2167 _playError = 1; // triggers callback from module process thread
2168 } else if (err != noErr) {
2169 logCAMsg(kTraceError, kTraceAudioDevice, _id,
2170 "Error in AudioDeviceGetProperty()", (const char*)&err);
2171 return -1;
2172 }
2173 }
2174
2175 return 0;
2176 }
2177
HandleStreamFormatChange(const AudioObjectID objectId,const AudioObjectPropertyAddress propertyAddress)2178 int32_t AudioDeviceMac::HandleStreamFormatChange(
2179 const AudioObjectID objectId,
2180 const AudioObjectPropertyAddress propertyAddress) {
2181 OSStatus err = noErr;
2182
2183 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, "Stream format changed");
2184
2185 if (objectId != _inputDeviceID && objectId != _outputDeviceID) {
2186 return 0;
2187 }
2188
2189 // Get the new device format
2190 AudioStreamBasicDescription streamFormat;
2191 UInt32 size = sizeof(streamFormat);
2192 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
2193 objectId, &propertyAddress, 0, NULL, &size, &streamFormat));
2194
2195 if (streamFormat.mFormatID != kAudioFormatLinearPCM) {
2196 logCAMsg(kTraceError, kTraceAudioDevice, _id,
2197 "Unacceptable input stream format -> mFormatID",
2198 (const char*)&streamFormat.mFormatID);
2199 return -1;
2200 }
2201
2202 if (streamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS) {
2203 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
2204 "Too many channels on device (mChannelsPerFrame = %d)",
2205 streamFormat.mChannelsPerFrame);
2206 return -1;
2207 }
2208
2209 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Stream format:");
2210 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
2211 "mSampleRate = %f, mChannelsPerFrame = %u",
2212 streamFormat.mSampleRate, streamFormat.mChannelsPerFrame);
2213 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
2214 "mBytesPerPacket = %u, mFramesPerPacket = %u",
2215 streamFormat.mBytesPerPacket, streamFormat.mFramesPerPacket);
2216 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
2217 "mBytesPerFrame = %u, mBitsPerChannel = %u",
2218 streamFormat.mBytesPerFrame, streamFormat.mBitsPerChannel);
2219 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "mFormatFlags = %u",
2220 streamFormat.mFormatFlags);
2221 logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
2222 (const char*)&streamFormat.mFormatID);
2223
2224 if (propertyAddress.mScope == kAudioDevicePropertyScopeInput) {
2225 const int io_block_size_samples = streamFormat.mChannelsPerFrame *
2226 streamFormat.mSampleRate / 100 *
2227 N_BLOCKS_IO;
2228 if (io_block_size_samples > _captureBufSizeSamples) {
2229 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
2230 "Input IO block size (%d) is larger than ring buffer (%u)",
2231 io_block_size_samples, _captureBufSizeSamples);
2232 return -1;
2233 }
2234
2235 memcpy(&_inStreamFormat, &streamFormat, sizeof(streamFormat));
2236
2237 if (_inStreamFormat.mChannelsPerFrame >= 2 && (_recChannels == 2)) {
2238 _inDesiredFormat.mChannelsPerFrame = 2;
2239 } else {
2240 // Disable stereo recording when we only have one channel on the device.
2241 _inDesiredFormat.mChannelsPerFrame = 1;
2242 _recChannels = 1;
2243 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
2244 "Stereo recording unavailable on this device");
2245 }
2246
2247 if (_ptrAudioBuffer) {
2248 // Update audio buffer with the selected parameters
2249 _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC);
2250 _ptrAudioBuffer->SetRecordingChannels((uint8_t)_recChannels);
2251 }
2252
2253 // Recreate the converter with the new format
2254 // TODO(xians): make this thread safe
2255 WEBRTC_CA_RETURN_ON_ERR(AudioConverterDispose(_captureConverter));
2256
2257 WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(&streamFormat, &_inDesiredFormat,
2258 &_captureConverter));
2259 } else {
2260 memcpy(&_outStreamFormat, &streamFormat, sizeof(streamFormat));
2261
2262 // Our preferred format to work with
2263 if (_outStreamFormat.mChannelsPerFrame < 2) {
2264 _playChannels = 1;
2265 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
2266 "Stereo playout unavailable on this device");
2267 }
2268 WEBRTC_CA_RETURN_ON_ERR(SetDesiredPlayoutFormat());
2269 }
2270 return 0;
2271 }
2272
HandleDataSourceChange(const AudioObjectID objectId,const AudioObjectPropertyAddress propertyAddress)2273 int32_t AudioDeviceMac::HandleDataSourceChange(
2274 const AudioObjectID objectId,
2275 const AudioObjectPropertyAddress propertyAddress) {
2276 OSStatus err = noErr;
2277
2278 if (_macBookPro &&
2279 propertyAddress.mScope == kAudioDevicePropertyScopeOutput) {
2280 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, "Data source changed");
2281
2282 _macBookProPanRight = false;
2283 UInt32 dataSource = 0;
2284 UInt32 size = sizeof(UInt32);
2285 WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
2286 objectId, &propertyAddress, 0, NULL, &size, &dataSource));
2287 if (dataSource == 'ispk') {
2288 _macBookProPanRight = true;
2289 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
2290 "MacBook Pro using internal speakers; stereo panning right");
2291 } else {
2292 WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
2293 "MacBook Pro not using internal speakers");
2294 }
2295 }
2296
2297 return 0;
2298 }
HandleProcessorOverload(const AudioObjectPropertyAddress propertyAddress)2299 int32_t AudioDeviceMac::HandleProcessorOverload(
2300 const AudioObjectPropertyAddress propertyAddress) {
2301 // TODO(xians): we probably want to notify the user in some way of the
2302 // overload. However, the Windows interpretations of these errors seem to
2303 // be more severe than what ProcessorOverload is thrown for.
2304 //
2305 // We don't log the notification, as it's sent from the HAL's IO thread. We
2306 // don't want to slow it down even further.
2307 if (propertyAddress.mScope == kAudioDevicePropertyScopeInput) {
2308 // WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "Capture processor
2309 // overload");
2310 //_callback->ProblemIsReported(
2311 // SndCardStreamObserver::ERecordingProblem);
2312 } else {
2313 // WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
2314 // "Render processor overload");
2315 //_callback->ProblemIsReported(
2316 // SndCardStreamObserver::EPlaybackProblem);
2317 }
2318
2319 return 0;
2320 }
2321
2322 // ============================================================================
2323 // Thread Methods
2324 // ============================================================================
2325
deviceIOProc(AudioDeviceID,const AudioTimeStamp *,const AudioBufferList * inputData,const AudioTimeStamp * inputTime,AudioBufferList * outputData,const AudioTimeStamp * outputTime,void * clientData)2326 OSStatus AudioDeviceMac::deviceIOProc(AudioDeviceID,
2327 const AudioTimeStamp*,
2328 const AudioBufferList* inputData,
2329 const AudioTimeStamp* inputTime,
2330 AudioBufferList* outputData,
2331 const AudioTimeStamp* outputTime,
2332 void* clientData) {
2333 AudioDeviceMac* ptrThis = (AudioDeviceMac*)clientData;
2334 RTC_DCHECK(ptrThis != NULL);
2335
2336 ptrThis->implDeviceIOProc(inputData, inputTime, outputData, outputTime);
2337
2338 // AudioDeviceIOProc functions are supposed to return 0
2339 return 0;
2340 }
2341
outConverterProc(AudioConverterRef,UInt32 * numberDataPackets,AudioBufferList * data,AudioStreamPacketDescription **,void * userData)2342 OSStatus AudioDeviceMac::outConverterProc(AudioConverterRef,
2343 UInt32* numberDataPackets,
2344 AudioBufferList* data,
2345 AudioStreamPacketDescription**,
2346 void* userData) {
2347 AudioDeviceMac* ptrThis = (AudioDeviceMac*)userData;
2348 RTC_DCHECK(ptrThis != NULL);
2349
2350 return ptrThis->implOutConverterProc(numberDataPackets, data);
2351 }
2352
inDeviceIOProc(AudioDeviceID,const AudioTimeStamp *,const AudioBufferList * inputData,const AudioTimeStamp * inputTime,AudioBufferList *,const AudioTimeStamp *,void * clientData)2353 OSStatus AudioDeviceMac::inDeviceIOProc(AudioDeviceID,
2354 const AudioTimeStamp*,
2355 const AudioBufferList* inputData,
2356 const AudioTimeStamp* inputTime,
2357 AudioBufferList*,
2358 const AudioTimeStamp*,
2359 void* clientData) {
2360 AudioDeviceMac* ptrThis = (AudioDeviceMac*)clientData;
2361 RTC_DCHECK(ptrThis != NULL);
2362
2363 ptrThis->implInDeviceIOProc(inputData, inputTime);
2364
2365 // AudioDeviceIOProc functions are supposed to return 0
2366 return 0;
2367 }
2368
inConverterProc(AudioConverterRef,UInt32 * numberDataPackets,AudioBufferList * data,AudioStreamPacketDescription **,void * userData)2369 OSStatus AudioDeviceMac::inConverterProc(
2370 AudioConverterRef,
2371 UInt32* numberDataPackets,
2372 AudioBufferList* data,
2373 AudioStreamPacketDescription** /*dataPacketDescription*/,
2374 void* userData) {
2375 AudioDeviceMac* ptrThis = static_cast<AudioDeviceMac*>(userData);
2376 RTC_DCHECK(ptrThis != NULL);
2377
2378 return ptrThis->implInConverterProc(numberDataPackets, data);
2379 }
2380
implDeviceIOProc(const AudioBufferList * inputData,const AudioTimeStamp * inputTime,AudioBufferList * outputData,const AudioTimeStamp * outputTime)2381 OSStatus AudioDeviceMac::implDeviceIOProc(const AudioBufferList* inputData,
2382 const AudioTimeStamp* inputTime,
2383 AudioBufferList* outputData,
2384 const AudioTimeStamp* outputTime) {
2385 OSStatus err = noErr;
2386 UInt64 outputTimeNs = AudioConvertHostTimeToNanos(outputTime->mHostTime);
2387 UInt64 nowNs = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
2388
2389 if (!_twoDevices && _recording) {
2390 implInDeviceIOProc(inputData, inputTime);
2391 }
2392
2393 // Check if we should close down audio device
2394 // Double-checked locking optimization to remove locking overhead
2395 if (_doStop) {
2396 _critSect.Enter();
2397 if (_doStop) {
2398 if (_twoDevices || (!_recording && !_playing)) {
2399 // In the case of a shared device, the single driving ioProc
2400 // is stopped here
2401 WEBRTC_CA_LOG_ERR(AudioDeviceStop(_outputDeviceID, _deviceIOProcID));
2402 WEBRTC_CA_LOG_WARN(
2403 AudioDeviceDestroyIOProcID(_outputDeviceID, _deviceIOProcID));
2404 if (err == noErr) {
2405 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
2406 " Playout or shared device stopped");
2407 }
2408 }
2409
2410 _doStop = false;
2411 _stopEvent.Set();
2412 _critSect.Leave();
2413 return 0;
2414 }
2415 _critSect.Leave();
2416 }
2417
2418 if (!_playing) {
2419 // This can be the case when a shared device is capturing but not
2420 // rendering. We allow the checks above before returning to avoid a
2421 // timeout when capturing is stopped.
2422 return 0;
2423 }
2424
2425 RTC_DCHECK(_outStreamFormat.mBytesPerFrame != 0);
2426 UInt32 size =
2427 outputData->mBuffers->mDataByteSize / _outStreamFormat.mBytesPerFrame;
2428
2429 // TODO(xians): signal an error somehow?
2430 err = AudioConverterFillComplexBuffer(_renderConverter, outConverterProc,
2431 this, &size, outputData, NULL);
2432 if (err != noErr) {
2433 if (err == 1) {
2434 // This is our own error.
2435 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
2436 " Error in AudioConverterFillComplexBuffer()");
2437 return 1;
2438 } else {
2439 logCAMsg(kTraceError, kTraceAudioDevice, _id,
2440 "Error in AudioConverterFillComplexBuffer()", (const char*)&err);
2441 return 1;
2442 }
2443 }
2444
2445 PaRingBufferSize bufSizeSamples =
2446 PaUtil_GetRingBufferReadAvailable(_paRenderBuffer);
2447
2448 int32_t renderDelayUs =
2449 static_cast<int32_t>(1e-3 * (outputTimeNs - nowNs) + 0.5);
2450 renderDelayUs += static_cast<int32_t>(
2451 (1.0e6 * bufSizeSamples) / _outDesiredFormat.mChannelsPerFrame /
2452 _outDesiredFormat.mSampleRate +
2453 0.5);
2454
2455 AtomicSet32(&_renderDelayUs, renderDelayUs);
2456
2457 return 0;
2458 }
2459
implOutConverterProc(UInt32 * numberDataPackets,AudioBufferList * data)2460 OSStatus AudioDeviceMac::implOutConverterProc(UInt32* numberDataPackets,
2461 AudioBufferList* data) {
2462 RTC_DCHECK(data->mNumberBuffers == 1);
2463 PaRingBufferSize numSamples =
2464 *numberDataPackets * _outDesiredFormat.mChannelsPerFrame;
2465
2466 data->mBuffers->mNumberChannels = _outDesiredFormat.mChannelsPerFrame;
2467 // Always give the converter as much as it wants, zero padding as required.
2468 data->mBuffers->mDataByteSize =
2469 *numberDataPackets * _outDesiredFormat.mBytesPerPacket;
2470 data->mBuffers->mData = _renderConvertData;
2471 memset(_renderConvertData, 0, sizeof(_renderConvertData));
2472
2473 PaUtil_ReadRingBuffer(_paRenderBuffer, _renderConvertData, numSamples);
2474
2475 kern_return_t kernErr = semaphore_signal_all(_renderSemaphore);
2476 if (kernErr != KERN_SUCCESS) {
2477 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
2478 " semaphore_signal_all() error: %d", kernErr);
2479 return 1;
2480 }
2481
2482 return 0;
2483 }
2484
implInDeviceIOProc(const AudioBufferList * inputData,const AudioTimeStamp * inputTime)2485 OSStatus AudioDeviceMac::implInDeviceIOProc(const AudioBufferList* inputData,
2486 const AudioTimeStamp* inputTime) {
2487 OSStatus err = noErr;
2488 UInt64 inputTimeNs = AudioConvertHostTimeToNanos(inputTime->mHostTime);
2489 UInt64 nowNs = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
2490
2491 // Check if we should close down audio device
2492 // Double-checked locking optimization to remove locking overhead
2493 if (_doStopRec) {
2494 _critSect.Enter();
2495 if (_doStopRec) {
2496 // This will be signalled only when a shared device is not in use.
2497 WEBRTC_CA_LOG_ERR(AudioDeviceStop(_inputDeviceID, _inDeviceIOProcID));
2498 WEBRTC_CA_LOG_WARN(
2499 AudioDeviceDestroyIOProcID(_inputDeviceID, _inDeviceIOProcID));
2500 if (err == noErr) {
2501 WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
2502 " Recording device stopped");
2503 }
2504
2505 _doStopRec = false;
2506 _stopEventRec.Set();
2507 _critSect.Leave();
2508 return 0;
2509 }
2510 _critSect.Leave();
2511 }
2512
2513 if (!_recording) {
2514 // Allow above checks to avoid a timeout on stopping capture.
2515 return 0;
2516 }
2517
2518 PaRingBufferSize bufSizeSamples =
2519 PaUtil_GetRingBufferReadAvailable(_paCaptureBuffer);
2520
2521 int32_t captureDelayUs =
2522 static_cast<int32_t>(1e-3 * (nowNs - inputTimeNs) + 0.5);
2523 captureDelayUs += static_cast<int32_t>((1.0e6 * bufSizeSamples) /
2524 _inStreamFormat.mChannelsPerFrame /
2525 _inStreamFormat.mSampleRate +
2526 0.5);
2527
2528 AtomicSet32(&_captureDelayUs, captureDelayUs);
2529
2530 RTC_DCHECK(inputData->mNumberBuffers == 1);
2531 PaRingBufferSize numSamples = inputData->mBuffers->mDataByteSize *
2532 _inStreamFormat.mChannelsPerFrame /
2533 _inStreamFormat.mBytesPerPacket;
2534 PaUtil_WriteRingBuffer(_paCaptureBuffer, inputData->mBuffers->mData,
2535 numSamples);
2536
2537 kern_return_t kernErr = semaphore_signal_all(_captureSemaphore);
2538 if (kernErr != KERN_SUCCESS) {
2539 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
2540 " semaphore_signal_all() error: %d", kernErr);
2541 }
2542
2543 return err;
2544 }
2545
implInConverterProc(UInt32 * numberDataPackets,AudioBufferList * data)2546 OSStatus AudioDeviceMac::implInConverterProc(UInt32* numberDataPackets,
2547 AudioBufferList* data) {
2548 RTC_DCHECK(data->mNumberBuffers == 1);
2549 PaRingBufferSize numSamples =
2550 *numberDataPackets * _inStreamFormat.mChannelsPerFrame;
2551
2552 while (PaUtil_GetRingBufferReadAvailable(_paCaptureBuffer) < numSamples) {
2553 mach_timespec_t timeout;
2554 timeout.tv_sec = 0;
2555 timeout.tv_nsec = TIMER_PERIOD_MS;
2556
2557 kern_return_t kernErr = semaphore_timedwait(_captureSemaphore, timeout);
2558 if (kernErr == KERN_OPERATION_TIMED_OUT) {
2559 int32_t signal = AtomicGet32(&_captureDeviceIsAlive);
2560 if (signal == 0) {
2561 // The capture device is no longer alive; stop the worker thread.
2562 *numberDataPackets = 0;
2563 return 1;
2564 }
2565 } else if (kernErr != KERN_SUCCESS) {
2566 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
2567 " semaphore_wait() error: %d", kernErr);
2568 }
2569 }
2570
2571 // Pass the read pointer directly to the converter to avoid a memcpy.
2572 void* dummyPtr;
2573 PaRingBufferSize dummySize;
2574 PaUtil_GetRingBufferReadRegions(_paCaptureBuffer, numSamples,
2575 &data->mBuffers->mData, &numSamples,
2576 &dummyPtr, &dummySize);
2577 PaUtil_AdvanceRingBufferReadIndex(_paCaptureBuffer, numSamples);
2578
2579 data->mBuffers->mNumberChannels = _inStreamFormat.mChannelsPerFrame;
2580 *numberDataPackets = numSamples / _inStreamFormat.mChannelsPerFrame;
2581 data->mBuffers->mDataByteSize =
2582 *numberDataPackets * _inStreamFormat.mBytesPerPacket;
2583
2584 return 0;
2585 }
2586
RunRender(void * ptrThis)2587 bool AudioDeviceMac::RunRender(void* ptrThis) {
2588 return static_cast<AudioDeviceMac*>(ptrThis)->RenderWorkerThread();
2589 }
2590
RenderWorkerThread()2591 bool AudioDeviceMac::RenderWorkerThread() {
2592 PaRingBufferSize numSamples =
2593 ENGINE_PLAY_BUF_SIZE_IN_SAMPLES * _outDesiredFormat.mChannelsPerFrame;
2594 while (PaUtil_GetRingBufferWriteAvailable(_paRenderBuffer) -
2595 _renderDelayOffsetSamples <
2596 numSamples) {
2597 mach_timespec_t timeout;
2598 timeout.tv_sec = 0;
2599 timeout.tv_nsec = TIMER_PERIOD_MS;
2600
2601 kern_return_t kernErr = semaphore_timedwait(_renderSemaphore, timeout);
2602 if (kernErr == KERN_OPERATION_TIMED_OUT) {
2603 int32_t signal = AtomicGet32(&_renderDeviceIsAlive);
2604 if (signal == 0) {
2605 // The render device is no longer alive; stop the worker thread.
2606 return false;
2607 }
2608 } else if (kernErr != KERN_SUCCESS) {
2609 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
2610 " semaphore_timedwait() error: %d", kernErr);
2611 }
2612 }
2613
2614 int8_t playBuffer[4 * ENGINE_PLAY_BUF_SIZE_IN_SAMPLES];
2615
2616 if (!_ptrAudioBuffer) {
2617 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
2618 " capture AudioBuffer is invalid");
2619 return false;
2620 }
2621
2622 // Ask for new PCM data to be played out using the AudioDeviceBuffer.
2623 uint32_t nSamples =
2624 _ptrAudioBuffer->RequestPlayoutData(ENGINE_PLAY_BUF_SIZE_IN_SAMPLES);
2625
2626 nSamples = _ptrAudioBuffer->GetPlayoutData(playBuffer);
2627 if (nSamples != ENGINE_PLAY_BUF_SIZE_IN_SAMPLES) {
2628 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
2629 " invalid number of output samples(%d)", nSamples);
2630 }
2631
2632 uint32_t nOutSamples = nSamples * _outDesiredFormat.mChannelsPerFrame;
2633
2634 SInt16* pPlayBuffer = (SInt16*)&playBuffer;
2635 if (_macBookProPanRight && (_playChannels == 2)) {
2636 // Mix entirely into the right channel and zero the left channel.
2637 SInt32 sampleInt32 = 0;
2638 for (uint32_t sampleIdx = 0; sampleIdx < nOutSamples; sampleIdx += 2) {
2639 sampleInt32 = pPlayBuffer[sampleIdx];
2640 sampleInt32 += pPlayBuffer[sampleIdx + 1];
2641 sampleInt32 /= 2;
2642
2643 if (sampleInt32 > 32767) {
2644 sampleInt32 = 32767;
2645 } else if (sampleInt32 < -32768) {
2646 sampleInt32 = -32768;
2647 }
2648
2649 pPlayBuffer[sampleIdx] = 0;
2650 pPlayBuffer[sampleIdx + 1] = static_cast<SInt16>(sampleInt32);
2651 }
2652 }
2653
2654 PaUtil_WriteRingBuffer(_paRenderBuffer, pPlayBuffer, nOutSamples);
2655
2656 return true;
2657 }
2658
RunCapture(void * ptrThis)2659 bool AudioDeviceMac::RunCapture(void* ptrThis) {
2660 return static_cast<AudioDeviceMac*>(ptrThis)->CaptureWorkerThread();
2661 }
2662
CaptureWorkerThread()2663 bool AudioDeviceMac::CaptureWorkerThread() {
2664 OSStatus err = noErr;
2665 UInt32 noRecSamples =
2666 ENGINE_REC_BUF_SIZE_IN_SAMPLES * _inDesiredFormat.mChannelsPerFrame;
2667 SInt16 recordBuffer[noRecSamples];
2668 UInt32 size = ENGINE_REC_BUF_SIZE_IN_SAMPLES;
2669
2670 AudioBufferList engineBuffer;
2671 engineBuffer.mNumberBuffers = 1; // Interleaved channels.
2672 engineBuffer.mBuffers->mNumberChannels = _inDesiredFormat.mChannelsPerFrame;
2673 engineBuffer.mBuffers->mDataByteSize =
2674 _inDesiredFormat.mBytesPerPacket * noRecSamples;
2675 engineBuffer.mBuffers->mData = recordBuffer;
2676
2677 err = AudioConverterFillComplexBuffer(_captureConverter, inConverterProc,
2678 this, &size, &engineBuffer, NULL);
2679 if (err != noErr) {
2680 if (err == 1) {
2681 // This is our own error.
2682 return false;
2683 } else {
2684 logCAMsg(kTraceError, kTraceAudioDevice, _id,
2685 "Error in AudioConverterFillComplexBuffer()", (const char*)&err);
2686 return false;
2687 }
2688 }
2689
2690 // TODO(xians): what if the returned size is incorrect?
2691 if (size == ENGINE_REC_BUF_SIZE_IN_SAMPLES) {
2692 uint32_t currentMicLevel(0);
2693 uint32_t newMicLevel(0);
2694 int32_t msecOnPlaySide;
2695 int32_t msecOnRecordSide;
2696
2697 int32_t captureDelayUs = AtomicGet32(&_captureDelayUs);
2698 int32_t renderDelayUs = AtomicGet32(&_renderDelayUs);
2699
2700 msecOnPlaySide =
2701 static_cast<int32_t>(1e-3 * (renderDelayUs + _renderLatencyUs) + 0.5);
2702 msecOnRecordSide =
2703 static_cast<int32_t>(1e-3 * (captureDelayUs + _captureLatencyUs) + 0.5);
2704
2705 if (!_ptrAudioBuffer) {
2706 WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
2707 " capture AudioBuffer is invalid");
2708 return false;
2709 }
2710
2711 // store the recorded buffer (no action will be taken if the
2712 // #recorded samples is not a full buffer)
2713 _ptrAudioBuffer->SetRecordedBuffer((int8_t*)&recordBuffer, (uint32_t)size);
2714
2715 if (AGC()) {
2716 // Use mod to ensure we check the volume on the first pass.
2717 if (get_mic_volume_counter_ms_ % kGetMicVolumeIntervalMs == 0) {
2718 get_mic_volume_counter_ms_ = 0;
2719 // store current mic level in the audio buffer if AGC is enabled
2720 if (MicrophoneVolume(currentMicLevel) == 0) {
2721 // this call does not affect the actual microphone volume
2722 _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel);
2723 }
2724 }
2725 get_mic_volume_counter_ms_ += kBufferSizeMs;
2726 }
2727
2728 _ptrAudioBuffer->SetVQEData(msecOnPlaySide, msecOnRecordSide, 0);
2729
2730 _ptrAudioBuffer->SetTypingStatus(KeyPressed());
2731
2732 // deliver recorded samples at specified sample rate, mic level etc.
2733 // to the observer using callback
2734 _ptrAudioBuffer->DeliverRecordedData();
2735
2736 if (AGC()) {
2737 newMicLevel = _ptrAudioBuffer->NewMicLevel();
2738 if (newMicLevel != 0) {
2739 // The VQE will only deliver non-zero microphone levels when
2740 // a change is needed.
2741 // Set this new mic level (received from the observer as return
2742 // value in the callback).
2743 WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id,
2744 " AGC change of volume: old=%u => new=%u",
2745 currentMicLevel, newMicLevel);
2746 if (SetMicrophoneVolume(newMicLevel) == -1) {
2747 WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
2748 " the required modification of the microphone "
2749 "volume failed");
2750 }
2751 }
2752 }
2753 }
2754
2755 return true;
2756 }
2757
KeyPressed()2758 bool AudioDeviceMac::KeyPressed() {
2759 bool key_down = false;
2760 // Loop through all Mac virtual key constant values.
2761 for (unsigned int key_index = 0; key_index < arraysize(prev_key_state_);
2762 ++key_index) {
2763 bool keyState =
2764 CGEventSourceKeyState(kCGEventSourceStateHIDSystemState, key_index);
2765 // A false -> true change in keymap means a key is pressed.
2766 key_down |= (keyState && !prev_key_state_[key_index]);
2767 // Save current state.
2768 prev_key_state_[key_index] = keyState;
2769 }
2770 return key_down;
2771 }
2772 } // namespace webrtc
2773