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