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
28namespace 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
61enum
62{
63    MaxNumberDevices = 64
64};
65
66void 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
79int32_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.
92void 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
109AudioDeviceMac::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
175AudioDeviceMac::~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
245void 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
259int32_t AudioDeviceMac::ActiveAudioLayer(
260    AudioDeviceModule::AudioLayer& audioLayer) const
261{
262    audioLayer = AudioDeviceModule::kPlatformDefaultAudio;
263    return 0;
264}
265
266int32_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
429int32_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
482bool AudioDeviceMac::Initialized() const
483{
484    return (_initialized);
485}
486
487int32_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
515int32_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
546int32_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
574int32_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
605bool AudioDeviceMac::SpeakerIsInitialized() const
606{
607    return (_mixerManager.SpeakerIsInitialized());
608}
609
610bool AudioDeviceMac::MicrophoneIsInitialized() const
611{
612    return (_mixerManager.MicrophoneIsInitialized());
613}
614
615int32_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
645int32_t AudioDeviceMac::SetSpeakerVolume(uint32_t volume)
646{
647
648    return (_mixerManager.SetSpeakerVolume(volume));
649}
650
651int32_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
665int32_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
674int32_t
675AudioDeviceMac::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
684int32_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
698int32_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
712int32_t
713AudioDeviceMac::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
727int32_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
761int32_t AudioDeviceMac::SetSpeakerMute(bool enable)
762{
763    return (_mixerManager.SetSpeakerMute(enable));
764}
765
766int32_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
780int32_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
813int32_t AudioDeviceMac::SetMicrophoneMute(bool enable)
814{
815    return (_mixerManager.SetMicrophoneMute(enable));
816}
817
818int32_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
832int32_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
865int32_t AudioDeviceMac::SetMicrophoneBoost(bool enable)
866{
867
868    return (_mixerManager.SetMicrophoneBoost(enable));
869}
870
871int32_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
885int32_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
913int32_t AudioDeviceMac::SetStereoRecording(bool enable)
914{
915
916    if (enable)
917        _recChannels = 2;
918    else
919        _recChannels = 1;
920
921    return 0;
922}
923
924int32_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
935int32_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
963int32_t AudioDeviceMac::SetStereoPlayout(bool enable)
964{
965
966    if (enable)
967        _playChannels = 2;
968    else
969        _playChannels = 1;
970
971    return 0;
972}
973
974int32_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
985int32_t AudioDeviceMac::SetAGC(bool enable)
986{
987
988    _AGC = enable;
989
990    return 0;
991}
992
993bool AudioDeviceMac::AGC() const
994{
995
996    return _AGC;
997}
998
999int32_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
1030int32_t AudioDeviceMac::SetMicrophoneVolume(uint32_t volume)
1031{
1032
1033    return (_mixerManager.SetMicrophoneVolume(volume));
1034}
1035
1036int32_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
1052int32_t
1053AudioDeviceMac::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
1067int32_t
1068AudioDeviceMac::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
1082int32_t
1083AudioDeviceMac::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
1097int16_t AudioDeviceMac::PlayoutDevices()
1098{
1099
1100    AudioDeviceID playDevices[MaxNumberDevices];
1101    return GetNumberDevices(kAudioDevicePropertyScopeOutput, playDevices,
1102                            MaxNumberDevices);
1103}
1104
1105int32_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
1133int32_t AudioDeviceMac::SetPlayoutDevice(
1134    AudioDeviceModule::WindowsDeviceType /*device*/)
1135{
1136    WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1137                 "WindowsDeviceType not supported");
1138    return -1;
1139}
1140
1141int32_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
1164int32_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
1187int16_t AudioDeviceMac::RecordingDevices()
1188{
1189
1190    AudioDeviceID recDevices[MaxNumberDevices];
1191    return GetNumberDevices(kAudioDevicePropertyScopeInput, recDevices,
1192                            MaxNumberDevices);
1193}
1194
1195int32_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
1224int32_t
1225AudioDeviceMac::SetRecordingDevice(AudioDeviceModule::WindowsDeviceType /*device*/)
1226{
1227    WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
1228                 "WindowsDeviceType not supported");
1229    return -1;
1230}
1231
1232int32_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
1260int32_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
1288int32_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
1550int32_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
1775int32_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
1819int32_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
1927bool AudioDeviceMac::RecordingIsInitialized() const
1928{
1929    return (_recIsInitialized);
1930}
1931
1932bool AudioDeviceMac::Recording() const
1933{
1934    return (_recording);
1935}
1936
1937bool AudioDeviceMac::PlayoutIsInitialized() const
1938{
1939    return (_playIsInitialized);
1940}
1941
1942int32_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
1975int32_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
2065int32_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
2073int32_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
2081bool AudioDeviceMac::Playing() const
2082{
2083    return (_playing);
2084}
2085
2086int32_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
2103int32_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.
2115int32_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
2124bool AudioDeviceMac::PlayoutWarning() const
2125{
2126    return (_playWarning > 0);
2127}
2128
2129bool AudioDeviceMac::PlayoutError() const
2130{
2131    return (_playError > 0);
2132}
2133
2134bool AudioDeviceMac::RecordingWarning() const
2135{
2136    return (_recWarning > 0);
2137}
2138
2139bool AudioDeviceMac::RecordingError() const
2140{
2141    return (_recError > 0);
2142}
2143
2144void AudioDeviceMac::ClearPlayoutWarning()
2145{
2146    _playWarning = 0;
2147}
2148
2149void AudioDeviceMac::ClearPlayoutError()
2150{
2151    _playError = 0;
2152}
2153
2154void AudioDeviceMac::ClearRecordingWarning()
2155{
2156    _recWarning = 0;
2157}
2158
2159void AudioDeviceMac::ClearRecordingError()
2160{
2161    _recError = 0;
2162}
2163
2164// ============================================================================
2165//                                 Private Methods
2166// ============================================================================
2167
2168int32_t
2169AudioDeviceMac::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
2306int32_t
2307AudioDeviceMac::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
2386int32_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
2479OSStatus 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
2494OSStatus 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
2522int32_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
2594int32_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
2725int32_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}
2756int32_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
2786OSStatus 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
2802OSStatus 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
2814OSStatus 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
2829OSStatus 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
2842OSStatus 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
2932OSStatus 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
2959OSStatus 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
3025OSStatus 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
3071bool AudioDeviceMac::RunRender(void* ptrThis)
3072{
3073    return static_cast<AudioDeviceMac*> (ptrThis)->RenderWorkerThread();
3074}
3075
3076bool 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
3155bool AudioDeviceMac::RunCapture(void* ptrThis)
3156{
3157    return static_cast<AudioDeviceMac*> (ptrThis)->CaptureWorkerThread();
3158}
3159
3160bool 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
3264bool 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