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