| Index: webrtc/modules/audio_device/mac/audio_device_mac.cc
|
| diff --git a/webrtc/modules/audio_device/mac/audio_device_mac.cc b/webrtc/modules/audio_device/mac/audio_device_mac.cc
|
| index d963f2b7f2a65503bce08ff238c7df4b0ef57609..0f33d1124deea786b1407133942e8b36bf978ae7 100644
|
| --- a/webrtc/modules/audio_device/mac/audio_device_mac.cc
|
| +++ b/webrtc/modules/audio_device/mac/audio_device_mac.cc
|
| @@ -18,3227 +18,2750 @@
|
| #include "webrtc/system_wrappers/include/trace.h"
|
|
|
| #include <ApplicationServices/ApplicationServices.h>
|
| -#include <libkern/OSAtomic.h> // OSAtomicCompareAndSwap()
|
| -#include <mach/mach.h> // mach_task_self()
|
| -#include <sys/sysctl.h> // sysctlbyname()
|
| -
|
| -
|
| -
|
| -namespace webrtc
|
| -{
|
| -
|
| -#define WEBRTC_CA_RETURN_ON_ERR(expr) \
|
| - do { \
|
| - err = expr; \
|
| - if (err != noErr) { \
|
| - logCAMsg(kTraceError, kTraceAudioDevice, _id, \
|
| - "Error in " #expr, (const char *)&err); \
|
| - return -1; \
|
| - } \
|
| - } while(0)
|
| -
|
| -#define WEBRTC_CA_LOG_ERR(expr) \
|
| - do { \
|
| - err = expr; \
|
| - if (err != noErr) { \
|
| - logCAMsg(kTraceError, kTraceAudioDevice, _id, \
|
| - "Error in " #expr, (const char *)&err); \
|
| - } \
|
| - } while(0)
|
| -
|
| -#define WEBRTC_CA_LOG_WARN(expr) \
|
| - do { \
|
| - err = expr; \
|
| - if (err != noErr) { \
|
| - logCAMsg(kTraceWarning, kTraceAudioDevice, _id, \
|
| - "Error in " #expr, (const char *)&err); \
|
| - } \
|
| - } while(0)
|
| -
|
| -enum
|
| -{
|
| - MaxNumberDevices = 64
|
| -};
|
| -
|
| -void AudioDeviceMac::AtomicSet32(int32_t* theValue, int32_t newValue)
|
| -{
|
| - while (1)
|
| - {
|
| - int32_t oldValue = *theValue;
|
| - if (OSAtomicCompareAndSwap32Barrier(oldValue, newValue, theValue)
|
| - == true)
|
| - {
|
| - return;
|
| - }
|
| +#include <libkern/OSAtomic.h> // OSAtomicCompareAndSwap()
|
| +#include <mach/mach.h> // mach_task_self()
|
| +#include <sys/sysctl.h> // sysctlbyname()
|
| +
|
| +namespace webrtc {
|
| +
|
| +#define WEBRTC_CA_RETURN_ON_ERR(expr) \
|
| + do { \
|
| + err = expr; \
|
| + if (err != noErr) { \
|
| + logCAMsg(kTraceError, kTraceAudioDevice, _id, "Error in " #expr, \
|
| + (const char*) & err); \
|
| + return -1; \
|
| + } \
|
| + } while (0)
|
| +
|
| +#define WEBRTC_CA_LOG_ERR(expr) \
|
| + do { \
|
| + err = expr; \
|
| + if (err != noErr) { \
|
| + logCAMsg(kTraceError, kTraceAudioDevice, _id, "Error in " #expr, \
|
| + (const char*) & err); \
|
| + } \
|
| + } while (0)
|
| +
|
| +#define WEBRTC_CA_LOG_WARN(expr) \
|
| + do { \
|
| + err = expr; \
|
| + if (err != noErr) { \
|
| + logCAMsg(kTraceWarning, kTraceAudioDevice, _id, "Error in " #expr, \
|
| + (const char*) & err); \
|
| + } \
|
| + } while (0)
|
| +
|
| +enum { MaxNumberDevices = 64 };
|
| +
|
| +void AudioDeviceMac::AtomicSet32(int32_t* theValue, int32_t newValue) {
|
| + while (1) {
|
| + int32_t oldValue = *theValue;
|
| + if (OSAtomicCompareAndSwap32Barrier(oldValue, newValue, theValue) == true) {
|
| + return;
|
| }
|
| + }
|
| }
|
|
|
| -int32_t AudioDeviceMac::AtomicGet32(int32_t* theValue)
|
| -{
|
| - while (1)
|
| - {
|
| - int32_t value = *theValue;
|
| - if (OSAtomicCompareAndSwap32Barrier(value, value, theValue) == true)
|
| - {
|
| - return value;
|
| - }
|
| +int32_t AudioDeviceMac::AtomicGet32(int32_t* theValue) {
|
| + while (1) {
|
| + int32_t value = *theValue;
|
| + if (OSAtomicCompareAndSwap32Barrier(value, value, theValue) == true) {
|
| + return value;
|
| }
|
| + }
|
| }
|
|
|
| // CoreAudio errors are best interpreted as four character strings.
|
| void AudioDeviceMac::logCAMsg(const TraceLevel level,
|
| const TraceModule module,
|
| - const int32_t id, const char *msg,
|
| - const char *err)
|
| -{
|
| + const int32_t id,
|
| + const char* msg,
|
| + const char* err) {
|
| RTC_DCHECK(msg != NULL);
|
| RTC_DCHECK(err != NULL);
|
|
|
| #ifdef WEBRTC_ARCH_BIG_ENDIAN
|
| - WEBRTC_TRACE(level, module, id, "%s: %.4s", msg, err);
|
| + WEBRTC_TRACE(level, module, id, "%s: %.4s", msg, err);
|
| #else
|
| - // We need to flip the characters in this case.
|
| - WEBRTC_TRACE(level, module, id, "%s: %.1s%.1s%.1s%.1s", msg, err + 3, err
|
| - + 2, err + 1, err);
|
| + // We need to flip the characters in this case.
|
| + WEBRTC_TRACE(level, module, id, "%s: %.1s%.1s%.1s%.1s", msg, err + 3, err + 2,
|
| + err + 1, err);
|
| #endif
|
| }
|
|
|
| -AudioDeviceMac::AudioDeviceMac(const int32_t id) :
|
| - _ptrAudioBuffer(NULL),
|
| - _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
|
| - _stopEventRec(*EventWrapper::Create()),
|
| - _stopEvent(*EventWrapper::Create()),
|
| - _id(id),
|
| - _mixerManager(id),
|
| - _inputDeviceIndex(0),
|
| - _outputDeviceIndex(0),
|
| - _inputDeviceID(kAudioObjectUnknown),
|
| - _outputDeviceID(kAudioObjectUnknown),
|
| - _inputDeviceIsSpecified(false),
|
| - _outputDeviceIsSpecified(false),
|
| - _recChannels(N_REC_CHANNELS),
|
| - _playChannels(N_PLAY_CHANNELS),
|
| - _captureBufData(NULL),
|
| - _renderBufData(NULL),
|
| - _playBufType(AudioDeviceModule::kFixedBufferSize),
|
| - _initialized(false),
|
| - _isShutDown(false),
|
| - _recording(false),
|
| - _playing(false),
|
| - _recIsInitialized(false),
|
| - _playIsInitialized(false),
|
| - _AGC(false),
|
| - _renderDeviceIsAlive(1),
|
| - _captureDeviceIsAlive(1),
|
| - _twoDevices(true),
|
| - _doStop(false),
|
| - _doStopRec(false),
|
| - _macBookPro(false),
|
| - _macBookProPanRight(false),
|
| - _captureLatencyUs(0),
|
| - _renderLatencyUs(0),
|
| - _captureDelayUs(0),
|
| - _renderDelayUs(0),
|
| - _renderDelayOffsetSamples(0),
|
| - _playBufDelayFixed(20),
|
| - _playWarning(0),
|
| - _playError(0),
|
| - _recWarning(0),
|
| - _recError(0),
|
| - _paCaptureBuffer(NULL),
|
| - _paRenderBuffer(NULL),
|
| - _captureBufSizeSamples(0),
|
| - _renderBufSizeSamples(0),
|
| - prev_key_state_(),
|
| - get_mic_volume_counter_ms_(0)
|
| -{
|
| - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id,
|
| - "%s created", __FUNCTION__);
|
| -
|
| - RTC_DCHECK(&_stopEvent != NULL);
|
| - RTC_DCHECK(&_stopEventRec != NULL);
|
| -
|
| - memset(_renderConvertData, 0, sizeof(_renderConvertData));
|
| - memset(&_outStreamFormat, 0, sizeof(AudioStreamBasicDescription));
|
| - memset(&_outDesiredFormat, 0, sizeof(AudioStreamBasicDescription));
|
| - memset(&_inStreamFormat, 0, sizeof(AudioStreamBasicDescription));
|
| - memset(&_inDesiredFormat, 0, sizeof(AudioStreamBasicDescription));
|
| -}
|
| -
|
| -
|
| -AudioDeviceMac::~AudioDeviceMac()
|
| -{
|
| - WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id,
|
| - "%s destroyed", __FUNCTION__);
|
| -
|
| - if (!_isShutDown)
|
| - {
|
| - Terminate();
|
| - }
|
| +AudioDeviceMac::AudioDeviceMac(const int32_t id)
|
| + : _ptrAudioBuffer(NULL),
|
| + _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
|
| + _stopEventRec(*EventWrapper::Create()),
|
| + _stopEvent(*EventWrapper::Create()),
|
| + _id(id),
|
| + _mixerManager(id),
|
| + _inputDeviceIndex(0),
|
| + _outputDeviceIndex(0),
|
| + _inputDeviceID(kAudioObjectUnknown),
|
| + _outputDeviceID(kAudioObjectUnknown),
|
| + _inputDeviceIsSpecified(false),
|
| + _outputDeviceIsSpecified(false),
|
| + _recChannels(N_REC_CHANNELS),
|
| + _playChannels(N_PLAY_CHANNELS),
|
| + _captureBufData(NULL),
|
| + _renderBufData(NULL),
|
| + _playBufType(AudioDeviceModule::kFixedBufferSize),
|
| + _initialized(false),
|
| + _isShutDown(false),
|
| + _recording(false),
|
| + _playing(false),
|
| + _recIsInitialized(false),
|
| + _playIsInitialized(false),
|
| + _AGC(false),
|
| + _renderDeviceIsAlive(1),
|
| + _captureDeviceIsAlive(1),
|
| + _twoDevices(true),
|
| + _doStop(false),
|
| + _doStopRec(false),
|
| + _macBookPro(false),
|
| + _macBookProPanRight(false),
|
| + _captureLatencyUs(0),
|
| + _renderLatencyUs(0),
|
| + _captureDelayUs(0),
|
| + _renderDelayUs(0),
|
| + _renderDelayOffsetSamples(0),
|
| + _playBufDelayFixed(20),
|
| + _playWarning(0),
|
| + _playError(0),
|
| + _recWarning(0),
|
| + _recError(0),
|
| + _paCaptureBuffer(NULL),
|
| + _paRenderBuffer(NULL),
|
| + _captureBufSizeSamples(0),
|
| + _renderBufSizeSamples(0),
|
| + prev_key_state_(),
|
| + get_mic_volume_counter_ms_(0) {
|
| + WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, id, "%s created", __FUNCTION__);
|
| +
|
| + RTC_DCHECK(&_stopEvent != NULL);
|
| + RTC_DCHECK(&_stopEventRec != NULL);
|
| +
|
| + memset(_renderConvertData, 0, sizeof(_renderConvertData));
|
| + memset(&_outStreamFormat, 0, sizeof(AudioStreamBasicDescription));
|
| + memset(&_outDesiredFormat, 0, sizeof(AudioStreamBasicDescription));
|
| + memset(&_inStreamFormat, 0, sizeof(AudioStreamBasicDescription));
|
| + memset(&_inDesiredFormat, 0, sizeof(AudioStreamBasicDescription));
|
| +}
|
| +
|
| +AudioDeviceMac::~AudioDeviceMac() {
|
| + WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destroyed",
|
| + __FUNCTION__);
|
| +
|
| + if (!_isShutDown) {
|
| + Terminate();
|
| + }
|
|
|
| - RTC_DCHECK(!capture_worker_thread_.get());
|
| - RTC_DCHECK(!render_worker_thread_.get());
|
| + RTC_DCHECK(!capture_worker_thread_.get());
|
| + RTC_DCHECK(!render_worker_thread_.get());
|
|
|
| - if (_paRenderBuffer)
|
| - {
|
| - delete _paRenderBuffer;
|
| - _paRenderBuffer = NULL;
|
| - }
|
| + if (_paRenderBuffer) {
|
| + delete _paRenderBuffer;
|
| + _paRenderBuffer = NULL;
|
| + }
|
|
|
| - if (_paCaptureBuffer)
|
| - {
|
| - delete _paCaptureBuffer;
|
| - _paCaptureBuffer = NULL;
|
| - }
|
| + if (_paCaptureBuffer) {
|
| + delete _paCaptureBuffer;
|
| + _paCaptureBuffer = NULL;
|
| + }
|
|
|
| - if (_renderBufData)
|
| - {
|
| - delete[] _renderBufData;
|
| - _renderBufData = NULL;
|
| - }
|
| + if (_renderBufData) {
|
| + delete[] _renderBufData;
|
| + _renderBufData = NULL;
|
| + }
|
|
|
| - if (_captureBufData)
|
| - {
|
| - delete[] _captureBufData;
|
| - _captureBufData = NULL;
|
| - }
|
| + if (_captureBufData) {
|
| + delete[] _captureBufData;
|
| + _captureBufData = NULL;
|
| + }
|
|
|
| - kern_return_t kernErr = KERN_SUCCESS;
|
| - kernErr = semaphore_destroy(mach_task_self(), _renderSemaphore);
|
| - if (kernErr != KERN_SUCCESS)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " semaphore_destroy() error: %d", kernErr);
|
| - }
|
| + kern_return_t kernErr = KERN_SUCCESS;
|
| + kernErr = semaphore_destroy(mach_task_self(), _renderSemaphore);
|
| + if (kernErr != KERN_SUCCESS) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " semaphore_destroy() error: %d", kernErr);
|
| + }
|
|
|
| - kernErr = semaphore_destroy(mach_task_self(), _captureSemaphore);
|
| - if (kernErr != KERN_SUCCESS)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " semaphore_destroy() error: %d", kernErr);
|
| - }
|
| + kernErr = semaphore_destroy(mach_task_self(), _captureSemaphore);
|
| + if (kernErr != KERN_SUCCESS) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " semaphore_destroy() error: %d", kernErr);
|
| + }
|
|
|
| - delete &_stopEvent;
|
| - delete &_stopEventRec;
|
| - delete &_critSect;
|
| + delete &_stopEvent;
|
| + delete &_stopEventRec;
|
| + delete &_critSect;
|
| }
|
|
|
| // ============================================================================
|
| // API
|
| // ============================================================================
|
|
|
| -void AudioDeviceMac::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer)
|
| -{
|
| -
|
| - CriticalSectionScoped lock(&_critSect);
|
| +void AudioDeviceMac::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
|
| + CriticalSectionScoped lock(&_critSect);
|
|
|
| - _ptrAudioBuffer = audioBuffer;
|
| + _ptrAudioBuffer = audioBuffer;
|
|
|
| - // inform the AudioBuffer about default settings for this implementation
|
| - _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC);
|
| - _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC);
|
| - _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS);
|
| - _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS);
|
| + // inform the AudioBuffer about default settings for this implementation
|
| + _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC);
|
| + _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC);
|
| + _ptrAudioBuffer->SetRecordingChannels(N_REC_CHANNELS);
|
| + _ptrAudioBuffer->SetPlayoutChannels(N_PLAY_CHANNELS);
|
| }
|
|
|
| int32_t AudioDeviceMac::ActiveAudioLayer(
|
| - AudioDeviceModule::AudioLayer& audioLayer) const
|
| -{
|
| - audioLayer = AudioDeviceModule::kPlatformDefaultAudio;
|
| - return 0;
|
| + AudioDeviceModule::AudioLayer& audioLayer) const {
|
| + audioLayer = AudioDeviceModule::kPlatformDefaultAudio;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::Init()
|
| -{
|
| +int32_t AudioDeviceMac::Init() {
|
| + CriticalSectionScoped lock(&_critSect);
|
|
|
| - CriticalSectionScoped lock(&_critSect);
|
| + if (_initialized) {
|
| + return 0;
|
| + }
|
|
|
| - if (_initialized)
|
| - {
|
| - return 0;
|
| - }
|
| + OSStatus err = noErr;
|
|
|
| - OSStatus err = noErr;
|
| + _isShutDown = false;
|
|
|
| - _isShutDown = false;
|
| -
|
| - // PortAudio ring buffers require an elementCount which is a power of two.
|
| - if (_renderBufData == NULL)
|
| - {
|
| - UInt32 powerOfTwo = 1;
|
| - while (powerOfTwo < PLAY_BUF_SIZE_IN_SAMPLES)
|
| - {
|
| - powerOfTwo <<= 1;
|
| - }
|
| - _renderBufSizeSamples = powerOfTwo;
|
| - _renderBufData = new SInt16[_renderBufSizeSamples];
|
| + // PortAudio ring buffers require an elementCount which is a power of two.
|
| + if (_renderBufData == NULL) {
|
| + UInt32 powerOfTwo = 1;
|
| + while (powerOfTwo < PLAY_BUF_SIZE_IN_SAMPLES) {
|
| + powerOfTwo <<= 1;
|
| }
|
| + _renderBufSizeSamples = powerOfTwo;
|
| + _renderBufData = new SInt16[_renderBufSizeSamples];
|
| + }
|
|
|
| - if (_paRenderBuffer == NULL)
|
| - {
|
| - _paRenderBuffer = new PaUtilRingBuffer;
|
| - PaRingBufferSize bufSize = -1;
|
| - bufSize = PaUtil_InitializeRingBuffer(_paRenderBuffer, sizeof(SInt16),
|
| - _renderBufSizeSamples,
|
| - _renderBufData);
|
| - if (bufSize == -1)
|
| - {
|
| - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice,
|
| - _id, " PaUtil_InitializeRingBuffer() error");
|
| - return -1;
|
| - }
|
| + if (_paRenderBuffer == NULL) {
|
| + _paRenderBuffer = new PaUtilRingBuffer;
|
| + PaRingBufferSize bufSize = -1;
|
| + bufSize = PaUtil_InitializeRingBuffer(
|
| + _paRenderBuffer, sizeof(SInt16), _renderBufSizeSamples, _renderBufData);
|
| + if (bufSize == -1) {
|
| + WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
|
| + " PaUtil_InitializeRingBuffer() error");
|
| + return -1;
|
| }
|
| + }
|
|
|
| - if (_captureBufData == NULL)
|
| - {
|
| - UInt32 powerOfTwo = 1;
|
| - while (powerOfTwo < REC_BUF_SIZE_IN_SAMPLES)
|
| - {
|
| - powerOfTwo <<= 1;
|
| - }
|
| - _captureBufSizeSamples = powerOfTwo;
|
| - _captureBufData = new Float32[_captureBufSizeSamples];
|
| + if (_captureBufData == NULL) {
|
| + UInt32 powerOfTwo = 1;
|
| + while (powerOfTwo < REC_BUF_SIZE_IN_SAMPLES) {
|
| + powerOfTwo <<= 1;
|
| }
|
| + _captureBufSizeSamples = powerOfTwo;
|
| + _captureBufData = new Float32[_captureBufSizeSamples];
|
| + }
|
|
|
| - if (_paCaptureBuffer == NULL)
|
| - {
|
| - _paCaptureBuffer = new PaUtilRingBuffer;
|
| - PaRingBufferSize bufSize = -1;
|
| - bufSize = PaUtil_InitializeRingBuffer(_paCaptureBuffer,
|
| - sizeof(Float32),
|
| - _captureBufSizeSamples,
|
| - _captureBufData);
|
| - if (bufSize == -1)
|
| - {
|
| - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice,
|
| - _id, " PaUtil_InitializeRingBuffer() error");
|
| - return -1;
|
| - }
|
| + if (_paCaptureBuffer == NULL) {
|
| + _paCaptureBuffer = new PaUtilRingBuffer;
|
| + PaRingBufferSize bufSize = -1;
|
| + bufSize =
|
| + PaUtil_InitializeRingBuffer(_paCaptureBuffer, sizeof(Float32),
|
| + _captureBufSizeSamples, _captureBufData);
|
| + if (bufSize == -1) {
|
| + WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
|
| + " PaUtil_InitializeRingBuffer() error");
|
| + return -1;
|
| }
|
| + }
|
|
|
| - kern_return_t kernErr = KERN_SUCCESS;
|
| - kernErr = semaphore_create(mach_task_self(), &_renderSemaphore,
|
| - SYNC_POLICY_FIFO, 0);
|
| - if (kernErr != KERN_SUCCESS)
|
| - {
|
| - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
|
| - " semaphore_create() error: %d", kernErr);
|
| - return -1;
|
| - }
|
| + kern_return_t kernErr = KERN_SUCCESS;
|
| + kernErr = semaphore_create(mach_task_self(), &_renderSemaphore,
|
| + SYNC_POLICY_FIFO, 0);
|
| + if (kernErr != KERN_SUCCESS) {
|
| + WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
|
| + " semaphore_create() error: %d", kernErr);
|
| + return -1;
|
| + }
|
|
|
| - kernErr = semaphore_create(mach_task_self(), &_captureSemaphore,
|
| - SYNC_POLICY_FIFO, 0);
|
| - if (kernErr != KERN_SUCCESS)
|
| - {
|
| - WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
|
| - " semaphore_create() error: %d", kernErr);
|
| - return -1;
|
| - }
|
| + kernErr = semaphore_create(mach_task_self(), &_captureSemaphore,
|
| + SYNC_POLICY_FIFO, 0);
|
| + if (kernErr != KERN_SUCCESS) {
|
| + WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
|
| + " semaphore_create() error: %d", kernErr);
|
| + return -1;
|
| + }
|
|
|
| - // Setting RunLoop to NULL here instructs HAL to manage its own thread for
|
| - // notifications. This was the default behaviour on OS X 10.5 and earlier,
|
| - // but now must be explicitly specified. HAL would otherwise try to use the
|
| - // main thread to issue notifications.
|
| - AudioObjectPropertyAddress propertyAddress = {
|
| - kAudioHardwarePropertyRunLoop,
|
| - kAudioObjectPropertyScopeGlobal,
|
| - kAudioObjectPropertyElementMaster };
|
| - CFRunLoopRef runLoop = NULL;
|
| - UInt32 size = sizeof(CFRunLoopRef);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(kAudioObjectSystemObject,
|
| - &propertyAddress, 0, NULL, size, &runLoop));
|
| -
|
| - // Listen for any device changes.
|
| - propertyAddress.mSelector = kAudioHardwarePropertyDevices;
|
| - WEBRTC_CA_LOG_ERR(AudioObjectAddPropertyListener(kAudioObjectSystemObject,
|
| - &propertyAddress, &objectListenerProc, this));
|
| -
|
| - // Determine if this is a MacBook Pro
|
| - _macBookPro = false;
|
| - _macBookProPanRight = false;
|
| - char buf[128];
|
| - size_t length = sizeof(buf);
|
| - memset(buf, 0, length);
|
| -
|
| - int intErr = sysctlbyname("hw.model", buf, &length, NULL, 0);
|
| - if (intErr != 0)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " Error in sysctlbyname(): %d", err);
|
| - } else
|
| - {
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - " Hardware model: %s", buf);
|
| - if (strncmp(buf, "MacBookPro", 10) == 0)
|
| - {
|
| - _macBookPro = true;
|
| - }
|
| + // Setting RunLoop to NULL here instructs HAL to manage its own thread for
|
| + // notifications. This was the default behaviour on OS X 10.5 and earlier,
|
| + // but now must be explicitly specified. HAL would otherwise try to use the
|
| + // main thread to issue notifications.
|
| + AudioObjectPropertyAddress propertyAddress = {
|
| + kAudioHardwarePropertyRunLoop, kAudioObjectPropertyScopeGlobal,
|
| + kAudioObjectPropertyElementMaster};
|
| + CFRunLoopRef runLoop = NULL;
|
| + UInt32 size = sizeof(CFRunLoopRef);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
|
| + kAudioObjectSystemObject, &propertyAddress, 0, NULL, size, &runLoop));
|
| +
|
| + // Listen for any device changes.
|
| + propertyAddress.mSelector = kAudioHardwarePropertyDevices;
|
| + WEBRTC_CA_LOG_ERR(AudioObjectAddPropertyListener(
|
| + kAudioObjectSystemObject, &propertyAddress, &objectListenerProc, this));
|
| +
|
| + // Determine if this is a MacBook Pro
|
| + _macBookPro = false;
|
| + _macBookProPanRight = false;
|
| + char buf[128];
|
| + size_t length = sizeof(buf);
|
| + memset(buf, 0, length);
|
| +
|
| + int intErr = sysctlbyname("hw.model", buf, &length, NULL, 0);
|
| + if (intErr != 0) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " Error in sysctlbyname(): %d", err);
|
| + } else {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " Hardware model: %s",
|
| + buf);
|
| + if (strncmp(buf, "MacBookPro", 10) == 0) {
|
| + _macBookPro = true;
|
| }
|
| + }
|
|
|
| - _playWarning = 0;
|
| - _playError = 0;
|
| - _recWarning = 0;
|
| - _recError = 0;
|
| + _playWarning = 0;
|
| + _playError = 0;
|
| + _recWarning = 0;
|
| + _recError = 0;
|
|
|
| - get_mic_volume_counter_ms_ = 0;
|
| + get_mic_volume_counter_ms_ = 0;
|
|
|
| - _initialized = true;
|
| + _initialized = true;
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::Terminate()
|
| -{
|
| +int32_t AudioDeviceMac::Terminate() {
|
| + if (!_initialized) {
|
| + return 0;
|
| + }
|
|
|
| - if (!_initialized)
|
| - {
|
| - return 0;
|
| - }
|
| + if (_recording) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " Recording must be stopped");
|
| + return -1;
|
| + }
|
|
|
| - if (_recording)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " Recording must be stopped");
|
| - return -1;
|
| - }
|
| + if (_playing) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " Playback must be stopped");
|
| + return -1;
|
| + }
|
|
|
| - if (_playing)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " Playback must be stopped");
|
| - return -1;
|
| - }
|
| + _critSect.Enter();
|
|
|
| - _critSect.Enter();
|
| + _mixerManager.Close();
|
|
|
| - _mixerManager.Close();
|
| + OSStatus err = noErr;
|
| + int retVal = 0;
|
|
|
| - OSStatus err = noErr;
|
| - int retVal = 0;
|
| + AudioObjectPropertyAddress propertyAddress = {
|
| + kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
|
| + kAudioObjectPropertyElementMaster};
|
| + WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
|
| + kAudioObjectSystemObject, &propertyAddress, &objectListenerProc, this));
|
|
|
| - AudioObjectPropertyAddress propertyAddress = {
|
| - kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
|
| - kAudioObjectPropertyElementMaster };
|
| - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(kAudioObjectSystemObject,
|
| - &propertyAddress, &objectListenerProc, this));
|
| -
|
| - err = AudioHardwareUnload();
|
| - if (err != noErr)
|
| - {
|
| - logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| - "Error in AudioHardwareUnload()", (const char*) &err);
|
| - retVal = -1;
|
| - }
|
| + err = AudioHardwareUnload();
|
| + if (err != noErr) {
|
| + logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| + "Error in AudioHardwareUnload()", (const char*)&err);
|
| + retVal = -1;
|
| + }
|
|
|
| - _isShutDown = true;
|
| - _initialized = false;
|
| - _outputDeviceIsSpecified = false;
|
| - _inputDeviceIsSpecified = false;
|
| + _isShutDown = true;
|
| + _initialized = false;
|
| + _outputDeviceIsSpecified = false;
|
| + _inputDeviceIsSpecified = false;
|
|
|
| - _critSect.Leave();
|
| + _critSect.Leave();
|
|
|
| - return retVal;
|
| + return retVal;
|
| }
|
|
|
| -bool AudioDeviceMac::Initialized() const
|
| -{
|
| - return (_initialized);
|
| +bool AudioDeviceMac::Initialized() const {
|
| + return (_initialized);
|
| }
|
|
|
| -int32_t AudioDeviceMac::SpeakerIsAvailable(bool& available)
|
| -{
|
| -
|
| - bool wasInitialized = _mixerManager.SpeakerIsInitialized();
|
| +int32_t AudioDeviceMac::SpeakerIsAvailable(bool& available) {
|
| + bool wasInitialized = _mixerManager.SpeakerIsInitialized();
|
|
|
| - // Make an attempt to open up the
|
| - // output mixer corresponding to the currently selected output device.
|
| - //
|
| - if (!wasInitialized && InitSpeaker() == -1)
|
| - {
|
| - available = false;
|
| - return 0;
|
| - }
|
| + // Make an attempt to open up the
|
| + // output mixer corresponding to the currently selected output device.
|
| + //
|
| + if (!wasInitialized && InitSpeaker() == -1) {
|
| + available = false;
|
| + return 0;
|
| + }
|
|
|
| - // Given that InitSpeaker was successful, we know that a valid speaker
|
| - // exists.
|
| - available = true;
|
| + // Given that InitSpeaker was successful, we know that a valid speaker
|
| + // exists.
|
| + available = true;
|
|
|
| - // Close the initialized output mixer
|
| - //
|
| - if (!wasInitialized)
|
| - {
|
| - _mixerManager.CloseSpeaker();
|
| - }
|
| + // Close the initialized output mixer
|
| + //
|
| + if (!wasInitialized) {
|
| + _mixerManager.CloseSpeaker();
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::InitSpeaker()
|
| -{
|
| +int32_t AudioDeviceMac::InitSpeaker() {
|
| + CriticalSectionScoped lock(&_critSect);
|
|
|
| - CriticalSectionScoped lock(&_critSect);
|
| -
|
| - if (_playing)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_playing) {
|
| + return -1;
|
| + }
|
|
|
| - if (InitDevice(_outputDeviceIndex, _outputDeviceID, false) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (InitDevice(_outputDeviceIndex, _outputDeviceID, false) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - if (_inputDeviceID == _outputDeviceID)
|
| - {
|
| - _twoDevices = false;
|
| - } else
|
| - {
|
| - _twoDevices = true;
|
| - }
|
| + if (_inputDeviceID == _outputDeviceID) {
|
| + _twoDevices = false;
|
| + } else {
|
| + _twoDevices = true;
|
| + }
|
|
|
| - if (_mixerManager.OpenSpeaker(_outputDeviceID) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.OpenSpeaker(_outputDeviceID) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::MicrophoneIsAvailable(bool& available)
|
| -{
|
| +int32_t AudioDeviceMac::MicrophoneIsAvailable(bool& available) {
|
| + bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
|
|
|
| - bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
|
| -
|
| - // Make an attempt to open up the
|
| - // input mixer corresponding to the currently selected output device.
|
| - //
|
| - if (!wasInitialized && InitMicrophone() == -1)
|
| - {
|
| - available = false;
|
| - return 0;
|
| - }
|
| + // Make an attempt to open up the
|
| + // input mixer corresponding to the currently selected output device.
|
| + //
|
| + if (!wasInitialized && InitMicrophone() == -1) {
|
| + available = false;
|
| + return 0;
|
| + }
|
|
|
| - // Given that InitMicrophone was successful, we know that a valid microphone
|
| - // exists.
|
| - available = true;
|
| + // Given that InitMicrophone was successful, we know that a valid microphone
|
| + // exists.
|
| + available = true;
|
|
|
| - // Close the initialized input mixer
|
| - //
|
| - if (!wasInitialized)
|
| - {
|
| - _mixerManager.CloseMicrophone();
|
| - }
|
| + // Close the initialized input mixer
|
| + //
|
| + if (!wasInitialized) {
|
| + _mixerManager.CloseMicrophone();
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::InitMicrophone()
|
| -{
|
| +int32_t AudioDeviceMac::InitMicrophone() {
|
| + CriticalSectionScoped lock(&_critSect);
|
|
|
| - CriticalSectionScoped lock(&_critSect);
|
| -
|
| - if (_recording)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_recording) {
|
| + return -1;
|
| + }
|
|
|
| - if (InitDevice(_inputDeviceIndex, _inputDeviceID, true) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (InitDevice(_inputDeviceIndex, _inputDeviceID, true) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - if (_inputDeviceID == _outputDeviceID)
|
| - {
|
| - _twoDevices = false;
|
| - } else
|
| - {
|
| - _twoDevices = true;
|
| - }
|
| + if (_inputDeviceID == _outputDeviceID) {
|
| + _twoDevices = false;
|
| + } else {
|
| + _twoDevices = true;
|
| + }
|
|
|
| - if (_mixerManager.OpenMicrophone(_inputDeviceID) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.OpenMicrophone(_inputDeviceID) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -bool AudioDeviceMac::SpeakerIsInitialized() const
|
| -{
|
| - return (_mixerManager.SpeakerIsInitialized());
|
| +bool AudioDeviceMac::SpeakerIsInitialized() const {
|
| + return (_mixerManager.SpeakerIsInitialized());
|
| }
|
|
|
| -bool AudioDeviceMac::MicrophoneIsInitialized() const
|
| -{
|
| - return (_mixerManager.MicrophoneIsInitialized());
|
| +bool AudioDeviceMac::MicrophoneIsInitialized() const {
|
| + return (_mixerManager.MicrophoneIsInitialized());
|
| }
|
|
|
| -int32_t AudioDeviceMac::SpeakerVolumeIsAvailable(bool& available)
|
| -{
|
| +int32_t AudioDeviceMac::SpeakerVolumeIsAvailable(bool& available) {
|
| + bool wasInitialized = _mixerManager.SpeakerIsInitialized();
|
|
|
| - bool wasInitialized = _mixerManager.SpeakerIsInitialized();
|
| -
|
| - // Make an attempt to open up the
|
| - // output mixer corresponding to the currently selected output device.
|
| - //
|
| - if (!wasInitialized && InitSpeaker() == -1)
|
| - {
|
| - // If we end up here it means that the selected speaker has no volume
|
| - // control.
|
| - available = false;
|
| - return 0;
|
| - }
|
| + // Make an attempt to open up the
|
| + // output mixer corresponding to the currently selected output device.
|
| + //
|
| + if (!wasInitialized && InitSpeaker() == -1) {
|
| + // If we end up here it means that the selected speaker has no volume
|
| + // control.
|
| + available = false;
|
| + return 0;
|
| + }
|
|
|
| - // Given that InitSpeaker was successful, we know that a volume control exists
|
| - //
|
| - available = true;
|
| + // Given that InitSpeaker was successful, we know that a volume control exists
|
| + //
|
| + available = true;
|
|
|
| - // Close the initialized output mixer
|
| - //
|
| - if (!wasInitialized)
|
| - {
|
| - _mixerManager.CloseSpeaker();
|
| - }
|
| + // Close the initialized output mixer
|
| + //
|
| + if (!wasInitialized) {
|
| + _mixerManager.CloseSpeaker();
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::SetSpeakerVolume(uint32_t volume)
|
| -{
|
| -
|
| - return (_mixerManager.SetSpeakerVolume(volume));
|
| +int32_t AudioDeviceMac::SetSpeakerVolume(uint32_t volume) {
|
| + return (_mixerManager.SetSpeakerVolume(volume));
|
| }
|
|
|
| -int32_t AudioDeviceMac::SpeakerVolume(uint32_t& volume) const
|
| -{
|
| -
|
| - uint32_t level(0);
|
| +int32_t AudioDeviceMac::SpeakerVolume(uint32_t& volume) const {
|
| + uint32_t level(0);
|
|
|
| - if (_mixerManager.SpeakerVolume(level) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.SpeakerVolume(level) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - volume = level;
|
| - return 0;
|
| + volume = level;
|
| + return 0;
|
| }
|
|
|
| int32_t AudioDeviceMac::SetWaveOutVolume(uint16_t volumeLeft,
|
| - uint16_t volumeRight)
|
| -{
|
| -
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " API call not supported on this platform");
|
| - return -1;
|
| + uint16_t volumeRight) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " API call not supported on this platform");
|
| + return -1;
|
| }
|
|
|
| -int32_t
|
| -AudioDeviceMac::WaveOutVolume(uint16_t& /*volumeLeft*/,
|
| - uint16_t& /*volumeRight*/) const
|
| -{
|
| -
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " API call not supported on this platform");
|
| - return -1;
|
| +int32_t AudioDeviceMac::WaveOutVolume(uint16_t& /*volumeLeft*/,
|
| + uint16_t& /*volumeRight*/) const {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " API call not supported on this platform");
|
| + return -1;
|
| }
|
|
|
| -int32_t AudioDeviceMac::MaxSpeakerVolume(uint32_t& maxVolume) const
|
| -{
|
| -
|
| - uint32_t maxVol(0);
|
| +int32_t AudioDeviceMac::MaxSpeakerVolume(uint32_t& maxVolume) const {
|
| + uint32_t maxVol(0);
|
|
|
| - if (_mixerManager.MaxSpeakerVolume(maxVol) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.MaxSpeakerVolume(maxVol) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - maxVolume = maxVol;
|
| - return 0;
|
| + maxVolume = maxVol;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::MinSpeakerVolume(uint32_t& minVolume) const
|
| -{
|
| -
|
| - uint32_t minVol(0);
|
| +int32_t AudioDeviceMac::MinSpeakerVolume(uint32_t& minVolume) const {
|
| + uint32_t minVol(0);
|
|
|
| - if (_mixerManager.MinSpeakerVolume(minVol) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.MinSpeakerVolume(minVol) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - minVolume = minVol;
|
| - return 0;
|
| + minVolume = minVol;
|
| + return 0;
|
| }
|
|
|
| -int32_t
|
| -AudioDeviceMac::SpeakerVolumeStepSize(uint16_t& stepSize) const
|
| -{
|
| -
|
| - uint16_t delta(0);
|
| +int32_t AudioDeviceMac::SpeakerVolumeStepSize(uint16_t& stepSize) const {
|
| + uint16_t delta(0);
|
|
|
| - if (_mixerManager.SpeakerVolumeStepSize(delta) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.SpeakerVolumeStepSize(delta) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - stepSize = delta;
|
| - return 0;
|
| + stepSize = delta;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::SpeakerMuteIsAvailable(bool& available)
|
| -{
|
| +int32_t AudioDeviceMac::SpeakerMuteIsAvailable(bool& available) {
|
| + bool isAvailable(false);
|
| + bool wasInitialized = _mixerManager.SpeakerIsInitialized();
|
|
|
| - bool isAvailable(false);
|
| - bool wasInitialized = _mixerManager.SpeakerIsInitialized();
|
| -
|
| - // Make an attempt to open up the
|
| - // output mixer corresponding to the currently selected output device.
|
| - //
|
| - if (!wasInitialized && InitSpeaker() == -1)
|
| - {
|
| - // If we end up here it means that the selected speaker has no volume
|
| - // control, hence it is safe to state that there is no mute control
|
| - // already at this stage.
|
| - available = false;
|
| - return 0;
|
| - }
|
| + // Make an attempt to open up the
|
| + // output mixer corresponding to the currently selected output device.
|
| + //
|
| + if (!wasInitialized && InitSpeaker() == -1) {
|
| + // If we end up here it means that the selected speaker has no volume
|
| + // control, hence it is safe to state that there is no mute control
|
| + // already at this stage.
|
| + available = false;
|
| + return 0;
|
| + }
|
|
|
| - // Check if the selected speaker has a mute control
|
| - //
|
| - _mixerManager.SpeakerMuteIsAvailable(isAvailable);
|
| + // Check if the selected speaker has a mute control
|
| + //
|
| + _mixerManager.SpeakerMuteIsAvailable(isAvailable);
|
|
|
| - available = isAvailable;
|
| + available = isAvailable;
|
|
|
| - // Close the initialized output mixer
|
| - //
|
| - if (!wasInitialized)
|
| - {
|
| - _mixerManager.CloseSpeaker();
|
| - }
|
| + // Close the initialized output mixer
|
| + //
|
| + if (!wasInitialized) {
|
| + _mixerManager.CloseSpeaker();
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::SetSpeakerMute(bool enable)
|
| -{
|
| - return (_mixerManager.SetSpeakerMute(enable));
|
| +int32_t AudioDeviceMac::SetSpeakerMute(bool enable) {
|
| + return (_mixerManager.SetSpeakerMute(enable));
|
| }
|
|
|
| -int32_t AudioDeviceMac::SpeakerMute(bool& enabled) const
|
| -{
|
| +int32_t AudioDeviceMac::SpeakerMute(bool& enabled) const {
|
| + bool muted(0);
|
|
|
| - bool muted(0);
|
| -
|
| - if (_mixerManager.SpeakerMute(muted) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.SpeakerMute(muted) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - enabled = muted;
|
| - return 0;
|
| + enabled = muted;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::MicrophoneMuteIsAvailable(bool& available)
|
| -{
|
| -
|
| - bool isAvailable(false);
|
| - bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
|
| +int32_t AudioDeviceMac::MicrophoneMuteIsAvailable(bool& available) {
|
| + bool isAvailable(false);
|
| + bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
|
|
|
| - // Make an attempt to open up the
|
| - // input mixer corresponding to the currently selected input device.
|
| - //
|
| - if (!wasInitialized && InitMicrophone() == -1)
|
| - {
|
| - // If we end up here it means that the selected microphone has no volume
|
| - // control, hence it is safe to state that there is no boost control
|
| - // already at this stage.
|
| - available = false;
|
| - return 0;
|
| - }
|
| + // Make an attempt to open up the
|
| + // input mixer corresponding to the currently selected input device.
|
| + //
|
| + if (!wasInitialized && InitMicrophone() == -1) {
|
| + // If we end up here it means that the selected microphone has no volume
|
| + // control, hence it is safe to state that there is no boost control
|
| + // already at this stage.
|
| + available = false;
|
| + return 0;
|
| + }
|
|
|
| - // Check if the selected microphone has a mute control
|
| - //
|
| - _mixerManager.MicrophoneMuteIsAvailable(isAvailable);
|
| - available = isAvailable;
|
| + // Check if the selected microphone has a mute control
|
| + //
|
| + _mixerManager.MicrophoneMuteIsAvailable(isAvailable);
|
| + available = isAvailable;
|
|
|
| - // Close the initialized input mixer
|
| - //
|
| - if (!wasInitialized)
|
| - {
|
| - _mixerManager.CloseMicrophone();
|
| - }
|
| + // Close the initialized input mixer
|
| + //
|
| + if (!wasInitialized) {
|
| + _mixerManager.CloseMicrophone();
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::SetMicrophoneMute(bool enable)
|
| -{
|
| - return (_mixerManager.SetMicrophoneMute(enable));
|
| +int32_t AudioDeviceMac::SetMicrophoneMute(bool enable) {
|
| + return (_mixerManager.SetMicrophoneMute(enable));
|
| }
|
|
|
| -int32_t AudioDeviceMac::MicrophoneMute(bool& enabled) const
|
| -{
|
| +int32_t AudioDeviceMac::MicrophoneMute(bool& enabled) const {
|
| + bool muted(0);
|
|
|
| - bool muted(0);
|
| -
|
| - if (_mixerManager.MicrophoneMute(muted) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.MicrophoneMute(muted) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - enabled = muted;
|
| - return 0;
|
| + enabled = muted;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::MicrophoneBoostIsAvailable(bool& available)
|
| -{
|
| +int32_t AudioDeviceMac::MicrophoneBoostIsAvailable(bool& available) {
|
| + bool isAvailable(false);
|
| + bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
|
|
|
| - bool isAvailable(false);
|
| - bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
|
| -
|
| - // Enumerate all avaliable microphone and make an attempt to open up the
|
| - // input mixer corresponding to the currently selected input device.
|
| - //
|
| - if (!wasInitialized && InitMicrophone() == -1)
|
| - {
|
| - // If we end up here it means that the selected microphone has no volume
|
| - // control, hence it is safe to state that there is no boost control
|
| - // already at this stage.
|
| - available = false;
|
| - return 0;
|
| - }
|
| + // Enumerate all avaliable microphone and make an attempt to open up the
|
| + // input mixer corresponding to the currently selected input device.
|
| + //
|
| + if (!wasInitialized && InitMicrophone() == -1) {
|
| + // If we end up here it means that the selected microphone has no volume
|
| + // control, hence it is safe to state that there is no boost control
|
| + // already at this stage.
|
| + available = false;
|
| + return 0;
|
| + }
|
|
|
| - // Check if the selected microphone has a boost control
|
| - //
|
| - _mixerManager.MicrophoneBoostIsAvailable(isAvailable);
|
| - available = isAvailable;
|
| + // Check if the selected microphone has a boost control
|
| + //
|
| + _mixerManager.MicrophoneBoostIsAvailable(isAvailable);
|
| + available = isAvailable;
|
|
|
| - // Close the initialized input mixer
|
| - //
|
| - if (!wasInitialized)
|
| - {
|
| - _mixerManager.CloseMicrophone();
|
| - }
|
| + // Close the initialized input mixer
|
| + //
|
| + if (!wasInitialized) {
|
| + _mixerManager.CloseMicrophone();
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::SetMicrophoneBoost(bool enable)
|
| -{
|
| -
|
| - return (_mixerManager.SetMicrophoneBoost(enable));
|
| +int32_t AudioDeviceMac::SetMicrophoneBoost(bool enable) {
|
| + return (_mixerManager.SetMicrophoneBoost(enable));
|
| }
|
|
|
| -int32_t AudioDeviceMac::MicrophoneBoost(bool& enabled) const
|
| -{
|
| -
|
| - bool onOff(0);
|
| +int32_t AudioDeviceMac::MicrophoneBoost(bool& enabled) const {
|
| + bool onOff(0);
|
|
|
| - if (_mixerManager.MicrophoneBoost(onOff) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.MicrophoneBoost(onOff) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - enabled = onOff;
|
| - return 0;
|
| + enabled = onOff;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::StereoRecordingIsAvailable(bool& available)
|
| -{
|
| -
|
| - bool isAvailable(false);
|
| - bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
|
| -
|
| - if (!wasInitialized && InitMicrophone() == -1)
|
| - {
|
| - // Cannot open the specified device
|
| - available = false;
|
| - return 0;
|
| - }
|
| -
|
| - // Check if the selected microphone can record stereo
|
| - //
|
| - _mixerManager.StereoRecordingIsAvailable(isAvailable);
|
| - available = isAvailable;
|
| -
|
| - // Close the initialized input mixer
|
| - //
|
| - if (!wasInitialized)
|
| - {
|
| - _mixerManager.CloseMicrophone();
|
| - }
|
| +int32_t AudioDeviceMac::StereoRecordingIsAvailable(bool& available) {
|
| + bool isAvailable(false);
|
| + bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
|
|
|
| + if (!wasInitialized && InitMicrophone() == -1) {
|
| + // Cannot open the specified device
|
| + available = false;
|
| return 0;
|
| -}
|
| + }
|
|
|
| -int32_t AudioDeviceMac::SetStereoRecording(bool enable)
|
| -{
|
| + // Check if the selected microphone can record stereo
|
| + //
|
| + _mixerManager.StereoRecordingIsAvailable(isAvailable);
|
| + available = isAvailable;
|
|
|
| - if (enable)
|
| - _recChannels = 2;
|
| - else
|
| - _recChannels = 1;
|
| + // Close the initialized input mixer
|
| + //
|
| + if (!wasInitialized) {
|
| + _mixerManager.CloseMicrophone();
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::StereoRecording(bool& enabled) const
|
| -{
|
| -
|
| - if (_recChannels == 2)
|
| - enabled = true;
|
| - else
|
| - enabled = false;
|
| +int32_t AudioDeviceMac::SetStereoRecording(bool enable) {
|
| + if (enable)
|
| + _recChannels = 2;
|
| + else
|
| + _recChannels = 1;
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::StereoPlayoutIsAvailable(bool& available)
|
| -{
|
| -
|
| - bool isAvailable(false);
|
| - bool wasInitialized = _mixerManager.SpeakerIsInitialized();
|
| +int32_t AudioDeviceMac::StereoRecording(bool& enabled) const {
|
| + if (_recChannels == 2)
|
| + enabled = true;
|
| + else
|
| + enabled = false;
|
|
|
| - if (!wasInitialized && InitSpeaker() == -1)
|
| - {
|
| - // Cannot open the specified device
|
| - available = false;
|
| - return 0;
|
| - }
|
| -
|
| - // Check if the selected microphone can record stereo
|
| - //
|
| - _mixerManager.StereoPlayoutIsAvailable(isAvailable);
|
| - available = isAvailable;
|
| + return 0;
|
| +}
|
|
|
| - // Close the initialized input mixer
|
| - //
|
| - if (!wasInitialized)
|
| - {
|
| - _mixerManager.CloseSpeaker();
|
| - }
|
| +int32_t AudioDeviceMac::StereoPlayoutIsAvailable(bool& available) {
|
| + bool isAvailable(false);
|
| + bool wasInitialized = _mixerManager.SpeakerIsInitialized();
|
|
|
| + if (!wasInitialized && InitSpeaker() == -1) {
|
| + // Cannot open the specified device
|
| + available = false;
|
| return 0;
|
| -}
|
| + }
|
|
|
| -int32_t AudioDeviceMac::SetStereoPlayout(bool enable)
|
| -{
|
| + // Check if the selected microphone can record stereo
|
| + //
|
| + _mixerManager.StereoPlayoutIsAvailable(isAvailable);
|
| + available = isAvailable;
|
|
|
| - if (enable)
|
| - _playChannels = 2;
|
| - else
|
| - _playChannels = 1;
|
| + // Close the initialized input mixer
|
| + //
|
| + if (!wasInitialized) {
|
| + _mixerManager.CloseSpeaker();
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::StereoPlayout(bool& enabled) const
|
| -{
|
| -
|
| - if (_playChannels == 2)
|
| - enabled = true;
|
| - else
|
| - enabled = false;
|
| +int32_t AudioDeviceMac::SetStereoPlayout(bool enable) {
|
| + if (enable)
|
| + _playChannels = 2;
|
| + else
|
| + _playChannels = 1;
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::SetAGC(bool enable)
|
| -{
|
| -
|
| - _AGC = enable;
|
| +int32_t AudioDeviceMac::StereoPlayout(bool& enabled) const {
|
| + if (_playChannels == 2)
|
| + enabled = true;
|
| + else
|
| + enabled = false;
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -bool AudioDeviceMac::AGC() const
|
| -{
|
| +int32_t AudioDeviceMac::SetAGC(bool enable) {
|
| + _AGC = enable;
|
|
|
| - return _AGC;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::MicrophoneVolumeIsAvailable(bool& available)
|
| -{
|
| +bool AudioDeviceMac::AGC() const {
|
| + return _AGC;
|
| +}
|
|
|
| - bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
|
| +int32_t AudioDeviceMac::MicrophoneVolumeIsAvailable(bool& available) {
|
| + bool wasInitialized = _mixerManager.MicrophoneIsInitialized();
|
|
|
| - // Make an attempt to open up the
|
| - // input mixer corresponding to the currently selected output device.
|
| - //
|
| - if (!wasInitialized && InitMicrophone() == -1)
|
| - {
|
| - // If we end up here it means that the selected microphone has no volume
|
| - // control.
|
| - available = false;
|
| - return 0;
|
| - }
|
| + // Make an attempt to open up the
|
| + // input mixer corresponding to the currently selected output device.
|
| + //
|
| + if (!wasInitialized && InitMicrophone() == -1) {
|
| + // If we end up here it means that the selected microphone has no volume
|
| + // control.
|
| + available = false;
|
| + return 0;
|
| + }
|
|
|
| - // Given that InitMicrophone was successful, we know that a volume control
|
| - // exists
|
| - //
|
| - available = true;
|
| + // Given that InitMicrophone was successful, we know that a volume control
|
| + // exists
|
| + //
|
| + available = true;
|
|
|
| - // Close the initialized input mixer
|
| - //
|
| - if (!wasInitialized)
|
| - {
|
| - _mixerManager.CloseMicrophone();
|
| - }
|
| + // Close the initialized input mixer
|
| + //
|
| + if (!wasInitialized) {
|
| + _mixerManager.CloseMicrophone();
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::SetMicrophoneVolume(uint32_t volume)
|
| -{
|
| -
|
| - return (_mixerManager.SetMicrophoneVolume(volume));
|
| +int32_t AudioDeviceMac::SetMicrophoneVolume(uint32_t volume) {
|
| + return (_mixerManager.SetMicrophoneVolume(volume));
|
| }
|
|
|
| -int32_t AudioDeviceMac::MicrophoneVolume(uint32_t& volume) const
|
| -{
|
| +int32_t AudioDeviceMac::MicrophoneVolume(uint32_t& volume) const {
|
| + uint32_t level(0);
|
|
|
| - uint32_t level(0);
|
| -
|
| - if (_mixerManager.MicrophoneVolume(level) == -1)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " failed to retrive current microphone level");
|
| - return -1;
|
| - }
|
| + if (_mixerManager.MicrophoneVolume(level) == -1) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " failed to retrive current microphone level");
|
| + return -1;
|
| + }
|
|
|
| - volume = level;
|
| - return 0;
|
| + volume = level;
|
| + return 0;
|
| }
|
|
|
| -int32_t
|
| -AudioDeviceMac::MaxMicrophoneVolume(uint32_t& maxVolume) const
|
| -{
|
| +int32_t AudioDeviceMac::MaxMicrophoneVolume(uint32_t& maxVolume) const {
|
| + uint32_t maxVol(0);
|
|
|
| - uint32_t maxVol(0);
|
| -
|
| - if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.MaxMicrophoneVolume(maxVol) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - maxVolume = maxVol;
|
| - return 0;
|
| + maxVolume = maxVol;
|
| + return 0;
|
| }
|
|
|
| -int32_t
|
| -AudioDeviceMac::MinMicrophoneVolume(uint32_t& minVolume) const
|
| -{
|
| +int32_t AudioDeviceMac::MinMicrophoneVolume(uint32_t& minVolume) const {
|
| + uint32_t minVol(0);
|
|
|
| - uint32_t minVol(0);
|
| -
|
| - if (_mixerManager.MinMicrophoneVolume(minVol) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.MinMicrophoneVolume(minVol) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - minVolume = minVol;
|
| - return 0;
|
| + minVolume = minVol;
|
| + return 0;
|
| }
|
|
|
| -int32_t
|
| -AudioDeviceMac::MicrophoneVolumeStepSize(uint16_t& stepSize) const
|
| -{
|
| -
|
| - uint16_t delta(0);
|
| +int32_t AudioDeviceMac::MicrophoneVolumeStepSize(uint16_t& stepSize) const {
|
| + uint16_t delta(0);
|
|
|
| - if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_mixerManager.MicrophoneVolumeStepSize(delta) == -1) {
|
| + return -1;
|
| + }
|
|
|
| - stepSize = delta;
|
| - return 0;
|
| + stepSize = delta;
|
| + return 0;
|
| }
|
|
|
| -int16_t AudioDeviceMac::PlayoutDevices()
|
| -{
|
| -
|
| - AudioDeviceID playDevices[MaxNumberDevices];
|
| - return GetNumberDevices(kAudioDevicePropertyScopeOutput, playDevices,
|
| - MaxNumberDevices);
|
| +int16_t AudioDeviceMac::PlayoutDevices() {
|
| + AudioDeviceID playDevices[MaxNumberDevices];
|
| + return GetNumberDevices(kAudioDevicePropertyScopeOutput, playDevices,
|
| + MaxNumberDevices);
|
| }
|
|
|
| -int32_t AudioDeviceMac::SetPlayoutDevice(uint16_t index)
|
| -{
|
| - CriticalSectionScoped lock(&_critSect);
|
| +int32_t AudioDeviceMac::SetPlayoutDevice(uint16_t index) {
|
| + CriticalSectionScoped lock(&_critSect);
|
|
|
| - if (_playIsInitialized)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_playIsInitialized) {
|
| + return -1;
|
| + }
|
|
|
| - AudioDeviceID playDevices[MaxNumberDevices];
|
| - uint32_t nDevices = GetNumberDevices(kAudioDevicePropertyScopeOutput,
|
| - playDevices, MaxNumberDevices);
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - " number of availiable waveform-audio output devices is %u",
|
| - nDevices);
|
| -
|
| - if (index > (nDevices - 1))
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " device index is out of range [0,%u]", (nDevices - 1));
|
| - return -1;
|
| - }
|
| + AudioDeviceID playDevices[MaxNumberDevices];
|
| + uint32_t nDevices = GetNumberDevices(kAudioDevicePropertyScopeOutput,
|
| + playDevices, MaxNumberDevices);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + " number of availiable waveform-audio output devices is %u",
|
| + nDevices);
|
|
|
| - _outputDeviceIndex = index;
|
| - _outputDeviceIsSpecified = true;
|
| + if (index > (nDevices - 1)) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " device index is out of range [0,%u]", (nDevices - 1));
|
| + return -1;
|
| + }
|
|
|
| - return 0;
|
| + _outputDeviceIndex = index;
|
| + _outputDeviceIsSpecified = true;
|
| +
|
| + return 0;
|
| }
|
|
|
| int32_t AudioDeviceMac::SetPlayoutDevice(
|
| - AudioDeviceModule::WindowsDeviceType /*device*/)
|
| -{
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - "WindowsDeviceType not supported");
|
| - return -1;
|
| + AudioDeviceModule::WindowsDeviceType /*device*/) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + "WindowsDeviceType not supported");
|
| + return -1;
|
| }
|
|
|
| -int32_t AudioDeviceMac::PlayoutDeviceName(
|
| - uint16_t index,
|
| - char name[kAdmMaxDeviceNameSize],
|
| - char guid[kAdmMaxGuidSize])
|
| -{
|
| -
|
| - const uint16_t nDevices(PlayoutDevices());
|
| +int32_t AudioDeviceMac::PlayoutDeviceName(uint16_t index,
|
| + char name[kAdmMaxDeviceNameSize],
|
| + char guid[kAdmMaxGuidSize]) {
|
| + const uint16_t nDevices(PlayoutDevices());
|
|
|
| - if ((index > (nDevices - 1)) || (name == NULL))
|
| - {
|
| - return -1;
|
| - }
|
| + if ((index > (nDevices - 1)) || (name == NULL)) {
|
| + return -1;
|
| + }
|
|
|
| - memset(name, 0, kAdmMaxDeviceNameSize);
|
| + memset(name, 0, kAdmMaxDeviceNameSize);
|
|
|
| - if (guid != NULL)
|
| - {
|
| - memset(guid, 0, kAdmMaxGuidSize);
|
| - }
|
| + if (guid != NULL) {
|
| + memset(guid, 0, kAdmMaxGuidSize);
|
| + }
|
|
|
| - return GetDeviceName(kAudioDevicePropertyScopeOutput, index, name);
|
| + return GetDeviceName(kAudioDevicePropertyScopeOutput, index, name);
|
| }
|
|
|
| -int32_t AudioDeviceMac::RecordingDeviceName(
|
| - uint16_t index,
|
| - char name[kAdmMaxDeviceNameSize],
|
| - char guid[kAdmMaxGuidSize])
|
| -{
|
| +int32_t AudioDeviceMac::RecordingDeviceName(uint16_t index,
|
| + char name[kAdmMaxDeviceNameSize],
|
| + char guid[kAdmMaxGuidSize]) {
|
| + const uint16_t nDevices(RecordingDevices());
|
|
|
| - const uint16_t nDevices(RecordingDevices());
|
| -
|
| - if ((index > (nDevices - 1)) || (name == NULL))
|
| - {
|
| - return -1;
|
| - }
|
| + if ((index > (nDevices - 1)) || (name == NULL)) {
|
| + return -1;
|
| + }
|
|
|
| - memset(name, 0, kAdmMaxDeviceNameSize);
|
| + memset(name, 0, kAdmMaxDeviceNameSize);
|
|
|
| - if (guid != NULL)
|
| - {
|
| - memset(guid, 0, kAdmMaxGuidSize);
|
| - }
|
| + if (guid != NULL) {
|
| + memset(guid, 0, kAdmMaxGuidSize);
|
| + }
|
|
|
| - return GetDeviceName(kAudioDevicePropertyScopeInput, index, name);
|
| + return GetDeviceName(kAudioDevicePropertyScopeInput, index, name);
|
| }
|
|
|
| -int16_t AudioDeviceMac::RecordingDevices()
|
| -{
|
| -
|
| - AudioDeviceID recDevices[MaxNumberDevices];
|
| - return GetNumberDevices(kAudioDevicePropertyScopeInput, recDevices,
|
| - MaxNumberDevices);
|
| +int16_t AudioDeviceMac::RecordingDevices() {
|
| + AudioDeviceID recDevices[MaxNumberDevices];
|
| + return GetNumberDevices(kAudioDevicePropertyScopeInput, recDevices,
|
| + MaxNumberDevices);
|
| }
|
|
|
| -int32_t AudioDeviceMac::SetRecordingDevice(uint16_t index)
|
| -{
|
| +int32_t AudioDeviceMac::SetRecordingDevice(uint16_t index) {
|
| + if (_recIsInitialized) {
|
| + return -1;
|
| + }
|
|
|
| - if (_recIsInitialized)
|
| - {
|
| - return -1;
|
| - }
|
| + AudioDeviceID recDevices[MaxNumberDevices];
|
| + uint32_t nDevices = GetNumberDevices(kAudioDevicePropertyScopeInput,
|
| + recDevices, MaxNumberDevices);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + " number of availiable waveform-audio input devices is %u",
|
| + nDevices);
|
|
|
| - AudioDeviceID recDevices[MaxNumberDevices];
|
| - uint32_t nDevices = GetNumberDevices(kAudioDevicePropertyScopeInput,
|
| - recDevices, MaxNumberDevices);
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - " number of availiable waveform-audio input devices is %u",
|
| - nDevices);
|
| -
|
| - if (index > (nDevices - 1))
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " device index is out of range [0,%u]", (nDevices - 1));
|
| - return -1;
|
| - }
|
| + if (index > (nDevices - 1)) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " device index is out of range [0,%u]", (nDevices - 1));
|
| + return -1;
|
| + }
|
|
|
| - _inputDeviceIndex = index;
|
| - _inputDeviceIsSpecified = true;
|
| + _inputDeviceIndex = index;
|
| + _inputDeviceIsSpecified = true;
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -
|
| -int32_t
|
| -AudioDeviceMac::SetRecordingDevice(AudioDeviceModule::WindowsDeviceType /*device*/)
|
| -{
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - "WindowsDeviceType not supported");
|
| - return -1;
|
| +int32_t AudioDeviceMac::SetRecordingDevice(
|
| + AudioDeviceModule::WindowsDeviceType /*device*/) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + "WindowsDeviceType not supported");
|
| + return -1;
|
| }
|
|
|
| -int32_t AudioDeviceMac::PlayoutIsAvailable(bool& available)
|
| -{
|
| +int32_t AudioDeviceMac::PlayoutIsAvailable(bool& available) {
|
| + available = true;
|
|
|
| - available = true;
|
| -
|
| - // Try to initialize the playout side
|
| - if (InitPlayout() == -1)
|
| - {
|
| - available = false;
|
| - }
|
| + // Try to initialize the playout side
|
| + if (InitPlayout() == -1) {
|
| + available = false;
|
| + }
|
|
|
| - // We destroy the IOProc created by InitPlayout() in implDeviceIOProc().
|
| - // We must actually start playout here in order to have the IOProc
|
| - // deleted by calling StopPlayout().
|
| - if (StartPlayout() == -1)
|
| - {
|
| - available = false;
|
| - }
|
| + // We destroy the IOProc created by InitPlayout() in implDeviceIOProc().
|
| + // We must actually start playout here in order to have the IOProc
|
| + // deleted by calling StopPlayout().
|
| + if (StartPlayout() == -1) {
|
| + available = false;
|
| + }
|
|
|
| - // Cancel effect of initialization
|
| - if (StopPlayout() == -1)
|
| - {
|
| - available = false;
|
| - }
|
| + // Cancel effect of initialization
|
| + if (StopPlayout() == -1) {
|
| + available = false;
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::RecordingIsAvailable(bool& available)
|
| -{
|
| +int32_t AudioDeviceMac::RecordingIsAvailable(bool& available) {
|
| + available = true;
|
|
|
| - available = true;
|
| -
|
| - // Try to initialize the recording side
|
| - if (InitRecording() == -1)
|
| - {
|
| - available = false;
|
| - }
|
| + // Try to initialize the recording side
|
| + if (InitRecording() == -1) {
|
| + available = false;
|
| + }
|
|
|
| - // We destroy the IOProc created by InitRecording() in implInDeviceIOProc().
|
| - // We must actually start recording here in order to have the IOProc
|
| - // deleted by calling StopRecording().
|
| - if (StartRecording() == -1)
|
| - {
|
| - available = false;
|
| - }
|
| + // We destroy the IOProc created by InitRecording() in implInDeviceIOProc().
|
| + // We must actually start recording here in order to have the IOProc
|
| + // deleted by calling StopRecording().
|
| + if (StartRecording() == -1) {
|
| + available = false;
|
| + }
|
|
|
| - // Cancel effect of initialization
|
| - if (StopRecording() == -1)
|
| - {
|
| - available = false;
|
| - }
|
| + // Cancel effect of initialization
|
| + if (StopRecording() == -1) {
|
| + available = false;
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::InitPlayout()
|
| -{
|
| - CriticalSectionScoped lock(&_critSect);
|
| +int32_t AudioDeviceMac::InitPlayout() {
|
| + CriticalSectionScoped lock(&_critSect);
|
|
|
| - if (_playing)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_playing) {
|
| + return -1;
|
| + }
|
|
|
| - if (!_outputDeviceIsSpecified)
|
| - {
|
| - return -1;
|
| - }
|
| + if (!_outputDeviceIsSpecified) {
|
| + return -1;
|
| + }
|
|
|
| - if (_playIsInitialized)
|
| - {
|
| - return 0;
|
| - }
|
| + if (_playIsInitialized) {
|
| + return 0;
|
| + }
|
|
|
| - // Initialize the speaker (devices might have been added or removed)
|
| - if (InitSpeaker() == -1)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " InitSpeaker() failed");
|
| - }
|
| + // Initialize the speaker (devices might have been added or removed)
|
| + if (InitSpeaker() == -1) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " InitSpeaker() failed");
|
| + }
|
|
|
| - if (!MicrophoneIsInitialized())
|
| - {
|
| - // Make this call to check if we are using
|
| - // one or two devices (_twoDevices)
|
| - bool available = false;
|
| - if (MicrophoneIsAvailable(available) == -1)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " MicrophoneIsAvailable() failed");
|
| - }
|
| + if (!MicrophoneIsInitialized()) {
|
| + // Make this call to check if we are using
|
| + // one or two devices (_twoDevices)
|
| + bool available = false;
|
| + if (MicrophoneIsAvailable(available) == -1) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " MicrophoneIsAvailable() failed");
|
| }
|
| + }
|
|
|
| - PaUtil_FlushRingBuffer(_paRenderBuffer);
|
| -
|
| - OSStatus err = noErr;
|
| - UInt32 size = 0;
|
| - _renderDelayOffsetSamples = 0;
|
| - _renderDelayUs = 0;
|
| - _renderLatencyUs = 0;
|
| - _renderDeviceIsAlive = 1;
|
| - _doStop = false;
|
| + PaUtil_FlushRingBuffer(_paRenderBuffer);
|
| +
|
| + OSStatus err = noErr;
|
| + UInt32 size = 0;
|
| + _renderDelayOffsetSamples = 0;
|
| + _renderDelayUs = 0;
|
| + _renderLatencyUs = 0;
|
| + _renderDeviceIsAlive = 1;
|
| + _doStop = false;
|
| +
|
| + // The internal microphone of a MacBook Pro is located under the left speaker
|
| + // grille. When the internal speakers are in use, we want to fully stereo
|
| + // pan to the right.
|
| + AudioObjectPropertyAddress propertyAddress = {
|
| + kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput, 0};
|
| + if (_macBookPro) {
|
| + _macBookProPanRight = false;
|
| + Boolean hasProperty =
|
| + AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
|
| + if (hasProperty) {
|
| + UInt32 dataSource = 0;
|
| + size = sizeof(dataSource);
|
| + WEBRTC_CA_LOG_WARN(AudioObjectGetPropertyData(
|
| + _outputDeviceID, &propertyAddress, 0, NULL, &size, &dataSource));
|
| +
|
| + if (dataSource == 'ispk') {
|
| + _macBookProPanRight = true;
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "MacBook Pro using internal speakers; stereo"
|
| + " panning right");
|
| + } else {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "MacBook Pro not using internal speakers");
|
| + }
|
|
|
| - // The internal microphone of a MacBook Pro is located under the left speaker
|
| - // grille. When the internal speakers are in use, we want to fully stereo
|
| - // pan to the right.
|
| - AudioObjectPropertyAddress
|
| - propertyAddress = { kAudioDevicePropertyDataSource,
|
| - kAudioDevicePropertyScopeOutput, 0 };
|
| - if (_macBookPro)
|
| - {
|
| - _macBookProPanRight = false;
|
| - Boolean hasProperty = AudioObjectHasProperty(_outputDeviceID,
|
| - &propertyAddress);
|
| - if (hasProperty)
|
| - {
|
| - UInt32 dataSource = 0;
|
| - size = sizeof(dataSource);
|
| - WEBRTC_CA_LOG_WARN(AudioObjectGetPropertyData(_outputDeviceID,
|
| - &propertyAddress, 0, NULL, &size, &dataSource));
|
| -
|
| - if (dataSource == 'ispk')
|
| - {
|
| - _macBookProPanRight = true;
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice,
|
| - _id,
|
| - "MacBook Pro using internal speakers; stereo"
|
| - " panning right");
|
| - } else
|
| - {
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice,
|
| - _id, "MacBook Pro not using internal speakers");
|
| - }
|
| -
|
| - // Add a listener to determine if the status changes.
|
| - WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(_outputDeviceID,
|
| - &propertyAddress, &objectListenerProc, this));
|
| - }
|
| + // Add a listener to determine if the status changes.
|
| + WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(
|
| + _outputDeviceID, &propertyAddress, &objectListenerProc, this));
|
| }
|
| + }
|
|
|
| - // Get current stream description
|
| - propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
|
| - memset(&_outStreamFormat, 0, sizeof(_outStreamFormat));
|
| - size = sizeof(_outStreamFormat);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID,
|
| - &propertyAddress, 0, NULL, &size, &_outStreamFormat));
|
| -
|
| - if (_outStreamFormat.mFormatID != kAudioFormatLinearPCM)
|
| - {
|
| - logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| - "Unacceptable output stream format -> mFormatID",
|
| - (const char *) &_outStreamFormat.mFormatID);
|
| - return -1;
|
| - }
|
| + // Get current stream description
|
| + propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
|
| + memset(&_outStreamFormat, 0, sizeof(_outStreamFormat));
|
| + size = sizeof(_outStreamFormat);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + _outputDeviceID, &propertyAddress, 0, NULL, &size, &_outStreamFormat));
|
| +
|
| + if (_outStreamFormat.mFormatID != kAudioFormatLinearPCM) {
|
| + logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| + "Unacceptable output stream format -> mFormatID",
|
| + (const char*)&_outStreamFormat.mFormatID);
|
| + return -1;
|
| + }
|
|
|
| - if (_outStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - "Too many channels on output device (mChannelsPerFrame = %d)",
|
| - _outStreamFormat.mChannelsPerFrame);
|
| - return -1;
|
| - }
|
| + if (_outStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + "Too many channels on output device (mChannelsPerFrame = %d)",
|
| + _outStreamFormat.mChannelsPerFrame);
|
| + return -1;
|
| + }
|
|
|
| - if (_outStreamFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - "Non-interleaved audio data is not supported.",
|
| - "AudioHardware streams should not have this format.");
|
| - return -1;
|
| - }
|
| + if (_outStreamFormat.mFormatFlags & kAudioFormatFlagIsNonInterleaved) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + "Non-interleaved audio data is not supported.",
|
| + "AudioHardware streams should not have this format.");
|
| + return -1;
|
| + }
|
|
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Ouput stream format:");
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "mSampleRate = %f, mChannelsPerFrame = %u",
|
| + _outStreamFormat.mSampleRate,
|
| + _outStreamFormat.mChannelsPerFrame);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "mBytesPerPacket = %u, mFramesPerPacket = %u",
|
| + _outStreamFormat.mBytesPerPacket,
|
| + _outStreamFormat.mFramesPerPacket);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "mBytesPerFrame = %u, mBitsPerChannel = %u",
|
| + _outStreamFormat.mBytesPerFrame,
|
| + _outStreamFormat.mBitsPerChannel);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "mFormatFlags = %u",
|
| + _outStreamFormat.mFormatFlags);
|
| + logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
| + (const char*)&_outStreamFormat.mFormatID);
|
| +
|
| + // Our preferred format to work with.
|
| + if (_outStreamFormat.mChannelsPerFrame < 2) {
|
| + // Disable stereo playout when we only have one channel on the device.
|
| + _playChannels = 1;
|
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "Ouput stream format:");
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "mSampleRate = %f, mChannelsPerFrame = %u",
|
| - _outStreamFormat.mSampleRate,
|
| - _outStreamFormat.mChannelsPerFrame);
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "mBytesPerPacket = %u, mFramesPerPacket = %u",
|
| - _outStreamFormat.mBytesPerPacket,
|
| - _outStreamFormat.mFramesPerPacket);
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "mBytesPerFrame = %u, mBitsPerChannel = %u",
|
| - _outStreamFormat.mBytesPerFrame,
|
| - _outStreamFormat.mBitsPerChannel);
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "mFormatFlags = %u",
|
| - _outStreamFormat.mFormatFlags);
|
| - logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
| - (const char *) &_outStreamFormat.mFormatID);
|
| -
|
| - // Our preferred format to work with.
|
| - if (_outStreamFormat.mChannelsPerFrame < 2)
|
| - {
|
| - // Disable stereo playout when we only have one channel on the device.
|
| - _playChannels = 1;
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "Stereo playout unavailable on this device");
|
| - }
|
| - WEBRTC_CA_RETURN_ON_ERR(SetDesiredPlayoutFormat());
|
| + "Stereo playout unavailable on this device");
|
| + }
|
| + WEBRTC_CA_RETURN_ON_ERR(SetDesiredPlayoutFormat());
|
|
|
| - // Listen for format changes.
|
| - propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectAddPropertyListener(_outputDeviceID,
|
| - &propertyAddress,
|
| - &objectListenerProc,
|
| - this));
|
| -
|
| - // Listen for processor overloads.
|
| - propertyAddress.mSelector = kAudioDeviceProcessorOverload;
|
| - WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(_outputDeviceID,
|
| - &propertyAddress,
|
| - &objectListenerProc,
|
| - this));
|
| -
|
| - if (_twoDevices || !_recIsInitialized)
|
| - {
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(_outputDeviceID,
|
| - deviceIOProc,
|
| - this,
|
| - &_deviceIOProcID));
|
| - }
|
| + // Listen for format changes.
|
| + propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectAddPropertyListener(
|
| + _outputDeviceID, &propertyAddress, &objectListenerProc, this));
|
|
|
| - _playIsInitialized = true;
|
| + // Listen for processor overloads.
|
| + propertyAddress.mSelector = kAudioDeviceProcessorOverload;
|
| + WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(
|
| + _outputDeviceID, &propertyAddress, &objectListenerProc, this));
|
|
|
| - return 0;
|
| + if (_twoDevices || !_recIsInitialized) {
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(
|
| + _outputDeviceID, deviceIOProc, this, &_deviceIOProcID));
|
| + }
|
| +
|
| + _playIsInitialized = true;
|
| +
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::InitRecording()
|
| -{
|
| +int32_t AudioDeviceMac::InitRecording() {
|
| + CriticalSectionScoped lock(&_critSect);
|
|
|
| - CriticalSectionScoped lock(&_critSect);
|
| + if (_recording) {
|
| + return -1;
|
| + }
|
|
|
| - if (_recording)
|
| - {
|
| - return -1;
|
| - }
|
| + if (!_inputDeviceIsSpecified) {
|
| + return -1;
|
| + }
|
|
|
| - if (!_inputDeviceIsSpecified)
|
| - {
|
| - return -1;
|
| - }
|
| + if (_recIsInitialized) {
|
| + return 0;
|
| + }
|
|
|
| - if (_recIsInitialized)
|
| - {
|
| - return 0;
|
| - }
|
| + // Initialize the microphone (devices might have been added or removed)
|
| + if (InitMicrophone() == -1) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " InitMicrophone() failed");
|
| + }
|
|
|
| - // Initialize the microphone (devices might have been added or removed)
|
| - if (InitMicrophone() == -1)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " InitMicrophone() failed");
|
| + if (!SpeakerIsInitialized()) {
|
| + // Make this call to check if we are using
|
| + // one or two devices (_twoDevices)
|
| + bool available = false;
|
| + if (SpeakerIsAvailable(available) == -1) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " SpeakerIsAvailable() failed");
|
| }
|
| + }
|
|
|
| - if (!SpeakerIsInitialized())
|
| - {
|
| - // Make this call to check if we are using
|
| - // one or two devices (_twoDevices)
|
| - bool available = false;
|
| - if (SpeakerIsAvailable(available) == -1)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " SpeakerIsAvailable() failed");
|
| - }
|
| - }
|
| + OSStatus err = noErr;
|
| + UInt32 size = 0;
|
|
|
| - OSStatus err = noErr;
|
| - UInt32 size = 0;
|
| -
|
| - PaUtil_FlushRingBuffer(_paCaptureBuffer);
|
| -
|
| - _captureDelayUs = 0;
|
| - _captureLatencyUs = 0;
|
| - _captureDeviceIsAlive = 1;
|
| - _doStopRec = false;
|
| -
|
| - // Get current stream description
|
| - AudioObjectPropertyAddress
|
| - propertyAddress = { kAudioDevicePropertyStreamFormat,
|
| - kAudioDevicePropertyScopeInput, 0 };
|
| - memset(&_inStreamFormat, 0, sizeof(_inStreamFormat));
|
| - size = sizeof(_inStreamFormat);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID,
|
| - &propertyAddress, 0, NULL, &size, &_inStreamFormat));
|
| -
|
| - if (_inStreamFormat.mFormatID != kAudioFormatLinearPCM)
|
| - {
|
| - logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| - "Unacceptable input stream format -> mFormatID",
|
| - (const char *) &_inStreamFormat.mFormatID);
|
| - return -1;
|
| - }
|
| + PaUtil_FlushRingBuffer(_paCaptureBuffer);
|
|
|
| - if (_inStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - "Too many channels on input device (mChannelsPerFrame = %d)",
|
| - _inStreamFormat.mChannelsPerFrame);
|
| - return -1;
|
| - }
|
| + _captureDelayUs = 0;
|
| + _captureLatencyUs = 0;
|
| + _captureDeviceIsAlive = 1;
|
| + _doStopRec = false;
|
|
|
| - const int io_block_size_samples = _inStreamFormat.mChannelsPerFrame *
|
| - _inStreamFormat.mSampleRate / 100 * N_BLOCKS_IO;
|
| - if (io_block_size_samples > _captureBufSizeSamples)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - "Input IO block size (%d) is larger than ring buffer (%u)",
|
| - io_block_size_samples, _captureBufSizeSamples);
|
| - return -1;
|
| - }
|
| + // Get current stream description
|
| + AudioObjectPropertyAddress propertyAddress = {
|
| + kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput, 0};
|
| + memset(&_inStreamFormat, 0, sizeof(_inStreamFormat));
|
| + size = sizeof(_inStreamFormat);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + _inputDeviceID, &propertyAddress, 0, NULL, &size, &_inStreamFormat));
|
|
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - " Input stream format:");
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - " mSampleRate = %f, mChannelsPerFrame = %u",
|
| - _inStreamFormat.mSampleRate, _inStreamFormat.mChannelsPerFrame);
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - " mBytesPerPacket = %u, mFramesPerPacket = %u",
|
| - _inStreamFormat.mBytesPerPacket,
|
| - _inStreamFormat.mFramesPerPacket);
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - " mBytesPerFrame = %u, mBitsPerChannel = %u",
|
| - _inStreamFormat.mBytesPerFrame,
|
| - _inStreamFormat.mBitsPerChannel);
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - " mFormatFlags = %u",
|
| - _inStreamFormat.mFormatFlags);
|
| - logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
| - (const char *) &_inStreamFormat.mFormatID);
|
| + if (_inStreamFormat.mFormatID != kAudioFormatLinearPCM) {
|
| + logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| + "Unacceptable input stream format -> mFormatID",
|
| + (const char*)&_inStreamFormat.mFormatID);
|
| + return -1;
|
| + }
|
|
|
| - // Our preferred format to work with
|
| - if (_inStreamFormat.mChannelsPerFrame >= 2 && (_recChannels == 2))
|
| - {
|
| - _inDesiredFormat.mChannelsPerFrame = 2;
|
| - } else
|
| - {
|
| - // Disable stereo recording when we only have one channel on the device.
|
| - _inDesiredFormat.mChannelsPerFrame = 1;
|
| - _recChannels = 1;
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "Stereo recording unavailable on this device");
|
| - }
|
| + if (_inStreamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + "Too many channels on input device (mChannelsPerFrame = %d)",
|
| + _inStreamFormat.mChannelsPerFrame);
|
| + return -1;
|
| + }
|
|
|
| - if (_ptrAudioBuffer)
|
| - {
|
| - // Update audio buffer with the selected parameters
|
| - _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC);
|
| - _ptrAudioBuffer->SetRecordingChannels((uint8_t) _recChannels);
|
| - }
|
| + const int io_block_size_samples = _inStreamFormat.mChannelsPerFrame *
|
| + _inStreamFormat.mSampleRate / 100 *
|
| + N_BLOCKS_IO;
|
| + if (io_block_size_samples > _captureBufSizeSamples) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + "Input IO block size (%d) is larger than ring buffer (%u)",
|
| + io_block_size_samples, _captureBufSizeSamples);
|
| + return -1;
|
| + }
|
|
|
| - _inDesiredFormat.mSampleRate = N_REC_SAMPLES_PER_SEC;
|
| - _inDesiredFormat.mBytesPerPacket = _inDesiredFormat.mChannelsPerFrame
|
| - * sizeof(SInt16);
|
| - _inDesiredFormat.mFramesPerPacket = 1;
|
| - _inDesiredFormat.mBytesPerFrame = _inDesiredFormat.mChannelsPerFrame
|
| - * sizeof(SInt16);
|
| - _inDesiredFormat.mBitsPerChannel = sizeof(SInt16) * 8;
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " Input stream format:");
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + " mSampleRate = %f, mChannelsPerFrame = %u",
|
| + _inStreamFormat.mSampleRate, _inStreamFormat.mChannelsPerFrame);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + " mBytesPerPacket = %u, mFramesPerPacket = %u",
|
| + _inStreamFormat.mBytesPerPacket,
|
| + _inStreamFormat.mFramesPerPacket);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + " mBytesPerFrame = %u, mBitsPerChannel = %u",
|
| + _inStreamFormat.mBytesPerFrame, _inStreamFormat.mBitsPerChannel);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " mFormatFlags = %u",
|
| + _inStreamFormat.mFormatFlags);
|
| + logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
| + (const char*)&_inStreamFormat.mFormatID);
|
| +
|
| + // Our preferred format to work with
|
| + if (_inStreamFormat.mChannelsPerFrame >= 2 && (_recChannels == 2)) {
|
| + _inDesiredFormat.mChannelsPerFrame = 2;
|
| + } else {
|
| + // Disable stereo recording when we only have one channel on the device.
|
| + _inDesiredFormat.mChannelsPerFrame = 1;
|
| + _recChannels = 1;
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "Stereo recording unavailable on this device");
|
| + }
|
| +
|
| + if (_ptrAudioBuffer) {
|
| + // Update audio buffer with the selected parameters
|
| + _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC);
|
| + _ptrAudioBuffer->SetRecordingChannels((uint8_t)_recChannels);
|
| + }
|
|
|
| - _inDesiredFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger
|
| - | kLinearPCMFormatFlagIsPacked;
|
| + _inDesiredFormat.mSampleRate = N_REC_SAMPLES_PER_SEC;
|
| + _inDesiredFormat.mBytesPerPacket =
|
| + _inDesiredFormat.mChannelsPerFrame * sizeof(SInt16);
|
| + _inDesiredFormat.mFramesPerPacket = 1;
|
| + _inDesiredFormat.mBytesPerFrame =
|
| + _inDesiredFormat.mChannelsPerFrame * sizeof(SInt16);
|
| + _inDesiredFormat.mBitsPerChannel = sizeof(SInt16) * 8;
|
| +
|
| + _inDesiredFormat.mFormatFlags =
|
| + kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
| #ifdef WEBRTC_ARCH_BIG_ENDIAN
|
| - _inDesiredFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
|
| + _inDesiredFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
|
| #endif
|
| - _inDesiredFormat.mFormatID = kAudioFormatLinearPCM;
|
| -
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(&_inStreamFormat, &_inDesiredFormat,
|
| - &_captureConverter));
|
| -
|
| - // First try to set buffer size to desired value (10 ms * N_BLOCKS_IO)
|
| - // TODO(xians): investigate this block.
|
| - UInt32 bufByteCount = (UInt32)((_inStreamFormat.mSampleRate / 1000.0)
|
| - * 10.0 * N_BLOCKS_IO * _inStreamFormat.mChannelsPerFrame
|
| - * sizeof(Float32));
|
| - if (_inStreamFormat.mFramesPerPacket != 0)
|
| - {
|
| - if (bufByteCount % _inStreamFormat.mFramesPerPacket != 0)
|
| - {
|
| - bufByteCount = ((UInt32)(bufByteCount
|
| - / _inStreamFormat.mFramesPerPacket) + 1)
|
| - * _inStreamFormat.mFramesPerPacket;
|
| - }
|
| + _inDesiredFormat.mFormatID = kAudioFormatLinearPCM;
|
| +
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(&_inStreamFormat, &_inDesiredFormat,
|
| + &_captureConverter));
|
| +
|
| + // First try to set buffer size to desired value (10 ms * N_BLOCKS_IO)
|
| + // TODO(xians): investigate this block.
|
| + UInt32 bufByteCount =
|
| + (UInt32)((_inStreamFormat.mSampleRate / 1000.0) * 10.0 * N_BLOCKS_IO *
|
| + _inStreamFormat.mChannelsPerFrame * sizeof(Float32));
|
| + if (_inStreamFormat.mFramesPerPacket != 0) {
|
| + if (bufByteCount % _inStreamFormat.mFramesPerPacket != 0) {
|
| + bufByteCount =
|
| + ((UInt32)(bufByteCount / _inStreamFormat.mFramesPerPacket) + 1) *
|
| + _inStreamFormat.mFramesPerPacket;
|
| }
|
| + }
|
|
|
| - // Ensure the buffer size is within the acceptable range provided by the device.
|
| - propertyAddress.mSelector = kAudioDevicePropertyBufferSizeRange;
|
| - AudioValueRange range;
|
| - size = sizeof(range);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID,
|
| - &propertyAddress, 0, NULL, &size, &range));
|
| - if (range.mMinimum > bufByteCount)
|
| - {
|
| - bufByteCount = range.mMinimum;
|
| - } else if (range.mMaximum < bufByteCount)
|
| - {
|
| - bufByteCount = range.mMaximum;
|
| - }
|
| + // Ensure the buffer size is within the acceptable range provided by the
|
| + // device.
|
| + propertyAddress.mSelector = kAudioDevicePropertyBufferSizeRange;
|
| + AudioValueRange range;
|
| + size = sizeof(range);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + _inputDeviceID, &propertyAddress, 0, NULL, &size, &range));
|
| + if (range.mMinimum > bufByteCount) {
|
| + bufByteCount = range.mMinimum;
|
| + } else if (range.mMaximum < bufByteCount) {
|
| + bufByteCount = range.mMaximum;
|
| + }
|
|
|
| - propertyAddress.mSelector = kAudioDevicePropertyBufferSize;
|
| - size = sizeof(bufByteCount);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_inputDeviceID,
|
| - &propertyAddress, 0, NULL, size, &bufByteCount));
|
| -
|
| - // Get capture device latency
|
| - propertyAddress.mSelector = kAudioDevicePropertyLatency;
|
| - UInt32 latency = 0;
|
| - size = sizeof(UInt32);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID,
|
| - &propertyAddress, 0, NULL, &size, &latency));
|
| - _captureLatencyUs = (UInt32)((1.0e6 * latency)
|
| - / _inStreamFormat.mSampleRate);
|
| -
|
| - // Get capture stream latency
|
| - propertyAddress.mSelector = kAudioDevicePropertyStreams;
|
| - AudioStreamID stream = 0;
|
| - size = sizeof(AudioStreamID);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID,
|
| - &propertyAddress, 0, NULL, &size, &stream));
|
| - propertyAddress.mSelector = kAudioStreamPropertyLatency;
|
| - size = sizeof(UInt32);
|
| - latency = 0;
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_inputDeviceID,
|
| - &propertyAddress, 0, NULL, &size, &latency));
|
| - _captureLatencyUs += (UInt32)((1.0e6 * latency)
|
| - / _inStreamFormat.mSampleRate);
|
| -
|
| - // Listen for format changes
|
| - // TODO(xians): should we be using kAudioDevicePropertyDeviceHasChanged?
|
| - propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectAddPropertyListener(_inputDeviceID,
|
| - &propertyAddress, &objectListenerProc, this));
|
| -
|
| - // Listen for processor overloads
|
| - propertyAddress.mSelector = kAudioDeviceProcessorOverload;
|
| - WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(_inputDeviceID,
|
| - &propertyAddress, &objectListenerProc, this));
|
| -
|
| - if (_twoDevices)
|
| - {
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(_inputDeviceID,
|
| - inDeviceIOProc, this, &_inDeviceIOProcID));
|
| - } else if (!_playIsInitialized)
|
| - {
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(_inputDeviceID,
|
| - deviceIOProc, this, &_deviceIOProcID));
|
| - }
|
| + propertyAddress.mSelector = kAudioDevicePropertyBufferSize;
|
| + size = sizeof(bufByteCount);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
|
| + _inputDeviceID, &propertyAddress, 0, NULL, size, &bufByteCount));
|
| +
|
| + // Get capture device latency
|
| + propertyAddress.mSelector = kAudioDevicePropertyLatency;
|
| + UInt32 latency = 0;
|
| + size = sizeof(UInt32);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + _inputDeviceID, &propertyAddress, 0, NULL, &size, &latency));
|
| + _captureLatencyUs = (UInt32)((1.0e6 * latency) / _inStreamFormat.mSampleRate);
|
| +
|
| + // Get capture stream latency
|
| + propertyAddress.mSelector = kAudioDevicePropertyStreams;
|
| + AudioStreamID stream = 0;
|
| + size = sizeof(AudioStreamID);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + _inputDeviceID, &propertyAddress, 0, NULL, &size, &stream));
|
| + propertyAddress.mSelector = kAudioStreamPropertyLatency;
|
| + size = sizeof(UInt32);
|
| + latency = 0;
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + _inputDeviceID, &propertyAddress, 0, NULL, &size, &latency));
|
| + _captureLatencyUs +=
|
| + (UInt32)((1.0e6 * latency) / _inStreamFormat.mSampleRate);
|
| +
|
| + // Listen for format changes
|
| + // TODO(xians): should we be using kAudioDevicePropertyDeviceHasChanged?
|
| + propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectAddPropertyListener(
|
| + _inputDeviceID, &propertyAddress, &objectListenerProc, this));
|
| +
|
| + // Listen for processor overloads
|
| + propertyAddress.mSelector = kAudioDeviceProcessorOverload;
|
| + WEBRTC_CA_LOG_WARN(AudioObjectAddPropertyListener(
|
| + _inputDeviceID, &propertyAddress, &objectListenerProc, this));
|
| +
|
| + if (_twoDevices) {
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(
|
| + _inputDeviceID, inDeviceIOProc, this, &_inDeviceIOProcID));
|
| + } else if (!_playIsInitialized) {
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioDeviceCreateIOProcID(
|
| + _inputDeviceID, deviceIOProc, this, &_deviceIOProcID));
|
| + }
|
|
|
| - // Mark recording side as initialized
|
| - _recIsInitialized = true;
|
| + // Mark recording side as initialized
|
| + _recIsInitialized = true;
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::StartRecording()
|
| -{
|
| -
|
| - CriticalSectionScoped lock(&_critSect);
|
| +int32_t AudioDeviceMac::StartRecording() {
|
| + CriticalSectionScoped lock(&_critSect);
|
|
|
| - if (!_recIsInitialized)
|
| - {
|
| - return -1;
|
| - }
|
| -
|
| - if (_recording)
|
| - {
|
| - return 0;
|
| - }
|
| + if (!_recIsInitialized) {
|
| + return -1;
|
| + }
|
|
|
| - if (!_initialized)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " Recording worker thread has not been started");
|
| - return -1;
|
| - }
|
| + if (_recording) {
|
| + return 0;
|
| + }
|
|
|
| - RTC_DCHECK(!capture_worker_thread_.get());
|
| - capture_worker_thread_.reset(
|
| - new rtc::PlatformThread(RunCapture, this, "CaptureWorkerThread"));
|
| - RTC_DCHECK(capture_worker_thread_.get());
|
| - capture_worker_thread_->Start();
|
| - capture_worker_thread_->SetPriority(rtc::kRealtimePriority);
|
| + if (!_initialized) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " Recording worker thread has not been started");
|
| + return -1;
|
| + }
|
|
|
| - OSStatus err = noErr;
|
| - if (_twoDevices)
|
| - {
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_inputDeviceID, _inDeviceIOProcID));
|
| - } else if (!_playing)
|
| - {
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_inputDeviceID, _deviceIOProcID));
|
| - }
|
| + RTC_DCHECK(!capture_worker_thread_.get());
|
| + capture_worker_thread_.reset(
|
| + new rtc::PlatformThread(RunCapture, this, "CaptureWorkerThread"));
|
| + RTC_DCHECK(capture_worker_thread_.get());
|
| + capture_worker_thread_->Start();
|
| + capture_worker_thread_->SetPriority(rtc::kRealtimePriority);
|
| +
|
| + OSStatus err = noErr;
|
| + if (_twoDevices) {
|
| + WEBRTC_CA_RETURN_ON_ERR(
|
| + AudioDeviceStart(_inputDeviceID, _inDeviceIOProcID));
|
| + } else if (!_playing) {
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_inputDeviceID, _deviceIOProcID));
|
| + }
|
|
|
| - _recording = true;
|
| + _recording = true;
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::StopRecording()
|
| -{
|
| -
|
| - CriticalSectionScoped lock(&_critSect);
|
| -
|
| - if (!_recIsInitialized)
|
| - {
|
| - return 0;
|
| - }
|
| +int32_t AudioDeviceMac::StopRecording() {
|
| + CriticalSectionScoped lock(&_critSect);
|
|
|
| - OSStatus err = noErr;
|
| + if (!_recIsInitialized) {
|
| + return 0;
|
| + }
|
|
|
| - // Stop device
|
| - int32_t captureDeviceIsAlive = AtomicGet32(&_captureDeviceIsAlive);
|
| - if (_twoDevices)
|
| - {
|
| - if (_recording && captureDeviceIsAlive == 1)
|
| - {
|
| - _recording = false;
|
| - _doStopRec = true; // Signal to io proc to stop audio device
|
| - _critSect.Leave(); // Cannot be under lock, risk of deadlock
|
| - if (kEventTimeout == _stopEventRec.Wait(2000))
|
| - {
|
| - CriticalSectionScoped critScoped(&_critSect);
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " Timed out stopping the capture IOProc. "
|
| - "We may have failed to detect a device removal.");
|
| -
|
| - WEBRTC_CA_LOG_WARN(AudioDeviceStop(_inputDeviceID,
|
| - _inDeviceIOProcID));
|
| - WEBRTC_CA_LOG_WARN(
|
| - AudioDeviceDestroyIOProcID(_inputDeviceID,
|
| - _inDeviceIOProcID));
|
| - }
|
| - _critSect.Enter();
|
| - _doStopRec = false;
|
| - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| - " Recording stopped");
|
| - }
|
| - }
|
| - else
|
| - {
|
| - // We signal a stop for a shared device even when rendering has
|
| - // not yet ended. This is to ensure the IOProc will return early as
|
| - // intended (by checking |_recording|) before accessing
|
| - // resources we free below (e.g. the capture converter).
|
| - //
|
| - // In the case of a shared devcie, the IOProc will verify
|
| - // rendering has ended before stopping itself.
|
| - if (_recording && captureDeviceIsAlive == 1)
|
| - {
|
| - _recording = false;
|
| - _doStop = true; // Signal to io proc to stop audio device
|
| - _critSect.Leave(); // Cannot be under lock, risk of deadlock
|
| - if (kEventTimeout == _stopEvent.Wait(2000))
|
| - {
|
| - CriticalSectionScoped critScoped(&_critSect);
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " Timed out stopping the shared IOProc. "
|
| - "We may have failed to detect a device removal.");
|
| -
|
| - // We assume rendering on a shared device has stopped as well if
|
| - // the IOProc times out.
|
| - WEBRTC_CA_LOG_WARN(AudioDeviceStop(_outputDeviceID,
|
| - _deviceIOProcID));
|
| - WEBRTC_CA_LOG_WARN(AudioDeviceDestroyIOProcID(_outputDeviceID,
|
| - _deviceIOProcID));
|
| - }
|
| - _critSect.Enter();
|
| - _doStop = false;
|
| - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| - " Recording stopped (shared)");
|
| - }
|
| + OSStatus err = noErr;
|
| +
|
| + // Stop device
|
| + int32_t captureDeviceIsAlive = AtomicGet32(&_captureDeviceIsAlive);
|
| + if (_twoDevices) {
|
| + if (_recording && captureDeviceIsAlive == 1) {
|
| + _recording = false;
|
| + _doStopRec = true; // Signal to io proc to stop audio device
|
| + _critSect.Leave(); // Cannot be under lock, risk of deadlock
|
| + if (kEventTimeout == _stopEventRec.Wait(2000)) {
|
| + CriticalSectionScoped critScoped(&_critSect);
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " Timed out stopping the capture IOProc. "
|
| + "We may have failed to detect a device removal.");
|
| +
|
| + WEBRTC_CA_LOG_WARN(AudioDeviceStop(_inputDeviceID, _inDeviceIOProcID));
|
| + WEBRTC_CA_LOG_WARN(
|
| + AudioDeviceDestroyIOProcID(_inputDeviceID, _inDeviceIOProcID));
|
| + }
|
| + _critSect.Enter();
|
| + _doStopRec = false;
|
| + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, " Recording stopped");
|
| + }
|
| + } else {
|
| + // We signal a stop for a shared device even when rendering has
|
| + // not yet ended. This is to ensure the IOProc will return early as
|
| + // intended (by checking |_recording|) before accessing
|
| + // resources we free below (e.g. the capture converter).
|
| + //
|
| + // In the case of a shared devcie, the IOProc will verify
|
| + // rendering has ended before stopping itself.
|
| + if (_recording && captureDeviceIsAlive == 1) {
|
| + _recording = false;
|
| + _doStop = true; // Signal to io proc to stop audio device
|
| + _critSect.Leave(); // Cannot be under lock, risk of deadlock
|
| + if (kEventTimeout == _stopEvent.Wait(2000)) {
|
| + CriticalSectionScoped critScoped(&_critSect);
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " Timed out stopping the shared IOProc. "
|
| + "We may have failed to detect a device removal.");
|
| +
|
| + // We assume rendering on a shared device has stopped as well if
|
| + // the IOProc times out.
|
| + WEBRTC_CA_LOG_WARN(AudioDeviceStop(_outputDeviceID, _deviceIOProcID));
|
| + WEBRTC_CA_LOG_WARN(
|
| + AudioDeviceDestroyIOProcID(_outputDeviceID, _deviceIOProcID));
|
| + }
|
| + _critSect.Enter();
|
| + _doStop = false;
|
| + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| + " Recording stopped (shared)");
|
| }
|
| + }
|
|
|
| - // Setting this signal will allow the worker thread to be stopped.
|
| - AtomicSet32(&_captureDeviceIsAlive, 0);
|
| + // Setting this signal will allow the worker thread to be stopped.
|
| + AtomicSet32(&_captureDeviceIsAlive, 0);
|
|
|
| - if (capture_worker_thread_.get()) {
|
| - _critSect.Leave();
|
| - capture_worker_thread_->Stop();
|
| - capture_worker_thread_.reset();
|
| - _critSect.Enter();
|
| - }
|
| + if (capture_worker_thread_.get()) {
|
| + _critSect.Leave();
|
| + capture_worker_thread_->Stop();
|
| + capture_worker_thread_.reset();
|
| + _critSect.Enter();
|
| + }
|
|
|
| - WEBRTC_CA_LOG_WARN(AudioConverterDispose(_captureConverter));
|
| + WEBRTC_CA_LOG_WARN(AudioConverterDispose(_captureConverter));
|
|
|
| - // Remove listeners.
|
| - AudioObjectPropertyAddress
|
| - propertyAddress = { kAudioDevicePropertyStreamFormat,
|
| - kAudioDevicePropertyScopeInput, 0 };
|
| - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(_inputDeviceID,
|
| - &propertyAddress, &objectListenerProc, this));
|
| + // Remove listeners.
|
| + AudioObjectPropertyAddress propertyAddress = {
|
| + kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeInput, 0};
|
| + WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
|
| + _inputDeviceID, &propertyAddress, &objectListenerProc, this));
|
|
|
| - propertyAddress.mSelector = kAudioDeviceProcessorOverload;
|
| - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(_inputDeviceID,
|
| - &propertyAddress, &objectListenerProc, this));
|
| + propertyAddress.mSelector = kAudioDeviceProcessorOverload;
|
| + WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
|
| + _inputDeviceID, &propertyAddress, &objectListenerProc, this));
|
|
|
| - _recIsInitialized = false;
|
| - _recording = false;
|
| + _recIsInitialized = false;
|
| + _recording = false;
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -bool AudioDeviceMac::RecordingIsInitialized() const
|
| -{
|
| - return (_recIsInitialized);
|
| +bool AudioDeviceMac::RecordingIsInitialized() const {
|
| + return (_recIsInitialized);
|
| }
|
|
|
| -bool AudioDeviceMac::Recording() const
|
| -{
|
| - return (_recording);
|
| +bool AudioDeviceMac::Recording() const {
|
| + return (_recording);
|
| }
|
|
|
| -bool AudioDeviceMac::PlayoutIsInitialized() const
|
| -{
|
| - return (_playIsInitialized);
|
| +bool AudioDeviceMac::PlayoutIsInitialized() const {
|
| + return (_playIsInitialized);
|
| }
|
|
|
| -int32_t AudioDeviceMac::StartPlayout()
|
| -{
|
| +int32_t AudioDeviceMac::StartPlayout() {
|
| + CriticalSectionScoped lock(&_critSect);
|
|
|
| - CriticalSectionScoped lock(&_critSect);
|
| -
|
| - if (!_playIsInitialized)
|
| - {
|
| - return -1;
|
| - }
|
| + if (!_playIsInitialized) {
|
| + return -1;
|
| + }
|
|
|
| - if (_playing)
|
| - {
|
| - return 0;
|
| - }
|
| + if (_playing) {
|
| + return 0;
|
| + }
|
|
|
| - RTC_DCHECK(!render_worker_thread_.get());
|
| - render_worker_thread_.reset(
|
| - new rtc::PlatformThread(RunRender, this, "RenderWorkerThread"));
|
| - render_worker_thread_->Start();
|
| - render_worker_thread_->SetPriority(rtc::kRealtimePriority);
|
| + RTC_DCHECK(!render_worker_thread_.get());
|
| + render_worker_thread_.reset(
|
| + new rtc::PlatformThread(RunRender, this, "RenderWorkerThread"));
|
| + render_worker_thread_->Start();
|
| + render_worker_thread_->SetPriority(rtc::kRealtimePriority);
|
|
|
| - if (_twoDevices || !_recording)
|
| - {
|
| - OSStatus err = noErr;
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_outputDeviceID, _deviceIOProcID));
|
| - }
|
| - _playing = true;
|
| + if (_twoDevices || !_recording) {
|
| + OSStatus err = noErr;
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioDeviceStart(_outputDeviceID, _deviceIOProcID));
|
| + }
|
| + _playing = true;
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::StopPlayout()
|
| -{
|
| +int32_t AudioDeviceMac::StopPlayout() {
|
| + CriticalSectionScoped lock(&_critSect);
|
| +
|
| + if (!_playIsInitialized) {
|
| + return 0;
|
| + }
|
|
|
| - CriticalSectionScoped lock(&_critSect);
|
| + OSStatus err = noErr;
|
|
|
| - if (!_playIsInitialized)
|
| - {
|
| - return 0;
|
| + int32_t renderDeviceIsAlive = AtomicGet32(&_renderDeviceIsAlive);
|
| + if (_playing && renderDeviceIsAlive == 1) {
|
| + // We signal a stop for a shared device even when capturing has not
|
| + // yet ended. This is to ensure the IOProc will return early as
|
| + // intended (by checking |_playing|) before accessing resources we
|
| + // free below (e.g. the render converter).
|
| + //
|
| + // In the case of a shared device, the IOProc will verify capturing
|
| + // has ended before stopping itself.
|
| + _playing = false;
|
| + _doStop = true; // Signal to io proc to stop audio device
|
| + _critSect.Leave(); // Cannot be under lock, risk of deadlock
|
| + if (kEventTimeout == _stopEvent.Wait(2000)) {
|
| + CriticalSectionScoped critScoped(&_critSect);
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " Timed out stopping the render IOProc. "
|
| + "We may have failed to detect a device removal.");
|
| +
|
| + // We assume capturing on a shared device has stopped as well if the
|
| + // IOProc times out.
|
| + WEBRTC_CA_LOG_WARN(AudioDeviceStop(_outputDeviceID, _deviceIOProcID));
|
| + WEBRTC_CA_LOG_WARN(
|
| + AudioDeviceDestroyIOProcID(_outputDeviceID, _deviceIOProcID));
|
| }
|
| + _critSect.Enter();
|
| + _doStop = false;
|
| + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, "Playout stopped");
|
| + }
|
|
|
| - OSStatus err = noErr;
|
| + // Setting this signal will allow the worker thread to be stopped.
|
| + AtomicSet32(&_renderDeviceIsAlive, 0);
|
| + if (render_worker_thread_.get()) {
|
| + _critSect.Leave();
|
| + render_worker_thread_->Stop();
|
| + render_worker_thread_.reset();
|
| + _critSect.Enter();
|
| + }
|
|
|
| - int32_t renderDeviceIsAlive = AtomicGet32(&_renderDeviceIsAlive);
|
| - if (_playing && renderDeviceIsAlive == 1)
|
| - {
|
| - // We signal a stop for a shared device even when capturing has not
|
| - // yet ended. This is to ensure the IOProc will return early as
|
| - // intended (by checking |_playing|) before accessing resources we
|
| - // free below (e.g. the render converter).
|
| - //
|
| - // In the case of a shared device, the IOProc will verify capturing
|
| - // has ended before stopping itself.
|
| - _playing = false;
|
| - _doStop = true; // Signal to io proc to stop audio device
|
| - _critSect.Leave(); // Cannot be under lock, risk of deadlock
|
| - if (kEventTimeout == _stopEvent.Wait(2000))
|
| - {
|
| - CriticalSectionScoped critScoped(&_critSect);
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " Timed out stopping the render IOProc. "
|
| - "We may have failed to detect a device removal.");
|
| -
|
| - // We assume capturing on a shared device has stopped as well if the
|
| - // IOProc times out.
|
| - WEBRTC_CA_LOG_WARN(AudioDeviceStop(_outputDeviceID,
|
| - _deviceIOProcID));
|
| - WEBRTC_CA_LOG_WARN(AudioDeviceDestroyIOProcID(_outputDeviceID,
|
| - _deviceIOProcID));
|
| - }
|
| - _critSect.Enter();
|
| - _doStop = false;
|
| - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| - "Playout stopped");
|
| - }
|
| + WEBRTC_CA_LOG_WARN(AudioConverterDispose(_renderConverter));
|
|
|
| - // Setting this signal will allow the worker thread to be stopped.
|
| - AtomicSet32(&_renderDeviceIsAlive, 0);
|
| - if (render_worker_thread_.get()) {
|
| - _critSect.Leave();
|
| - render_worker_thread_->Stop();
|
| - render_worker_thread_.reset();
|
| - _critSect.Enter();
|
| - }
|
| + // Remove listeners.
|
| + AudioObjectPropertyAddress propertyAddress = {
|
| + kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput, 0};
|
| + WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
|
| + _outputDeviceID, &propertyAddress, &objectListenerProc, this));
|
|
|
| - WEBRTC_CA_LOG_WARN(AudioConverterDispose(_renderConverter));
|
| + propertyAddress.mSelector = kAudioDeviceProcessorOverload;
|
| + WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
|
| + _outputDeviceID, &propertyAddress, &objectListenerProc, this));
|
|
|
| - // Remove listeners.
|
| - AudioObjectPropertyAddress propertyAddress = {
|
| - kAudioDevicePropertyStreamFormat, kAudioDevicePropertyScopeOutput,
|
| - 0 };
|
| - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(_outputDeviceID,
|
| - &propertyAddress, &objectListenerProc, this));
|
| -
|
| - propertyAddress.mSelector = kAudioDeviceProcessorOverload;
|
| - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(_outputDeviceID,
|
| - &propertyAddress, &objectListenerProc, this));
|
| -
|
| - if (_macBookPro)
|
| - {
|
| - Boolean hasProperty = AudioObjectHasProperty(_outputDeviceID,
|
| - &propertyAddress);
|
| - if (hasProperty)
|
| - {
|
| - propertyAddress.mSelector = kAudioDevicePropertyDataSource;
|
| - WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(_outputDeviceID,
|
| - &propertyAddress, &objectListenerProc, this));
|
| - }
|
| + if (_macBookPro) {
|
| + Boolean hasProperty =
|
| + AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
|
| + if (hasProperty) {
|
| + propertyAddress.mSelector = kAudioDevicePropertyDataSource;
|
| + WEBRTC_CA_LOG_WARN(AudioObjectRemovePropertyListener(
|
| + _outputDeviceID, &propertyAddress, &objectListenerProc, this));
|
| }
|
| + }
|
|
|
| - _playIsInitialized = false;
|
| - _playing = false;
|
| + _playIsInitialized = false;
|
| + _playing = false;
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::PlayoutDelay(uint16_t& delayMS) const
|
| -{
|
| - int32_t renderDelayUs = AtomicGet32(&_renderDelayUs);
|
| - delayMS = static_cast<uint16_t> (1e-3 * (renderDelayUs + _renderLatencyUs) +
|
| - 0.5);
|
| - return 0;
|
| +int32_t AudioDeviceMac::PlayoutDelay(uint16_t& delayMS) const {
|
| + int32_t renderDelayUs = AtomicGet32(&_renderDelayUs);
|
| + delayMS =
|
| + static_cast<uint16_t>(1e-3 * (renderDelayUs + _renderLatencyUs) + 0.5);
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::RecordingDelay(uint16_t& delayMS) const
|
| -{
|
| - int32_t captureDelayUs = AtomicGet32(&_captureDelayUs);
|
| - delayMS = static_cast<uint16_t> (1e-3 * (captureDelayUs +
|
| - _captureLatencyUs) + 0.5);
|
| - return 0;
|
| +int32_t AudioDeviceMac::RecordingDelay(uint16_t& delayMS) const {
|
| + int32_t captureDelayUs = AtomicGet32(&_captureDelayUs);
|
| + delayMS =
|
| + static_cast<uint16_t>(1e-3 * (captureDelayUs + _captureLatencyUs) + 0.5);
|
| + return 0;
|
| }
|
|
|
| -bool AudioDeviceMac::Playing() const
|
| -{
|
| - return (_playing);
|
| +bool AudioDeviceMac::Playing() const {
|
| + return (_playing);
|
| }
|
|
|
| int32_t AudioDeviceMac::SetPlayoutBuffer(
|
| const AudioDeviceModule::BufferType type,
|
| - uint16_t sizeMS)
|
| -{
|
| -
|
| - if (type != AudioDeviceModule::kFixedBufferSize)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " Adaptive buffer size not supported on this platform");
|
| - return -1;
|
| - }
|
| + uint16_t sizeMS) {
|
| + if (type != AudioDeviceModule::kFixedBufferSize) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " Adaptive buffer size not supported on this platform");
|
| + return -1;
|
| + }
|
|
|
| - _playBufType = type;
|
| - _playBufDelayFixed = sizeMS;
|
| - return 0;
|
| + _playBufType = type;
|
| + _playBufDelayFixed = sizeMS;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::PlayoutBuffer(
|
| - AudioDeviceModule::BufferType& type,
|
| - uint16_t& sizeMS) const
|
| -{
|
| -
|
| - type = _playBufType;
|
| - sizeMS = _playBufDelayFixed;
|
| +int32_t AudioDeviceMac::PlayoutBuffer(AudioDeviceModule::BufferType& type,
|
| + uint16_t& sizeMS) const {
|
| + type = _playBufType;
|
| + sizeMS = _playBufDelayFixed;
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| // Not implemented for Mac.
|
| -int32_t AudioDeviceMac::CPULoad(uint16_t& /*load*/) const
|
| -{
|
| -
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " API call not supported on this platform");
|
| +int32_t AudioDeviceMac::CPULoad(uint16_t& /*load*/) const {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " API call not supported on this platform");
|
|
|
| - return -1;
|
| + return -1;
|
| }
|
|
|
| -bool AudioDeviceMac::PlayoutWarning() const
|
| -{
|
| - return (_playWarning > 0);
|
| +bool AudioDeviceMac::PlayoutWarning() const {
|
| + return (_playWarning > 0);
|
| }
|
|
|
| -bool AudioDeviceMac::PlayoutError() const
|
| -{
|
| - return (_playError > 0);
|
| +bool AudioDeviceMac::PlayoutError() const {
|
| + return (_playError > 0);
|
| }
|
|
|
| -bool AudioDeviceMac::RecordingWarning() const
|
| -{
|
| - return (_recWarning > 0);
|
| +bool AudioDeviceMac::RecordingWarning() const {
|
| + return (_recWarning > 0);
|
| }
|
|
|
| -bool AudioDeviceMac::RecordingError() const
|
| -{
|
| - return (_recError > 0);
|
| +bool AudioDeviceMac::RecordingError() const {
|
| + return (_recError > 0);
|
| }
|
|
|
| -void AudioDeviceMac::ClearPlayoutWarning()
|
| -{
|
| - _playWarning = 0;
|
| +void AudioDeviceMac::ClearPlayoutWarning() {
|
| + _playWarning = 0;
|
| }
|
|
|
| -void AudioDeviceMac::ClearPlayoutError()
|
| -{
|
| - _playError = 0;
|
| +void AudioDeviceMac::ClearPlayoutError() {
|
| + _playError = 0;
|
| }
|
|
|
| -void AudioDeviceMac::ClearRecordingWarning()
|
| -{
|
| - _recWarning = 0;
|
| +void AudioDeviceMac::ClearRecordingWarning() {
|
| + _recWarning = 0;
|
| }
|
|
|
| -void AudioDeviceMac::ClearRecordingError()
|
| -{
|
| - _recError = 0;
|
| +void AudioDeviceMac::ClearRecordingError() {
|
| + _recError = 0;
|
| }
|
|
|
| // ============================================================================
|
| // Private Methods
|
| // ============================================================================
|
|
|
| -int32_t
|
| -AudioDeviceMac::GetNumberDevices(const AudioObjectPropertyScope scope,
|
| - AudioDeviceID scopedDeviceIds[],
|
| - const uint32_t deviceListLength)
|
| -{
|
| - OSStatus err = noErr;
|
| -
|
| - AudioObjectPropertyAddress propertyAddress = {
|
| - kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
|
| - kAudioObjectPropertyElementMaster };
|
| - UInt32 size = 0;
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyDataSize(kAudioObjectSystemObject,
|
| - &propertyAddress, 0, NULL, &size));
|
| - if (size == 0)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - "No devices");
|
| - return 0;
|
| - }
|
| -
|
| - AudioDeviceID* deviceIds = (AudioDeviceID*) malloc(size);
|
| - UInt32 numberDevices = size / sizeof(AudioDeviceID);
|
| - AudioBufferList* bufferList = NULL;
|
| - UInt32 numberScopedDevices = 0;
|
| +int32_t AudioDeviceMac::GetNumberDevices(const AudioObjectPropertyScope scope,
|
| + AudioDeviceID scopedDeviceIds[],
|
| + const uint32_t deviceListLength) {
|
| + OSStatus err = noErr;
|
| +
|
| + AudioObjectPropertyAddress propertyAddress = {
|
| + kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal,
|
| + kAudioObjectPropertyElementMaster};
|
| + UInt32 size = 0;
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyDataSize(
|
| + kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size));
|
| + if (size == 0) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "No devices");
|
| + return 0;
|
| + }
|
|
|
| - // First check if there is a default device and list it
|
| - UInt32 hardwareProperty = 0;
|
| - if (scope == kAudioDevicePropertyScopeOutput)
|
| - {
|
| - hardwareProperty = kAudioHardwarePropertyDefaultOutputDevice;
|
| - } else
|
| - {
|
| - hardwareProperty = kAudioHardwarePropertyDefaultInputDevice;
|
| - }
|
| + AudioDeviceID* deviceIds = (AudioDeviceID*)malloc(size);
|
| + UInt32 numberDevices = size / sizeof(AudioDeviceID);
|
| + AudioBufferList* bufferList = NULL;
|
| + UInt32 numberScopedDevices = 0;
|
| +
|
| + // First check if there is a default device and list it
|
| + UInt32 hardwareProperty = 0;
|
| + if (scope == kAudioDevicePropertyScopeOutput) {
|
| + hardwareProperty = kAudioHardwarePropertyDefaultOutputDevice;
|
| + } else {
|
| + hardwareProperty = kAudioHardwarePropertyDefaultInputDevice;
|
| + }
|
|
|
| - AudioObjectPropertyAddress
|
| - propertyAddressDefault = { hardwareProperty,
|
| - kAudioObjectPropertyScopeGlobal,
|
| - kAudioObjectPropertyElementMaster };
|
| -
|
| - AudioDeviceID usedID;
|
| - UInt32 uintSize = sizeof(UInt32);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
| - &propertyAddressDefault, 0, NULL, &uintSize, &usedID));
|
| - if (usedID != kAudioDeviceUnknown)
|
| - {
|
| - scopedDeviceIds[numberScopedDevices] = usedID;
|
| - numberScopedDevices++;
|
| - } else
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - "GetNumberDevices(): Default device unknown");
|
| - }
|
| + AudioObjectPropertyAddress propertyAddressDefault = {
|
| + hardwareProperty, kAudioObjectPropertyScopeGlobal,
|
| + kAudioObjectPropertyElementMaster};
|
| +
|
| + AudioDeviceID usedID;
|
| + UInt32 uintSize = sizeof(UInt32);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
| + &propertyAddressDefault, 0,
|
| + NULL, &uintSize, &usedID));
|
| + if (usedID != kAudioDeviceUnknown) {
|
| + scopedDeviceIds[numberScopedDevices] = usedID;
|
| + numberScopedDevices++;
|
| + } else {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + "GetNumberDevices(): Default device unknown");
|
| + }
|
|
|
| - // Then list the rest of the devices
|
| - bool listOK = true;
|
| + // Then list the rest of the devices
|
| + bool listOK = true;
|
|
|
| - WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
| - &propertyAddress, 0, NULL, &size, deviceIds));
|
| - if (err != noErr)
|
| - {
|
| + WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyData(
|
| + kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, deviceIds));
|
| + if (err != noErr) {
|
| + listOK = false;
|
| + } else {
|
| + propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
|
| + propertyAddress.mScope = scope;
|
| + propertyAddress.mElement = 0;
|
| + for (UInt32 i = 0; i < numberDevices; i++) {
|
| + // Check for input channels
|
| + WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyDataSize(
|
| + deviceIds[i], &propertyAddress, 0, NULL, &size));
|
| + if (err == kAudioHardwareBadDeviceError) {
|
| + // This device doesn't actually exist; continue iterating.
|
| + continue;
|
| + } else if (err != noErr) {
|
| listOK = false;
|
| - } else
|
| - {
|
| - propertyAddress.mSelector = kAudioDevicePropertyStreamConfiguration;
|
| - propertyAddress.mScope = scope;
|
| - propertyAddress.mElement = 0;
|
| - for (UInt32 i = 0; i < numberDevices; i++)
|
| - {
|
| - // Check for input channels
|
| - WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyDataSize(deviceIds[i],
|
| - &propertyAddress, 0, NULL, &size));
|
| - if (err == kAudioHardwareBadDeviceError)
|
| - {
|
| - // This device doesn't actually exist; continue iterating.
|
| - continue;
|
| - } else if (err != noErr)
|
| - {
|
| - listOK = false;
|
| - break;
|
| - }
|
| -
|
| - bufferList = (AudioBufferList*) malloc(size);
|
| - WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyData(deviceIds[i],
|
| - &propertyAddress, 0, NULL, &size, bufferList));
|
| - if (err != noErr)
|
| - {
|
| - listOK = false;
|
| - break;
|
| - }
|
| -
|
| - if (bufferList->mNumberBuffers > 0)
|
| - {
|
| - if (numberScopedDevices >= deviceListLength)
|
| - {
|
| - WEBRTC_TRACE(kTraceError,
|
| - kTraceAudioDevice, _id,
|
| - "Device list is not long enough");
|
| - listOK = false;
|
| - break;
|
| - }
|
| -
|
| - scopedDeviceIds[numberScopedDevices] = deviceIds[i];
|
| - numberScopedDevices++;
|
| - }
|
| -
|
| - free(bufferList);
|
| - bufferList = NULL;
|
| - } // for
|
| - }
|
| + break;
|
| + }
|
|
|
| - if (!listOK)
|
| - {
|
| - if (deviceIds)
|
| - {
|
| - free(deviceIds);
|
| - deviceIds = NULL;
|
| + bufferList = (AudioBufferList*)malloc(size);
|
| + WEBRTC_CA_LOG_ERR(AudioObjectGetPropertyData(
|
| + deviceIds[i], &propertyAddress, 0, NULL, &size, bufferList));
|
| + if (err != noErr) {
|
| + listOK = false;
|
| + break;
|
| + }
|
| +
|
| + if (bufferList->mNumberBuffers > 0) {
|
| + if (numberScopedDevices >= deviceListLength) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + "Device list is not long enough");
|
| + listOK = false;
|
| + break;
|
| }
|
|
|
| - if (bufferList)
|
| - {
|
| - free(bufferList);
|
| - bufferList = NULL;
|
| - }
|
| + scopedDeviceIds[numberScopedDevices] = deviceIds[i];
|
| + numberScopedDevices++;
|
| + }
|
| +
|
| + free(bufferList);
|
| + bufferList = NULL;
|
| + } // for
|
| + }
|
|
|
| - return -1;
|
| + if (!listOK) {
|
| + if (deviceIds) {
|
| + free(deviceIds);
|
| + deviceIds = NULL;
|
| }
|
|
|
| - // Happy ending
|
| - if (deviceIds)
|
| - {
|
| - free(deviceIds);
|
| - deviceIds = NULL;
|
| + if (bufferList) {
|
| + free(bufferList);
|
| + bufferList = NULL;
|
| }
|
|
|
| - return numberScopedDevices;
|
| + return -1;
|
| + }
|
| +
|
| + // Happy ending
|
| + if (deviceIds) {
|
| + free(deviceIds);
|
| + deviceIds = NULL;
|
| + }
|
| +
|
| + return numberScopedDevices;
|
| }
|
|
|
| -int32_t
|
| -AudioDeviceMac::GetDeviceName(const AudioObjectPropertyScope scope,
|
| - const uint16_t index,
|
| - char* name)
|
| -{
|
| - OSStatus err = noErr;
|
| - UInt32 len = kAdmMaxDeviceNameSize;
|
| - AudioDeviceID deviceIds[MaxNumberDevices];
|
| -
|
| - int numberDevices = GetNumberDevices(scope, deviceIds, MaxNumberDevices);
|
| - if (numberDevices < 0)
|
| - {
|
| - return -1;
|
| - } else if (numberDevices == 0)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - "No devices");
|
| - return -1;
|
| - }
|
| +int32_t AudioDeviceMac::GetDeviceName(const AudioObjectPropertyScope scope,
|
| + const uint16_t index,
|
| + char* name) {
|
| + OSStatus err = noErr;
|
| + UInt32 len = kAdmMaxDeviceNameSize;
|
| + AudioDeviceID deviceIds[MaxNumberDevices];
|
|
|
| - // If the number is below the number of devices, assume it's "WEBRTC ID"
|
| - // otherwise assume it's a CoreAudio ID
|
| - AudioDeviceID usedID;
|
| -
|
| - // Check if there is a default device
|
| - bool isDefaultDevice = false;
|
| - if (index == 0)
|
| - {
|
| - UInt32 hardwareProperty = 0;
|
| - if (scope == kAudioDevicePropertyScopeOutput)
|
| - {
|
| - hardwareProperty = kAudioHardwarePropertyDefaultOutputDevice;
|
| - } else
|
| - {
|
| - hardwareProperty = kAudioHardwarePropertyDefaultInputDevice;
|
| - }
|
| - AudioObjectPropertyAddress propertyAddress = { hardwareProperty,
|
| - kAudioObjectPropertyScopeGlobal,
|
| - kAudioObjectPropertyElementMaster };
|
| - UInt32 size = sizeof(UInt32);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
| - &propertyAddress, 0, NULL, &size, &usedID));
|
| - if (usedID == kAudioDeviceUnknown)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - "GetDeviceName(): Default device unknown");
|
| - } else
|
| - {
|
| - isDefaultDevice = true;
|
| - }
|
| - }
|
| + int numberDevices = GetNumberDevices(scope, deviceIds, MaxNumberDevices);
|
| + if (numberDevices < 0) {
|
| + return -1;
|
| + } else if (numberDevices == 0) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, "No devices");
|
| + return -1;
|
| + }
|
|
|
| + // If the number is below the number of devices, assume it's "WEBRTC ID"
|
| + // otherwise assume it's a CoreAudio ID
|
| + AudioDeviceID usedID;
|
| +
|
| + // Check if there is a default device
|
| + bool isDefaultDevice = false;
|
| + if (index == 0) {
|
| + UInt32 hardwareProperty = 0;
|
| + if (scope == kAudioDevicePropertyScopeOutput) {
|
| + hardwareProperty = kAudioHardwarePropertyDefaultOutputDevice;
|
| + } else {
|
| + hardwareProperty = kAudioHardwarePropertyDefaultInputDevice;
|
| + }
|
| AudioObjectPropertyAddress propertyAddress = {
|
| - kAudioDevicePropertyDeviceName, scope, 0 };
|
| -
|
| - if (isDefaultDevice)
|
| - {
|
| - char devName[len];
|
| -
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(usedID,
|
| - &propertyAddress, 0, NULL, &len, devName));
|
| -
|
| - sprintf(name, "default (%s)", devName);
|
| - } else
|
| - {
|
| - if (index < numberDevices)
|
| - {
|
| - usedID = deviceIds[index];
|
| - } else
|
| - {
|
| - usedID = index;
|
| - }
|
| + hardwareProperty, kAudioObjectPropertyScopeGlobal,
|
| + kAudioObjectPropertyElementMaster};
|
| + UInt32 size = sizeof(UInt32);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &usedID));
|
| + if (usedID == kAudioDeviceUnknown) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + "GetDeviceName(): Default device unknown");
|
| + } else {
|
| + isDefaultDevice = true;
|
| + }
|
| + }
|
| +
|
| + AudioObjectPropertyAddress propertyAddress = {kAudioDevicePropertyDeviceName,
|
| + scope, 0};
|
| +
|
| + if (isDefaultDevice) {
|
| + char devName[len];
|
|
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(usedID,
|
| - &propertyAddress, 0, NULL, &len, name));
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(usedID, &propertyAddress,
|
| + 0, NULL, &len, devName));
|
| +
|
| + sprintf(name, "default (%s)", devName);
|
| + } else {
|
| + if (index < numberDevices) {
|
| + usedID = deviceIds[index];
|
| + } else {
|
| + usedID = index;
|
| }
|
|
|
| - return 0;
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(usedID, &propertyAddress,
|
| + 0, NULL, &len, name));
|
| + }
|
| +
|
| + return 0;
|
| }
|
|
|
| int32_t AudioDeviceMac::InitDevice(const uint16_t userDeviceIndex,
|
| AudioDeviceID& deviceId,
|
| - const bool isInput)
|
| -{
|
| - OSStatus err = noErr;
|
| - UInt32 size = 0;
|
| - AudioObjectPropertyScope deviceScope;
|
| - AudioObjectPropertySelector defaultDeviceSelector;
|
| - AudioDeviceID deviceIds[MaxNumberDevices];
|
| -
|
| - if (isInput)
|
| - {
|
| - deviceScope = kAudioDevicePropertyScopeInput;
|
| - defaultDeviceSelector = kAudioHardwarePropertyDefaultInputDevice;
|
| - } else
|
| - {
|
| - deviceScope = kAudioDevicePropertyScopeOutput;
|
| - defaultDeviceSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
| - }
|
| + const bool isInput) {
|
| + OSStatus err = noErr;
|
| + UInt32 size = 0;
|
| + AudioObjectPropertyScope deviceScope;
|
| + AudioObjectPropertySelector defaultDeviceSelector;
|
| + AudioDeviceID deviceIds[MaxNumberDevices];
|
| +
|
| + if (isInput) {
|
| + deviceScope = kAudioDevicePropertyScopeInput;
|
| + defaultDeviceSelector = kAudioHardwarePropertyDefaultInputDevice;
|
| + } else {
|
| + deviceScope = kAudioDevicePropertyScopeOutput;
|
| + defaultDeviceSelector = kAudioHardwarePropertyDefaultOutputDevice;
|
| + }
|
|
|
| - AudioObjectPropertyAddress
|
| - propertyAddress = { defaultDeviceSelector,
|
| - kAudioObjectPropertyScopeGlobal,
|
| - kAudioObjectPropertyElementMaster };
|
| -
|
| - // Get the actual device IDs
|
| - int numberDevices = GetNumberDevices(deviceScope, deviceIds,
|
| - MaxNumberDevices);
|
| - if (numberDevices < 0)
|
| - {
|
| - return -1;
|
| - } else if (numberDevices == 0)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - "InitDevice(): No devices");
|
| - return -1;
|
| - }
|
| + AudioObjectPropertyAddress propertyAddress = {
|
| + defaultDeviceSelector, kAudioObjectPropertyScopeGlobal,
|
| + kAudioObjectPropertyElementMaster};
|
|
|
| - bool isDefaultDevice = false;
|
| - deviceId = kAudioDeviceUnknown;
|
| - if (userDeviceIndex == 0)
|
| - {
|
| - // Try to use default system device
|
| - size = sizeof(AudioDeviceID);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(kAudioObjectSystemObject,
|
| - &propertyAddress, 0, NULL, &size, &deviceId));
|
| - if (deviceId == kAudioDeviceUnknown)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " No default device exists");
|
| - } else
|
| - {
|
| - isDefaultDevice = true;
|
| - }
|
| - }
|
| + // Get the actual device IDs
|
| + int numberDevices =
|
| + GetNumberDevices(deviceScope, deviceIds, MaxNumberDevices);
|
| + if (numberDevices < 0) {
|
| + return -1;
|
| + } else if (numberDevices == 0) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + "InitDevice(): No devices");
|
| + return -1;
|
| + }
|
|
|
| - if (!isDefaultDevice)
|
| - {
|
| - deviceId = deviceIds[userDeviceIndex];
|
| + bool isDefaultDevice = false;
|
| + deviceId = kAudioDeviceUnknown;
|
| + if (userDeviceIndex == 0) {
|
| + // Try to use default system device
|
| + size = sizeof(AudioDeviceID);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + kAudioObjectSystemObject, &propertyAddress, 0, NULL, &size, &deviceId));
|
| + if (deviceId == kAudioDeviceUnknown) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " No default device exists");
|
| + } else {
|
| + isDefaultDevice = true;
|
| }
|
| + }
|
|
|
| - // Obtain device name and manufacturer for logging.
|
| - // Also use this as a test to ensure a user-set device ID is valid.
|
| - char devName[128];
|
| - char devManf[128];
|
| - memset(devName, 0, sizeof(devName));
|
| - memset(devManf, 0, sizeof(devManf));
|
| -
|
| - propertyAddress.mSelector = kAudioDevicePropertyDeviceName;
|
| - propertyAddress.mScope = deviceScope;
|
| - propertyAddress.mElement = 0;
|
| - size = sizeof(devName);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(deviceId,
|
| - &propertyAddress, 0, NULL, &size, devName));
|
| -
|
| - propertyAddress.mSelector = kAudioDevicePropertyDeviceManufacturer;
|
| - size = sizeof(devManf);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(deviceId,
|
| - &propertyAddress, 0, NULL, &size, devManf));
|
| + if (!isDefaultDevice) {
|
| + deviceId = deviceIds[userDeviceIndex];
|
| + }
|
|
|
| - if (isInput)
|
| - {
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - " Input device: %s %s", devManf, devName);
|
| - } else
|
| - {
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - " Output device: %s %s", devManf, devName);
|
| - }
|
| + // Obtain device name and manufacturer for logging.
|
| + // Also use this as a test to ensure a user-set device ID is valid.
|
| + char devName[128];
|
| + char devManf[128];
|
| + memset(devName, 0, sizeof(devName));
|
| + memset(devManf, 0, sizeof(devManf));
|
| +
|
| + propertyAddress.mSelector = kAudioDevicePropertyDeviceName;
|
| + propertyAddress.mScope = deviceScope;
|
| + propertyAddress.mElement = 0;
|
| + size = sizeof(devName);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(deviceId, &propertyAddress,
|
| + 0, NULL, &size, devName));
|
| +
|
| + propertyAddress.mSelector = kAudioDevicePropertyDeviceManufacturer;
|
| + size = sizeof(devManf);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(deviceId, &propertyAddress,
|
| + 0, NULL, &size, devManf));
|
| +
|
| + if (isInput) {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " Input device: %s %s",
|
| + devManf, devName);
|
| + } else {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, " Output device: %s %s",
|
| + devManf, devName);
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -OSStatus AudioDeviceMac::SetDesiredPlayoutFormat()
|
| -{
|
| - // Our preferred format to work with.
|
| - _outDesiredFormat.mSampleRate = N_PLAY_SAMPLES_PER_SEC;
|
| - _outDesiredFormat.mChannelsPerFrame = _playChannels;
|
| +OSStatus AudioDeviceMac::SetDesiredPlayoutFormat() {
|
| + // Our preferred format to work with.
|
| + _outDesiredFormat.mSampleRate = N_PLAY_SAMPLES_PER_SEC;
|
| + _outDesiredFormat.mChannelsPerFrame = _playChannels;
|
|
|
| - if (_ptrAudioBuffer)
|
| - {
|
| - // Update audio buffer with the selected parameters.
|
| - _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC);
|
| - _ptrAudioBuffer->SetPlayoutChannels((uint8_t) _playChannels);
|
| - }
|
| + if (_ptrAudioBuffer) {
|
| + // Update audio buffer with the selected parameters.
|
| + _ptrAudioBuffer->SetPlayoutSampleRate(N_PLAY_SAMPLES_PER_SEC);
|
| + _ptrAudioBuffer->SetPlayoutChannels((uint8_t)_playChannels);
|
| + }
|
|
|
| - _renderDelayOffsetSamples = _renderBufSizeSamples - N_BUFFERS_OUT *
|
| - ENGINE_PLAY_BUF_SIZE_IN_SAMPLES * _outDesiredFormat.mChannelsPerFrame;
|
| + _renderDelayOffsetSamples = _renderBufSizeSamples -
|
| + N_BUFFERS_OUT * ENGINE_PLAY_BUF_SIZE_IN_SAMPLES *
|
| + _outDesiredFormat.mChannelsPerFrame;
|
|
|
| - _outDesiredFormat.mBytesPerPacket = _outDesiredFormat.mChannelsPerFrame *
|
| - sizeof(SInt16);
|
| - // In uncompressed audio, a packet is one frame.
|
| - _outDesiredFormat.mFramesPerPacket = 1;
|
| - _outDesiredFormat.mBytesPerFrame = _outDesiredFormat.mChannelsPerFrame *
|
| - sizeof(SInt16);
|
| - _outDesiredFormat.mBitsPerChannel = sizeof(SInt16) * 8;
|
| + _outDesiredFormat.mBytesPerPacket =
|
| + _outDesiredFormat.mChannelsPerFrame * sizeof(SInt16);
|
| + // In uncompressed audio, a packet is one frame.
|
| + _outDesiredFormat.mFramesPerPacket = 1;
|
| + _outDesiredFormat.mBytesPerFrame =
|
| + _outDesiredFormat.mChannelsPerFrame * sizeof(SInt16);
|
| + _outDesiredFormat.mBitsPerChannel = sizeof(SInt16) * 8;
|
|
|
| - _outDesiredFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger |
|
| - kLinearPCMFormatFlagIsPacked;
|
| + _outDesiredFormat.mFormatFlags =
|
| + kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked;
|
| #ifdef WEBRTC_ARCH_BIG_ENDIAN
|
| - _outDesiredFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
|
| + _outDesiredFormat.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
|
| #endif
|
| - _outDesiredFormat.mFormatID = kAudioFormatLinearPCM;
|
| -
|
| - OSStatus err = noErr;
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(&_outDesiredFormat,
|
| - &_outStreamFormat,
|
| - &_renderConverter));
|
| -
|
| - // Try to set buffer size to desired value (_playBufDelayFixed).
|
| - UInt32 bufByteCount = static_cast<UInt32> ((_outStreamFormat.mSampleRate /
|
| - 1000.0) *
|
| - _playBufDelayFixed *
|
| - _outStreamFormat.mChannelsPerFrame *
|
| - sizeof(Float32));
|
| - if (_outStreamFormat.mFramesPerPacket != 0)
|
| - {
|
| - if (bufByteCount % _outStreamFormat.mFramesPerPacket != 0)
|
| - {
|
| - bufByteCount = (static_cast<UInt32> (bufByteCount /
|
| - _outStreamFormat.mFramesPerPacket) + 1) *
|
| - _outStreamFormat.mFramesPerPacket;
|
| - }
|
| - }
|
| -
|
| - // Ensure the buffer size is within the range provided by the device.
|
| - AudioObjectPropertyAddress propertyAddress =
|
| - {kAudioDevicePropertyDataSource,
|
| - kAudioDevicePropertyScopeOutput,
|
| - 0};
|
| - propertyAddress.mSelector = kAudioDevicePropertyBufferSizeRange;
|
| - AudioValueRange range;
|
| - UInt32 size = sizeof(range);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID,
|
| - &propertyAddress,
|
| - 0,
|
| - NULL,
|
| - &size,
|
| - &range));
|
| - if (range.mMinimum > bufByteCount)
|
| - {
|
| - bufByteCount = range.mMinimum;
|
| - } else if (range.mMaximum < bufByteCount)
|
| - {
|
| - bufByteCount = range.mMaximum;
|
| + _outDesiredFormat.mFormatID = kAudioFormatLinearPCM;
|
| +
|
| + OSStatus err = noErr;
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(
|
| + &_outDesiredFormat, &_outStreamFormat, &_renderConverter));
|
| +
|
| + // Try to set buffer size to desired value (_playBufDelayFixed).
|
| + UInt32 bufByteCount = static_cast<UInt32>(
|
| + (_outStreamFormat.mSampleRate / 1000.0) * _playBufDelayFixed *
|
| + _outStreamFormat.mChannelsPerFrame * sizeof(Float32));
|
| + if (_outStreamFormat.mFramesPerPacket != 0) {
|
| + if (bufByteCount % _outStreamFormat.mFramesPerPacket != 0) {
|
| + bufByteCount = (static_cast<UInt32>(bufByteCount /
|
| + _outStreamFormat.mFramesPerPacket) +
|
| + 1) *
|
| + _outStreamFormat.mFramesPerPacket;
|
| }
|
| + }
|
|
|
| - propertyAddress.mSelector = kAudioDevicePropertyBufferSize;
|
| - size = sizeof(bufByteCount);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(_outputDeviceID,
|
| - &propertyAddress,
|
| - 0,
|
| - NULL,
|
| - size,
|
| - &bufByteCount));
|
| -
|
| - // Get render device latency.
|
| - propertyAddress.mSelector = kAudioDevicePropertyLatency;
|
| - UInt32 latency = 0;
|
| - size = sizeof(UInt32);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID,
|
| - &propertyAddress,
|
| - 0,
|
| - NULL,
|
| - &size,
|
| - &latency));
|
| - _renderLatencyUs = static_cast<uint32_t> ((1.0e6 * latency) /
|
| - _outStreamFormat.mSampleRate);
|
| -
|
| - // Get render stream latency.
|
| - propertyAddress.mSelector = kAudioDevicePropertyStreams;
|
| - AudioStreamID stream = 0;
|
| - size = sizeof(AudioStreamID);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID,
|
| - &propertyAddress,
|
| - 0,
|
| - NULL,
|
| - &size,
|
| - &stream));
|
| - propertyAddress.mSelector = kAudioStreamPropertyLatency;
|
| - size = sizeof(UInt32);
|
| - latency = 0;
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(_outputDeviceID,
|
| - &propertyAddress,
|
| - 0,
|
| - NULL,
|
| - &size,
|
| - &latency));
|
| - _renderLatencyUs += static_cast<uint32_t> ((1.0e6 * latency) /
|
| - _outStreamFormat.mSampleRate);
|
| + // Ensure the buffer size is within the range provided by the device.
|
| + AudioObjectPropertyAddress propertyAddress = {
|
| + kAudioDevicePropertyDataSource, kAudioDevicePropertyScopeOutput, 0};
|
| + propertyAddress.mSelector = kAudioDevicePropertyBufferSizeRange;
|
| + AudioValueRange range;
|
| + UInt32 size = sizeof(range);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + _outputDeviceID, &propertyAddress, 0, NULL, &size, &range));
|
| + if (range.mMinimum > bufByteCount) {
|
| + bufByteCount = range.mMinimum;
|
| + } else if (range.mMaximum < bufByteCount) {
|
| + bufByteCount = range.mMaximum;
|
| + }
|
|
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - " initial playout status: _renderDelayOffsetSamples=%d,"
|
| - " _renderDelayUs=%d, _renderLatencyUs=%d",
|
| - _renderDelayOffsetSamples, _renderDelayUs, _renderLatencyUs);
|
| - return 0;
|
| + propertyAddress.mSelector = kAudioDevicePropertyBufferSize;
|
| + size = sizeof(bufByteCount);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
|
| + _outputDeviceID, &propertyAddress, 0, NULL, size, &bufByteCount));
|
| +
|
| + // Get render device latency.
|
| + propertyAddress.mSelector = kAudioDevicePropertyLatency;
|
| + UInt32 latency = 0;
|
| + size = sizeof(UInt32);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + _outputDeviceID, &propertyAddress, 0, NULL, &size, &latency));
|
| + _renderLatencyUs =
|
| + static_cast<uint32_t>((1.0e6 * latency) / _outStreamFormat.mSampleRate);
|
| +
|
| + // Get render stream latency.
|
| + propertyAddress.mSelector = kAudioDevicePropertyStreams;
|
| + AudioStreamID stream = 0;
|
| + size = sizeof(AudioStreamID);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + _outputDeviceID, &propertyAddress, 0, NULL, &size, &stream));
|
| + propertyAddress.mSelector = kAudioStreamPropertyLatency;
|
| + size = sizeof(UInt32);
|
| + latency = 0;
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + _outputDeviceID, &propertyAddress, 0, NULL, &size, &latency));
|
| + _renderLatencyUs +=
|
| + static_cast<uint32_t>((1.0e6 * latency) / _outStreamFormat.mSampleRate);
|
| +
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + " initial playout status: _renderDelayOffsetSamples=%d,"
|
| + " _renderDelayUs=%d, _renderLatencyUs=%d",
|
| + _renderDelayOffsetSamples, _renderDelayUs, _renderLatencyUs);
|
| + return 0;
|
| }
|
|
|
| OSStatus AudioDeviceMac::objectListenerProc(
|
| AudioObjectID objectId,
|
| UInt32 numberAddresses,
|
| const AudioObjectPropertyAddress addresses[],
|
| - void* clientData)
|
| -{
|
| - AudioDeviceMac *ptrThis = (AudioDeviceMac *) clientData;
|
| - RTC_DCHECK(ptrThis != NULL);
|
| + void* clientData) {
|
| + AudioDeviceMac* ptrThis = (AudioDeviceMac*)clientData;
|
| + RTC_DCHECK(ptrThis != NULL);
|
|
|
| - ptrThis->implObjectListenerProc(objectId, numberAddresses, addresses);
|
| + ptrThis->implObjectListenerProc(objectId, numberAddresses, addresses);
|
|
|
| - // AudioObjectPropertyListenerProc functions are supposed to return 0
|
| - return 0;
|
| + // AudioObjectPropertyListenerProc functions are supposed to return 0
|
| + return 0;
|
| }
|
|
|
| OSStatus AudioDeviceMac::implObjectListenerProc(
|
| const AudioObjectID objectId,
|
| const UInt32 numberAddresses,
|
| - const AudioObjectPropertyAddress addresses[])
|
| -{
|
| - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| - "AudioDeviceMac::implObjectListenerProc()");
|
| -
|
| - for (UInt32 i = 0; i < numberAddresses; i++)
|
| - {
|
| - if (addresses[i].mSelector == kAudioHardwarePropertyDevices)
|
| - {
|
| - HandleDeviceChange();
|
| - } else if (addresses[i].mSelector == kAudioDevicePropertyStreamFormat)
|
| - {
|
| - HandleStreamFormatChange(objectId, addresses[i]);
|
| - } else if (addresses[i].mSelector == kAudioDevicePropertyDataSource)
|
| - {
|
| - HandleDataSourceChange(objectId, addresses[i]);
|
| - } else if (addresses[i].mSelector == kAudioDeviceProcessorOverload)
|
| - {
|
| - HandleProcessorOverload(addresses[i]);
|
| - }
|
| + const AudioObjectPropertyAddress addresses[]) {
|
| + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| + "AudioDeviceMac::implObjectListenerProc()");
|
| +
|
| + for (UInt32 i = 0; i < numberAddresses; i++) {
|
| + if (addresses[i].mSelector == kAudioHardwarePropertyDevices) {
|
| + HandleDeviceChange();
|
| + } else if (addresses[i].mSelector == kAudioDevicePropertyStreamFormat) {
|
| + HandleStreamFormatChange(objectId, addresses[i]);
|
| + } else if (addresses[i].mSelector == kAudioDevicePropertyDataSource) {
|
| + HandleDataSourceChange(objectId, addresses[i]);
|
| + } else if (addresses[i].mSelector == kAudioDeviceProcessorOverload) {
|
| + HandleProcessorOverload(addresses[i]);
|
| }
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -int32_t AudioDeviceMac::HandleDeviceChange()
|
| -{
|
| - OSStatus err = noErr;
|
| +int32_t AudioDeviceMac::HandleDeviceChange() {
|
| + OSStatus err = noErr;
|
|
|
| - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| - "kAudioHardwarePropertyDevices");
|
| -
|
| - // A device has changed. Check if our registered devices have been removed.
|
| - // Ensure the devices have been initialized, meaning the IDs are valid.
|
| - if (MicrophoneIsInitialized())
|
| - {
|
| - AudioObjectPropertyAddress propertyAddress = {
|
| - kAudioDevicePropertyDeviceIsAlive,
|
| - kAudioDevicePropertyScopeInput, 0 };
|
| - UInt32 deviceIsAlive = 1;
|
| - UInt32 size = sizeof(UInt32);
|
| - err = AudioObjectGetPropertyData(_inputDeviceID, &propertyAddress, 0,
|
| - NULL, &size, &deviceIsAlive);
|
| -
|
| - if (err == kAudioHardwareBadDeviceError || deviceIsAlive == 0)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - "Capture device is not alive (probably removed)");
|
| - AtomicSet32(&_captureDeviceIsAlive, 0);
|
| - _mixerManager.CloseMicrophone();
|
| - if (_recError == 1)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice,
|
| - _id, " pending recording error exists");
|
| - }
|
| - _recError = 1; // triggers callback from module process thread
|
| - } else if (err != noErr)
|
| - {
|
| - logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| - "Error in AudioDeviceGetProperty()", (const char*) &err);
|
| - return -1;
|
| - }
|
| + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| + "kAudioHardwarePropertyDevices");
|
| +
|
| + // A device has changed. Check if our registered devices have been removed.
|
| + // Ensure the devices have been initialized, meaning the IDs are valid.
|
| + if (MicrophoneIsInitialized()) {
|
| + AudioObjectPropertyAddress propertyAddress = {
|
| + kAudioDevicePropertyDeviceIsAlive, kAudioDevicePropertyScopeInput, 0};
|
| + UInt32 deviceIsAlive = 1;
|
| + UInt32 size = sizeof(UInt32);
|
| + err = AudioObjectGetPropertyData(_inputDeviceID, &propertyAddress, 0, NULL,
|
| + &size, &deviceIsAlive);
|
| +
|
| + if (err == kAudioHardwareBadDeviceError || deviceIsAlive == 0) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + "Capture device is not alive (probably removed)");
|
| + AtomicSet32(&_captureDeviceIsAlive, 0);
|
| + _mixerManager.CloseMicrophone();
|
| + if (_recError == 1) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " pending recording error exists");
|
| + }
|
| + _recError = 1; // triggers callback from module process thread
|
| + } else if (err != noErr) {
|
| + logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| + "Error in AudioDeviceGetProperty()", (const char*)&err);
|
| + return -1;
|
| }
|
| + }
|
|
|
| - if (SpeakerIsInitialized())
|
| - {
|
| - AudioObjectPropertyAddress propertyAddress = {
|
| - kAudioDevicePropertyDeviceIsAlive,
|
| - kAudioDevicePropertyScopeOutput, 0 };
|
| - UInt32 deviceIsAlive = 1;
|
| - UInt32 size = sizeof(UInt32);
|
| - err = AudioObjectGetPropertyData(_outputDeviceID, &propertyAddress, 0,
|
| - NULL, &size, &deviceIsAlive);
|
| -
|
| - if (err == kAudioHardwareBadDeviceError || deviceIsAlive == 0)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - "Render device is not alive (probably removed)");
|
| - AtomicSet32(&_renderDeviceIsAlive, 0);
|
| - _mixerManager.CloseSpeaker();
|
| - if (_playError == 1)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice,
|
| - _id, " pending playout error exists");
|
| - }
|
| - _playError = 1; // triggers callback from module process thread
|
| - } else if (err != noErr)
|
| - {
|
| - logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| - "Error in AudioDeviceGetProperty()", (const char*) &err);
|
| - return -1;
|
| - }
|
| + if (SpeakerIsInitialized()) {
|
| + AudioObjectPropertyAddress propertyAddress = {
|
| + kAudioDevicePropertyDeviceIsAlive, kAudioDevicePropertyScopeOutput, 0};
|
| + UInt32 deviceIsAlive = 1;
|
| + UInt32 size = sizeof(UInt32);
|
| + err = AudioObjectGetPropertyData(_outputDeviceID, &propertyAddress, 0, NULL,
|
| + &size, &deviceIsAlive);
|
| +
|
| + if (err == kAudioHardwareBadDeviceError || deviceIsAlive == 0) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + "Render device is not alive (probably removed)");
|
| + AtomicSet32(&_renderDeviceIsAlive, 0);
|
| + _mixerManager.CloseSpeaker();
|
| + if (_playError == 1) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " pending playout error exists");
|
| + }
|
| + _playError = 1; // triggers callback from module process thread
|
| + } else if (err != noErr) {
|
| + logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| + "Error in AudioDeviceGetProperty()", (const char*)&err);
|
| + return -1;
|
| }
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| int32_t AudioDeviceMac::HandleStreamFormatChange(
|
| const AudioObjectID objectId,
|
| - const AudioObjectPropertyAddress propertyAddress)
|
| -{
|
| - OSStatus err = noErr;
|
| + const AudioObjectPropertyAddress propertyAddress) {
|
| + OSStatus err = noErr;
|
|
|
| - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| - "Stream format changed");
|
| -
|
| - if (objectId != _inputDeviceID && objectId != _outputDeviceID)
|
| - {
|
| - return 0;
|
| - }
|
| -
|
| - // Get the new device format
|
| - AudioStreamBasicDescription streamFormat;
|
| - UInt32 size = sizeof(streamFormat);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(objectId,
|
| - &propertyAddress, 0, NULL, &size, &streamFormat));
|
| -
|
| - if (streamFormat.mFormatID != kAudioFormatLinearPCM)
|
| - {
|
| - logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| - "Unacceptable input stream format -> mFormatID",
|
| - (const char *) &streamFormat.mFormatID);
|
| - return -1;
|
| - }
|
| + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, "Stream format changed");
|
|
|
| - if (streamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - "Too many channels on device (mChannelsPerFrame = %d)",
|
| - streamFormat.mChannelsPerFrame);
|
| - return -1;
|
| - }
|
| + if (objectId != _inputDeviceID && objectId != _outputDeviceID) {
|
| + return 0;
|
| + }
|
|
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "Stream format:");
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "mSampleRate = %f, mChannelsPerFrame = %u",
|
| - streamFormat.mSampleRate, streamFormat.mChannelsPerFrame);
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "mBytesPerPacket = %u, mFramesPerPacket = %u",
|
| - streamFormat.mBytesPerPacket, streamFormat.mFramesPerPacket);
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "mBytesPerFrame = %u, mBitsPerChannel = %u",
|
| - streamFormat.mBytesPerFrame, streamFormat.mBitsPerChannel);
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "mFormatFlags = %u",
|
| - streamFormat.mFormatFlags);
|
| - logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
| - (const char *) &streamFormat.mFormatID);
|
| -
|
| - if (propertyAddress.mScope == kAudioDevicePropertyScopeInput)
|
| - {
|
| - const int io_block_size_samples = streamFormat.mChannelsPerFrame *
|
| - streamFormat.mSampleRate / 100 * N_BLOCKS_IO;
|
| - if (io_block_size_samples > _captureBufSizeSamples)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - "Input IO block size (%d) is larger than ring buffer (%u)",
|
| - io_block_size_samples, _captureBufSizeSamples);
|
| - return -1;
|
| + // Get the new device format
|
| + AudioStreamBasicDescription streamFormat;
|
| + UInt32 size = sizeof(streamFormat);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + objectId, &propertyAddress, 0, NULL, &size, &streamFormat));
|
|
|
| - }
|
| + if (streamFormat.mFormatID != kAudioFormatLinearPCM) {
|
| + logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| + "Unacceptable input stream format -> mFormatID",
|
| + (const char*)&streamFormat.mFormatID);
|
| + return -1;
|
| + }
|
|
|
| - memcpy(&_inStreamFormat, &streamFormat, sizeof(streamFormat));
|
| -
|
| - if (_inStreamFormat.mChannelsPerFrame >= 2 && (_recChannels == 2))
|
| - {
|
| - _inDesiredFormat.mChannelsPerFrame = 2;
|
| - } else
|
| - {
|
| - // Disable stereo recording when we only have one channel on the device.
|
| - _inDesiredFormat.mChannelsPerFrame = 1;
|
| - _recChannels = 1;
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "Stereo recording unavailable on this device");
|
| - }
|
| + if (streamFormat.mChannelsPerFrame > N_DEVICE_CHANNELS) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + "Too many channels on device (mChannelsPerFrame = %d)",
|
| + streamFormat.mChannelsPerFrame);
|
| + return -1;
|
| + }
|
|
|
| - if (_ptrAudioBuffer)
|
| - {
|
| - // Update audio buffer with the selected parameters
|
| - _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC);
|
| - _ptrAudioBuffer->SetRecordingChannels((uint8_t) _recChannels);
|
| - }
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "Stream format:");
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "mSampleRate = %f, mChannelsPerFrame = %u",
|
| + streamFormat.mSampleRate, streamFormat.mChannelsPerFrame);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "mBytesPerPacket = %u, mFramesPerPacket = %u",
|
| + streamFormat.mBytesPerPacket, streamFormat.mFramesPerPacket);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "mBytesPerFrame = %u, mBitsPerChannel = %u",
|
| + streamFormat.mBytesPerFrame, streamFormat.mBitsPerChannel);
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "mFormatFlags = %u",
|
| + streamFormat.mFormatFlags);
|
| + logCAMsg(kTraceInfo, kTraceAudioDevice, _id, "mFormatID",
|
| + (const char*)&streamFormat.mFormatID);
|
| +
|
| + if (propertyAddress.mScope == kAudioDevicePropertyScopeInput) {
|
| + const int io_block_size_samples = streamFormat.mChannelsPerFrame *
|
| + streamFormat.mSampleRate / 100 *
|
| + N_BLOCKS_IO;
|
| + if (io_block_size_samples > _captureBufSizeSamples) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + "Input IO block size (%d) is larger than ring buffer (%u)",
|
| + io_block_size_samples, _captureBufSizeSamples);
|
| + return -1;
|
| + }
|
| +
|
| + memcpy(&_inStreamFormat, &streamFormat, sizeof(streamFormat));
|
| +
|
| + if (_inStreamFormat.mChannelsPerFrame >= 2 && (_recChannels == 2)) {
|
| + _inDesiredFormat.mChannelsPerFrame = 2;
|
| + } else {
|
| + // Disable stereo recording when we only have one channel on the device.
|
| + _inDesiredFormat.mChannelsPerFrame = 1;
|
| + _recChannels = 1;
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "Stereo recording unavailable on this device");
|
| + }
|
| +
|
| + if (_ptrAudioBuffer) {
|
| + // Update audio buffer with the selected parameters
|
| + _ptrAudioBuffer->SetRecordingSampleRate(N_REC_SAMPLES_PER_SEC);
|
| + _ptrAudioBuffer->SetRecordingChannels((uint8_t)_recChannels);
|
| + }
|
| +
|
| + // Recreate the converter with the new format
|
| + // TODO(xians): make this thread safe
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioConverterDispose(_captureConverter));
|
| +
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(&streamFormat, &_inDesiredFormat,
|
| + &_captureConverter));
|
| + } else {
|
| + memcpy(&_outStreamFormat, &streamFormat, sizeof(streamFormat));
|
|
|
| - // Recreate the converter with the new format
|
| - // TODO(xians): make this thread safe
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioConverterDispose(_captureConverter));
|
| -
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioConverterNew(&streamFormat, &_inDesiredFormat,
|
| - &_captureConverter));
|
| - } else
|
| - {
|
| - memcpy(&_outStreamFormat, &streamFormat, sizeof(streamFormat));
|
| -
|
| - // Our preferred format to work with
|
| - if (_outStreamFormat.mChannelsPerFrame < 2)
|
| - {
|
| - _playChannels = 1;
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "Stereo playout unavailable on this device");
|
| - }
|
| - WEBRTC_CA_RETURN_ON_ERR(SetDesiredPlayoutFormat());
|
| + // Our preferred format to work with
|
| + if (_outStreamFormat.mChannelsPerFrame < 2) {
|
| + _playChannels = 1;
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "Stereo playout unavailable on this device");
|
| }
|
| - return 0;
|
| + WEBRTC_CA_RETURN_ON_ERR(SetDesiredPlayoutFormat());
|
| + }
|
| + return 0;
|
| }
|
|
|
| int32_t AudioDeviceMac::HandleDataSourceChange(
|
| const AudioObjectID objectId,
|
| - const AudioObjectPropertyAddress propertyAddress)
|
| -{
|
| - OSStatus err = noErr;
|
| + const AudioObjectPropertyAddress propertyAddress) {
|
| + OSStatus err = noErr;
|
|
|
| - if (_macBookPro && propertyAddress.mScope
|
| - == kAudioDevicePropertyScopeOutput)
|
| - {
|
| - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| - "Data source changed");
|
| -
|
| - _macBookProPanRight = false;
|
| - UInt32 dataSource = 0;
|
| - UInt32 size = sizeof(UInt32);
|
| - WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(objectId,
|
| - &propertyAddress, 0, NULL, &size, &dataSource));
|
| - if (dataSource == 'ispk')
|
| - {
|
| - _macBookProPanRight = true;
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "MacBook Pro using internal speakers; stereo panning right");
|
| - } else
|
| - {
|
| - WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| - "MacBook Pro not using internal speakers");
|
| - }
|
| + if (_macBookPro &&
|
| + propertyAddress.mScope == kAudioDevicePropertyScopeOutput) {
|
| + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id, "Data source changed");
|
| +
|
| + _macBookProPanRight = false;
|
| + UInt32 dataSource = 0;
|
| + UInt32 size = sizeof(UInt32);
|
| + WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
|
| + objectId, &propertyAddress, 0, NULL, &size, &dataSource));
|
| + if (dataSource == 'ispk') {
|
| + _macBookProPanRight = true;
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "MacBook Pro using internal speakers; stereo panning right");
|
| + } else {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
|
| + "MacBook Pro not using internal speakers");
|
| }
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
| int32_t AudioDeviceMac::HandleProcessorOverload(
|
| - const AudioObjectPropertyAddress propertyAddress)
|
| -{
|
| - // TODO(xians): we probably want to notify the user in some way of the
|
| - // overload. However, the Windows interpretations of these errors seem to
|
| - // be more severe than what ProcessorOverload is thrown for.
|
| - //
|
| - // We don't log the notification, as it's sent from the HAL's IO thread. We
|
| - // don't want to slow it down even further.
|
| - if (propertyAddress.mScope == kAudioDevicePropertyScopeInput)
|
| - {
|
| - //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "Capture processor
|
| - // overload");
|
| - //_callback->ProblemIsReported(
|
| - // SndCardStreamObserver::ERecordingProblem);
|
| - } else
|
| - {
|
| - //WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - // "Render processor overload");
|
| - //_callback->ProblemIsReported(
|
| - // SndCardStreamObserver::EPlaybackProblem);
|
| - }
|
| + const AudioObjectPropertyAddress propertyAddress) {
|
| + // TODO(xians): we probably want to notify the user in some way of the
|
| + // overload. However, the Windows interpretations of these errors seem to
|
| + // be more severe than what ProcessorOverload is thrown for.
|
| + //
|
| + // We don't log the notification, as it's sent from the HAL's IO thread. We
|
| + // don't want to slow it down even further.
|
| + if (propertyAddress.mScope == kAudioDevicePropertyScopeInput) {
|
| + // WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "Capture processor
|
| + // overload");
|
| + //_callback->ProblemIsReported(
|
| + // SndCardStreamObserver::ERecordingProblem);
|
| + } else {
|
| + // WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + // "Render processor overload");
|
| + //_callback->ProblemIsReported(
|
| + // SndCardStreamObserver::EPlaybackProblem);
|
| + }
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| // ============================================================================
|
| // Thread Methods
|
| // ============================================================================
|
|
|
| -OSStatus AudioDeviceMac::deviceIOProc(AudioDeviceID, const AudioTimeStamp*,
|
| +OSStatus AudioDeviceMac::deviceIOProc(AudioDeviceID,
|
| + const AudioTimeStamp*,
|
| const AudioBufferList* inputData,
|
| const AudioTimeStamp* inputTime,
|
| AudioBufferList* outputData,
|
| const AudioTimeStamp* outputTime,
|
| - void *clientData)
|
| -{
|
| - AudioDeviceMac *ptrThis = (AudioDeviceMac *) clientData;
|
| - RTC_DCHECK(ptrThis != NULL);
|
| + void* clientData) {
|
| + AudioDeviceMac* ptrThis = (AudioDeviceMac*)clientData;
|
| + RTC_DCHECK(ptrThis != NULL);
|
|
|
| - ptrThis->implDeviceIOProc(inputData, inputTime, outputData, outputTime);
|
| + ptrThis->implDeviceIOProc(inputData, inputTime, outputData, outputTime);
|
|
|
| - // AudioDeviceIOProc functions are supposed to return 0
|
| - return 0;
|
| + // AudioDeviceIOProc functions are supposed to return 0
|
| + return 0;
|
| }
|
|
|
| OSStatus AudioDeviceMac::outConverterProc(AudioConverterRef,
|
| - UInt32 *numberDataPackets,
|
| - AudioBufferList *data,
|
| - AudioStreamPacketDescription **,
|
| - void *userData)
|
| -{
|
| - AudioDeviceMac *ptrThis = (AudioDeviceMac *) userData;
|
| - RTC_DCHECK(ptrThis != NULL);
|
| + UInt32* numberDataPackets,
|
| + AudioBufferList* data,
|
| + AudioStreamPacketDescription**,
|
| + void* userData) {
|
| + AudioDeviceMac* ptrThis = (AudioDeviceMac*)userData;
|
| + RTC_DCHECK(ptrThis != NULL);
|
|
|
| - return ptrThis->implOutConverterProc(numberDataPackets, data);
|
| + return ptrThis->implOutConverterProc(numberDataPackets, data);
|
| }
|
|
|
| -OSStatus AudioDeviceMac::inDeviceIOProc(AudioDeviceID, const AudioTimeStamp*,
|
| +OSStatus AudioDeviceMac::inDeviceIOProc(AudioDeviceID,
|
| + const AudioTimeStamp*,
|
| const AudioBufferList* inputData,
|
| const AudioTimeStamp* inputTime,
|
| AudioBufferList*,
|
| - const AudioTimeStamp*, void* clientData)
|
| -{
|
| - AudioDeviceMac *ptrThis = (AudioDeviceMac *) clientData;
|
| - RTC_DCHECK(ptrThis != NULL);
|
| + const AudioTimeStamp*,
|
| + void* clientData) {
|
| + AudioDeviceMac* ptrThis = (AudioDeviceMac*)clientData;
|
| + RTC_DCHECK(ptrThis != NULL);
|
|
|
| - ptrThis->implInDeviceIOProc(inputData, inputTime);
|
| + ptrThis->implInDeviceIOProc(inputData, inputTime);
|
|
|
| - // AudioDeviceIOProc functions are supposed to return 0
|
| - return 0;
|
| + // AudioDeviceIOProc functions are supposed to return 0
|
| + return 0;
|
| }
|
|
|
| OSStatus AudioDeviceMac::inConverterProc(
|
| AudioConverterRef,
|
| - UInt32 *numberDataPackets,
|
| - AudioBufferList *data,
|
| - AudioStreamPacketDescription ** /*dataPacketDescription*/,
|
| - void *userData)
|
| -{
|
| - AudioDeviceMac *ptrThis = static_cast<AudioDeviceMac*> (userData);
|
| - RTC_DCHECK(ptrThis != NULL);
|
| -
|
| - return ptrThis->implInConverterProc(numberDataPackets, data);
|
| -}
|
| -
|
| -OSStatus AudioDeviceMac::implDeviceIOProc(const AudioBufferList *inputData,
|
| - const AudioTimeStamp *inputTime,
|
| - AudioBufferList *outputData,
|
| - const AudioTimeStamp *outputTime)
|
| -{
|
| - OSStatus err = noErr;
|
| - UInt64 outputTimeNs = AudioConvertHostTimeToNanos(outputTime->mHostTime);
|
| - UInt64 nowNs = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
|
| -
|
| - if (!_twoDevices && _recording)
|
| - {
|
| - implInDeviceIOProc(inputData, inputTime);
|
| - }
|
| + UInt32* numberDataPackets,
|
| + AudioBufferList* data,
|
| + AudioStreamPacketDescription** /*dataPacketDescription*/,
|
| + void* userData) {
|
| + AudioDeviceMac* ptrThis = static_cast<AudioDeviceMac*>(userData);
|
| + RTC_DCHECK(ptrThis != NULL);
|
| +
|
| + return ptrThis->implInConverterProc(numberDataPackets, data);
|
| +}
|
| +
|
| +OSStatus AudioDeviceMac::implDeviceIOProc(const AudioBufferList* inputData,
|
| + const AudioTimeStamp* inputTime,
|
| + AudioBufferList* outputData,
|
| + const AudioTimeStamp* outputTime) {
|
| + OSStatus err = noErr;
|
| + UInt64 outputTimeNs = AudioConvertHostTimeToNanos(outputTime->mHostTime);
|
| + UInt64 nowNs = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
|
| +
|
| + if (!_twoDevices && _recording) {
|
| + implInDeviceIOProc(inputData, inputTime);
|
| + }
|
|
|
| - // Check if we should close down audio device
|
| - // Double-checked locking optimization to remove locking overhead
|
| - if (_doStop)
|
| - {
|
| - _critSect.Enter();
|
| - if (_doStop)
|
| - {
|
| - if (_twoDevices || (!_recording && !_playing))
|
| - {
|
| - // In the case of a shared device, the single driving ioProc
|
| - // is stopped here
|
| - WEBRTC_CA_LOG_ERR(AudioDeviceStop(_outputDeviceID,
|
| - _deviceIOProcID));
|
| - WEBRTC_CA_LOG_WARN(AudioDeviceDestroyIOProcID(_outputDeviceID,
|
| - _deviceIOProcID));
|
| - if (err == noErr)
|
| - {
|
| - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice,
|
| - _id, " Playout or shared device stopped");
|
| - }
|
| - }
|
| -
|
| - _doStop = false;
|
| - _stopEvent.Set();
|
| - _critSect.Leave();
|
| - return 0;
|
| + // Check if we should close down audio device
|
| + // Double-checked locking optimization to remove locking overhead
|
| + if (_doStop) {
|
| + _critSect.Enter();
|
| + if (_doStop) {
|
| + if (_twoDevices || (!_recording && !_playing)) {
|
| + // In the case of a shared device, the single driving ioProc
|
| + // is stopped here
|
| + WEBRTC_CA_LOG_ERR(AudioDeviceStop(_outputDeviceID, _deviceIOProcID));
|
| + WEBRTC_CA_LOG_WARN(
|
| + AudioDeviceDestroyIOProcID(_outputDeviceID, _deviceIOProcID));
|
| + if (err == noErr) {
|
| + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| + " Playout or shared device stopped");
|
| }
|
| - _critSect.Leave();
|
| - }
|
| + }
|
|
|
| - if (!_playing)
|
| - {
|
| - // This can be the case when a shared device is capturing but not
|
| - // rendering. We allow the checks above before returning to avoid a
|
| - // timeout when capturing is stopped.
|
| - return 0;
|
| + _doStop = false;
|
| + _stopEvent.Set();
|
| + _critSect.Leave();
|
| + return 0;
|
| }
|
| + _critSect.Leave();
|
| + }
|
|
|
| - RTC_DCHECK(_outStreamFormat.mBytesPerFrame != 0);
|
| - UInt32 size = outputData->mBuffers->mDataByteSize
|
| - / _outStreamFormat.mBytesPerFrame;
|
| -
|
| - // TODO(xians): signal an error somehow?
|
| - err = AudioConverterFillComplexBuffer(_renderConverter, outConverterProc,
|
| - this, &size, outputData, NULL);
|
| - if (err != noErr)
|
| - {
|
| - if (err == 1)
|
| - {
|
| - // This is our own error.
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " Error in AudioConverterFillComplexBuffer()");
|
| - return 1;
|
| - } else
|
| - {
|
| - logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| - "Error in AudioConverterFillComplexBuffer()",
|
| - (const char *) &err);
|
| - return 1;
|
| - }
|
| + if (!_playing) {
|
| + // This can be the case when a shared device is capturing but not
|
| + // rendering. We allow the checks above before returning to avoid a
|
| + // timeout when capturing is stopped.
|
| + return 0;
|
| + }
|
| +
|
| + RTC_DCHECK(_outStreamFormat.mBytesPerFrame != 0);
|
| + UInt32 size =
|
| + outputData->mBuffers->mDataByteSize / _outStreamFormat.mBytesPerFrame;
|
| +
|
| + // TODO(xians): signal an error somehow?
|
| + err = AudioConverterFillComplexBuffer(_renderConverter, outConverterProc,
|
| + this, &size, outputData, NULL);
|
| + if (err != noErr) {
|
| + if (err == 1) {
|
| + // This is our own error.
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " Error in AudioConverterFillComplexBuffer()");
|
| + return 1;
|
| + } else {
|
| + logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| + "Error in AudioConverterFillComplexBuffer()", (const char*)&err);
|
| + return 1;
|
| }
|
| + }
|
|
|
| - PaRingBufferSize bufSizeSamples =
|
| - PaUtil_GetRingBufferReadAvailable(_paRenderBuffer);
|
| + PaRingBufferSize bufSizeSamples =
|
| + PaUtil_GetRingBufferReadAvailable(_paRenderBuffer);
|
|
|
| - int32_t renderDelayUs = static_cast<int32_t> (1e-3 * (outputTimeNs - nowNs)
|
| - + 0.5);
|
| - renderDelayUs += static_cast<int32_t> ((1.0e6 * bufSizeSamples)
|
| - / _outDesiredFormat.mChannelsPerFrame / _outDesiredFormat.mSampleRate
|
| - + 0.5);
|
| + int32_t renderDelayUs =
|
| + static_cast<int32_t>(1e-3 * (outputTimeNs - nowNs) + 0.5);
|
| + renderDelayUs += static_cast<int32_t>(
|
| + (1.0e6 * bufSizeSamples) / _outDesiredFormat.mChannelsPerFrame /
|
| + _outDesiredFormat.mSampleRate +
|
| + 0.5);
|
|
|
| - AtomicSet32(&_renderDelayUs, renderDelayUs);
|
| + AtomicSet32(&_renderDelayUs, renderDelayUs);
|
|
|
| - return 0;
|
| + return 0;
|
| }
|
|
|
| -OSStatus AudioDeviceMac::implOutConverterProc(UInt32 *numberDataPackets,
|
| - AudioBufferList *data)
|
| -{
|
| +OSStatus AudioDeviceMac::implOutConverterProc(UInt32* numberDataPackets,
|
| + AudioBufferList* data) {
|
| RTC_DCHECK(data->mNumberBuffers == 1);
|
| - PaRingBufferSize numSamples = *numberDataPackets
|
| - * _outDesiredFormat.mChannelsPerFrame;
|
| -
|
| - data->mBuffers->mNumberChannels = _outDesiredFormat.mChannelsPerFrame;
|
| - // Always give the converter as much as it wants, zero padding as required.
|
| - data->mBuffers->mDataByteSize = *numberDataPackets
|
| - * _outDesiredFormat.mBytesPerPacket;
|
| - data->mBuffers->mData = _renderConvertData;
|
| - memset(_renderConvertData, 0, sizeof(_renderConvertData));
|
| -
|
| - PaUtil_ReadRingBuffer(_paRenderBuffer, _renderConvertData, numSamples);
|
| -
|
| - kern_return_t kernErr = semaphore_signal_all(_renderSemaphore);
|
| - if (kernErr != KERN_SUCCESS)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " semaphore_signal_all() error: %d", kernErr);
|
| - return 1;
|
| - }
|
| + PaRingBufferSize numSamples =
|
| + *numberDataPackets * _outDesiredFormat.mChannelsPerFrame;
|
|
|
| - return 0;
|
| + data->mBuffers->mNumberChannels = _outDesiredFormat.mChannelsPerFrame;
|
| + // Always give the converter as much as it wants, zero padding as required.
|
| + data->mBuffers->mDataByteSize =
|
| + *numberDataPackets * _outDesiredFormat.mBytesPerPacket;
|
| + data->mBuffers->mData = _renderConvertData;
|
| + memset(_renderConvertData, 0, sizeof(_renderConvertData));
|
| +
|
| + PaUtil_ReadRingBuffer(_paRenderBuffer, _renderConvertData, numSamples);
|
| +
|
| + kern_return_t kernErr = semaphore_signal_all(_renderSemaphore);
|
| + if (kernErr != KERN_SUCCESS) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " semaphore_signal_all() error: %d", kernErr);
|
| + return 1;
|
| + }
|
| +
|
| + return 0;
|
| }
|
|
|
| -OSStatus AudioDeviceMac::implInDeviceIOProc(const AudioBufferList *inputData,
|
| - const AudioTimeStamp *inputTime)
|
| -{
|
| - OSStatus err = noErr;
|
| - UInt64 inputTimeNs = AudioConvertHostTimeToNanos(inputTime->mHostTime);
|
| - UInt64 nowNs = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
|
| -
|
| - // Check if we should close down audio device
|
| - // Double-checked locking optimization to remove locking overhead
|
| - if (_doStopRec)
|
| - {
|
| - _critSect.Enter();
|
| - if (_doStopRec)
|
| - {
|
| - // This will be signalled only when a shared device is not in use.
|
| - WEBRTC_CA_LOG_ERR(AudioDeviceStop(_inputDeviceID, _inDeviceIOProcID));
|
| - WEBRTC_CA_LOG_WARN(AudioDeviceDestroyIOProcID(_inputDeviceID,
|
| - _inDeviceIOProcID));
|
| - if (err == noErr)
|
| - {
|
| - WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice,
|
| - _id, " Recording device stopped");
|
| - }
|
| -
|
| - _doStopRec = false;
|
| - _stopEventRec.Set();
|
| - _critSect.Leave();
|
| - return 0;
|
| - }
|
| - _critSect.Leave();
|
| - }
|
| +OSStatus AudioDeviceMac::implInDeviceIOProc(const AudioBufferList* inputData,
|
| + const AudioTimeStamp* inputTime) {
|
| + OSStatus err = noErr;
|
| + UInt64 inputTimeNs = AudioConvertHostTimeToNanos(inputTime->mHostTime);
|
| + UInt64 nowNs = AudioConvertHostTimeToNanos(AudioGetCurrentHostTime());
|
|
|
| - if (!_recording)
|
| - {
|
| - // Allow above checks to avoid a timeout on stopping capture.
|
| - return 0;
|
| - }
|
| + // Check if we should close down audio device
|
| + // Double-checked locking optimization to remove locking overhead
|
| + if (_doStopRec) {
|
| + _critSect.Enter();
|
| + if (_doStopRec) {
|
| + // This will be signalled only when a shared device is not in use.
|
| + WEBRTC_CA_LOG_ERR(AudioDeviceStop(_inputDeviceID, _inDeviceIOProcID));
|
| + WEBRTC_CA_LOG_WARN(
|
| + AudioDeviceDestroyIOProcID(_inputDeviceID, _inDeviceIOProcID));
|
| + if (err == noErr) {
|
| + WEBRTC_TRACE(kTraceDebug, kTraceAudioDevice, _id,
|
| + " Recording device stopped");
|
| + }
|
|
|
| - PaRingBufferSize bufSizeSamples =
|
| - PaUtil_GetRingBufferReadAvailable(_paCaptureBuffer);
|
| -
|
| - int32_t captureDelayUs = static_cast<int32_t> (1e-3 * (nowNs - inputTimeNs)
|
| - + 0.5);
|
| - captureDelayUs
|
| - += static_cast<int32_t> ((1.0e6 * bufSizeSamples)
|
| - / _inStreamFormat.mChannelsPerFrame / _inStreamFormat.mSampleRate
|
| - + 0.5);
|
| -
|
| - AtomicSet32(&_captureDelayUs, captureDelayUs);
|
| -
|
| - RTC_DCHECK(inputData->mNumberBuffers == 1);
|
| - PaRingBufferSize numSamples = inputData->mBuffers->mDataByteSize
|
| - * _inStreamFormat.mChannelsPerFrame / _inStreamFormat.mBytesPerPacket;
|
| - PaUtil_WriteRingBuffer(_paCaptureBuffer, inputData->mBuffers->mData,
|
| - numSamples);
|
| -
|
| - kern_return_t kernErr = semaphore_signal_all(_captureSemaphore);
|
| - if (kernErr != KERN_SUCCESS)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " semaphore_signal_all() error: %d", kernErr);
|
| + _doStopRec = false;
|
| + _stopEventRec.Set();
|
| + _critSect.Leave();
|
| + return 0;
|
| }
|
| + _critSect.Leave();
|
| + }
|
|
|
| - return err;
|
| -}
|
| + if (!_recording) {
|
| + // Allow above checks to avoid a timeout on stopping capture.
|
| + return 0;
|
| + }
|
|
|
| -OSStatus AudioDeviceMac::implInConverterProc(UInt32 *numberDataPackets,
|
| - AudioBufferList *data)
|
| -{
|
| - RTC_DCHECK(data->mNumberBuffers == 1);
|
| - PaRingBufferSize numSamples = *numberDataPackets
|
| - * _inStreamFormat.mChannelsPerFrame;
|
| -
|
| - while (PaUtil_GetRingBufferReadAvailable(_paCaptureBuffer) < numSamples)
|
| - {
|
| - mach_timespec_t timeout;
|
| - timeout.tv_sec = 0;
|
| - timeout.tv_nsec = TIMER_PERIOD_MS;
|
| -
|
| - kern_return_t kernErr = semaphore_timedwait(_captureSemaphore, timeout);
|
| - if (kernErr == KERN_OPERATION_TIMED_OUT)
|
| - {
|
| - int32_t signal = AtomicGet32(&_captureDeviceIsAlive);
|
| - if (signal == 0)
|
| - {
|
| - // The capture device is no longer alive; stop the worker thread.
|
| - *numberDataPackets = 0;
|
| - return 1;
|
| - }
|
| - } else if (kernErr != KERN_SUCCESS)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " semaphore_wait() error: %d", kernErr);
|
| - }
|
| - }
|
| + PaRingBufferSize bufSizeSamples =
|
| + PaUtil_GetRingBufferReadAvailable(_paCaptureBuffer);
|
|
|
| - // Pass the read pointer directly to the converter to avoid a memcpy.
|
| - void* dummyPtr;
|
| - PaRingBufferSize dummySize;
|
| - PaUtil_GetRingBufferReadRegions(_paCaptureBuffer, numSamples,
|
| - &data->mBuffers->mData, &numSamples,
|
| - &dummyPtr, &dummySize);
|
| - PaUtil_AdvanceRingBufferReadIndex(_paCaptureBuffer, numSamples);
|
| + int32_t captureDelayUs =
|
| + static_cast<int32_t>(1e-3 * (nowNs - inputTimeNs) + 0.5);
|
| + captureDelayUs += static_cast<int32_t>((1.0e6 * bufSizeSamples) /
|
| + _inStreamFormat.mChannelsPerFrame /
|
| + _inStreamFormat.mSampleRate +
|
| + 0.5);
|
|
|
| - data->mBuffers->mNumberChannels = _inStreamFormat.mChannelsPerFrame;
|
| - *numberDataPackets = numSamples / _inStreamFormat.mChannelsPerFrame;
|
| - data->mBuffers->mDataByteSize = *numberDataPackets
|
| - * _inStreamFormat.mBytesPerPacket;
|
| + AtomicSet32(&_captureDelayUs, captureDelayUs);
|
|
|
| - return 0;
|
| + RTC_DCHECK(inputData->mNumberBuffers == 1);
|
| + PaRingBufferSize numSamples = inputData->mBuffers->mDataByteSize *
|
| + _inStreamFormat.mChannelsPerFrame /
|
| + _inStreamFormat.mBytesPerPacket;
|
| + PaUtil_WriteRingBuffer(_paCaptureBuffer, inputData->mBuffers->mData,
|
| + numSamples);
|
| +
|
| + kern_return_t kernErr = semaphore_signal_all(_captureSemaphore);
|
| + if (kernErr != KERN_SUCCESS) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " semaphore_signal_all() error: %d", kernErr);
|
| + }
|
| +
|
| + return err;
|
| }
|
|
|
| -bool AudioDeviceMac::RunRender(void* ptrThis)
|
| -{
|
| - return static_cast<AudioDeviceMac*> (ptrThis)->RenderWorkerThread();
|
| -}
|
| -
|
| -bool AudioDeviceMac::RenderWorkerThread()
|
| -{
|
| - PaRingBufferSize numSamples = ENGINE_PLAY_BUF_SIZE_IN_SAMPLES
|
| - * _outDesiredFormat.mChannelsPerFrame;
|
| - while (PaUtil_GetRingBufferWriteAvailable(_paRenderBuffer)
|
| - - _renderDelayOffsetSamples < numSamples)
|
| - {
|
| - mach_timespec_t timeout;
|
| - timeout.tv_sec = 0;
|
| - timeout.tv_nsec = TIMER_PERIOD_MS;
|
| -
|
| - kern_return_t kernErr = semaphore_timedwait(_renderSemaphore, timeout);
|
| - if (kernErr == KERN_OPERATION_TIMED_OUT)
|
| - {
|
| - int32_t signal = AtomicGet32(&_renderDeviceIsAlive);
|
| - if (signal == 0)
|
| - {
|
| - // The render device is no longer alive; stop the worker thread.
|
| - return false;
|
| - }
|
| - } else if (kernErr != KERN_SUCCESS)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " semaphore_timedwait() error: %d", kernErr);
|
| - }
|
| +OSStatus AudioDeviceMac::implInConverterProc(UInt32* numberDataPackets,
|
| + AudioBufferList* data) {
|
| + RTC_DCHECK(data->mNumberBuffers == 1);
|
| + PaRingBufferSize numSamples =
|
| + *numberDataPackets * _inStreamFormat.mChannelsPerFrame;
|
| +
|
| + while (PaUtil_GetRingBufferReadAvailable(_paCaptureBuffer) < numSamples) {
|
| + mach_timespec_t timeout;
|
| + timeout.tv_sec = 0;
|
| + timeout.tv_nsec = TIMER_PERIOD_MS;
|
| +
|
| + kern_return_t kernErr = semaphore_timedwait(_captureSemaphore, timeout);
|
| + if (kernErr == KERN_OPERATION_TIMED_OUT) {
|
| + int32_t signal = AtomicGet32(&_captureDeviceIsAlive);
|
| + if (signal == 0) {
|
| + // The capture device is no longer alive; stop the worker thread.
|
| + *numberDataPackets = 0;
|
| + return 1;
|
| + }
|
| + } else if (kernErr != KERN_SUCCESS) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " semaphore_wait() error: %d", kernErr);
|
| }
|
| + }
|
|
|
| - int8_t playBuffer[4 * ENGINE_PLAY_BUF_SIZE_IN_SAMPLES];
|
| -
|
| - if (!_ptrAudioBuffer)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " capture AudioBuffer is invalid");
|
| + // Pass the read pointer directly to the converter to avoid a memcpy.
|
| + void* dummyPtr;
|
| + PaRingBufferSize dummySize;
|
| + PaUtil_GetRingBufferReadRegions(_paCaptureBuffer, numSamples,
|
| + &data->mBuffers->mData, &numSamples,
|
| + &dummyPtr, &dummySize);
|
| + PaUtil_AdvanceRingBufferReadIndex(_paCaptureBuffer, numSamples);
|
| +
|
| + data->mBuffers->mNumberChannels = _inStreamFormat.mChannelsPerFrame;
|
| + *numberDataPackets = numSamples / _inStreamFormat.mChannelsPerFrame;
|
| + data->mBuffers->mDataByteSize =
|
| + *numberDataPackets * _inStreamFormat.mBytesPerPacket;
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +bool AudioDeviceMac::RunRender(void* ptrThis) {
|
| + return static_cast<AudioDeviceMac*>(ptrThis)->RenderWorkerThread();
|
| +}
|
| +
|
| +bool AudioDeviceMac::RenderWorkerThread() {
|
| + PaRingBufferSize numSamples =
|
| + ENGINE_PLAY_BUF_SIZE_IN_SAMPLES * _outDesiredFormat.mChannelsPerFrame;
|
| + while (PaUtil_GetRingBufferWriteAvailable(_paRenderBuffer) -
|
| + _renderDelayOffsetSamples <
|
| + numSamples) {
|
| + mach_timespec_t timeout;
|
| + timeout.tv_sec = 0;
|
| + timeout.tv_nsec = TIMER_PERIOD_MS;
|
| +
|
| + kern_return_t kernErr = semaphore_timedwait(_renderSemaphore, timeout);
|
| + if (kernErr == KERN_OPERATION_TIMED_OUT) {
|
| + int32_t signal = AtomicGet32(&_renderDeviceIsAlive);
|
| + if (signal == 0) {
|
| + // The render device is no longer alive; stop the worker thread.
|
| return false;
|
| + }
|
| + } else if (kernErr != KERN_SUCCESS) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " semaphore_timedwait() error: %d", kernErr);
|
| }
|
| + }
|
|
|
| - // Ask for new PCM data to be played out using the AudioDeviceBuffer.
|
| - uint32_t nSamples =
|
| - _ptrAudioBuffer->RequestPlayoutData(ENGINE_PLAY_BUF_SIZE_IN_SAMPLES);
|
| + int8_t playBuffer[4 * ENGINE_PLAY_BUF_SIZE_IN_SAMPLES];
|
|
|
| - nSamples = _ptrAudioBuffer->GetPlayoutData(playBuffer);
|
| - if (nSamples != ENGINE_PLAY_BUF_SIZE_IN_SAMPLES)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " invalid number of output samples(%d)", nSamples);
|
| - }
|
| + if (!_ptrAudioBuffer) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " capture AudioBuffer is invalid");
|
| + return false;
|
| + }
|
|
|
| - uint32_t nOutSamples = nSamples * _outDesiredFormat.mChannelsPerFrame;
|
| -
|
| - SInt16 *pPlayBuffer = (SInt16 *) &playBuffer;
|
| - if (_macBookProPanRight && (_playChannels == 2))
|
| - {
|
| - // Mix entirely into the right channel and zero the left channel.
|
| - SInt32 sampleInt32 = 0;
|
| - for (uint32_t sampleIdx = 0; sampleIdx < nOutSamples; sampleIdx
|
| - += 2)
|
| - {
|
| - sampleInt32 = pPlayBuffer[sampleIdx];
|
| - sampleInt32 += pPlayBuffer[sampleIdx + 1];
|
| - sampleInt32 /= 2;
|
| -
|
| - if (sampleInt32 > 32767)
|
| - {
|
| - sampleInt32 = 32767;
|
| - } else if (sampleInt32 < -32768)
|
| - {
|
| - sampleInt32 = -32768;
|
| - }
|
| -
|
| - pPlayBuffer[sampleIdx] = 0;
|
| - pPlayBuffer[sampleIdx + 1] = static_cast<SInt16> (sampleInt32);
|
| - }
|
| + // Ask for new PCM data to be played out using the AudioDeviceBuffer.
|
| + uint32_t nSamples =
|
| + _ptrAudioBuffer->RequestPlayoutData(ENGINE_PLAY_BUF_SIZE_IN_SAMPLES);
|
| +
|
| + nSamples = _ptrAudioBuffer->GetPlayoutData(playBuffer);
|
| + if (nSamples != ENGINE_PLAY_BUF_SIZE_IN_SAMPLES) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " invalid number of output samples(%d)", nSamples);
|
| + }
|
| +
|
| + uint32_t nOutSamples = nSamples * _outDesiredFormat.mChannelsPerFrame;
|
| +
|
| + SInt16* pPlayBuffer = (SInt16*)&playBuffer;
|
| + if (_macBookProPanRight && (_playChannels == 2)) {
|
| + // Mix entirely into the right channel and zero the left channel.
|
| + SInt32 sampleInt32 = 0;
|
| + for (uint32_t sampleIdx = 0; sampleIdx < nOutSamples; sampleIdx += 2) {
|
| + sampleInt32 = pPlayBuffer[sampleIdx];
|
| + sampleInt32 += pPlayBuffer[sampleIdx + 1];
|
| + sampleInt32 /= 2;
|
| +
|
| + if (sampleInt32 > 32767) {
|
| + sampleInt32 = 32767;
|
| + } else if (sampleInt32 < -32768) {
|
| + sampleInt32 = -32768;
|
| + }
|
| +
|
| + pPlayBuffer[sampleIdx] = 0;
|
| + pPlayBuffer[sampleIdx + 1] = static_cast<SInt16>(sampleInt32);
|
| }
|
| + }
|
|
|
| - PaUtil_WriteRingBuffer(_paRenderBuffer, pPlayBuffer, nOutSamples);
|
| + PaUtil_WriteRingBuffer(_paRenderBuffer, pPlayBuffer, nOutSamples);
|
|
|
| - return true;
|
| + return true;
|
| }
|
|
|
| -bool AudioDeviceMac::RunCapture(void* ptrThis)
|
| -{
|
| - return static_cast<AudioDeviceMac*> (ptrThis)->CaptureWorkerThread();
|
| +bool AudioDeviceMac::RunCapture(void* ptrThis) {
|
| + return static_cast<AudioDeviceMac*>(ptrThis)->CaptureWorkerThread();
|
| }
|
|
|
| -bool AudioDeviceMac::CaptureWorkerThread()
|
| -{
|
| - OSStatus err = noErr;
|
| - UInt32 noRecSamples = ENGINE_REC_BUF_SIZE_IN_SAMPLES
|
| - * _inDesiredFormat.mChannelsPerFrame;
|
| - SInt16 recordBuffer[noRecSamples];
|
| - UInt32 size = ENGINE_REC_BUF_SIZE_IN_SAMPLES;
|
| -
|
| - AudioBufferList engineBuffer;
|
| - engineBuffer.mNumberBuffers = 1; // Interleaved channels.
|
| - engineBuffer.mBuffers->mNumberChannels = _inDesiredFormat.mChannelsPerFrame;
|
| - engineBuffer.mBuffers->mDataByteSize = _inDesiredFormat.mBytesPerPacket
|
| - * noRecSamples;
|
| - engineBuffer.mBuffers->mData = recordBuffer;
|
| -
|
| - err = AudioConverterFillComplexBuffer(_captureConverter, inConverterProc,
|
| - this, &size, &engineBuffer, NULL);
|
| - if (err != noErr)
|
| - {
|
| - if (err == 1)
|
| - {
|
| - // This is our own error.
|
| - return false;
|
| - } else
|
| - {
|
| - logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| - "Error in AudioConverterFillComplexBuffer()",
|
| - (const char *) &err);
|
| - return false;
|
| - }
|
| +bool AudioDeviceMac::CaptureWorkerThread() {
|
| + OSStatus err = noErr;
|
| + UInt32 noRecSamples =
|
| + ENGINE_REC_BUF_SIZE_IN_SAMPLES * _inDesiredFormat.mChannelsPerFrame;
|
| + SInt16 recordBuffer[noRecSamples];
|
| + UInt32 size = ENGINE_REC_BUF_SIZE_IN_SAMPLES;
|
| +
|
| + AudioBufferList engineBuffer;
|
| + engineBuffer.mNumberBuffers = 1; // Interleaved channels.
|
| + engineBuffer.mBuffers->mNumberChannels = _inDesiredFormat.mChannelsPerFrame;
|
| + engineBuffer.mBuffers->mDataByteSize =
|
| + _inDesiredFormat.mBytesPerPacket * noRecSamples;
|
| + engineBuffer.mBuffers->mData = recordBuffer;
|
| +
|
| + err = AudioConverterFillComplexBuffer(_captureConverter, inConverterProc,
|
| + this, &size, &engineBuffer, NULL);
|
| + if (err != noErr) {
|
| + if (err == 1) {
|
| + // This is our own error.
|
| + return false;
|
| + } else {
|
| + logCAMsg(kTraceError, kTraceAudioDevice, _id,
|
| + "Error in AudioConverterFillComplexBuffer()", (const char*)&err);
|
| + return false;
|
| }
|
| + }
|
|
|
| - // TODO(xians): what if the returned size is incorrect?
|
| - if (size == ENGINE_REC_BUF_SIZE_IN_SAMPLES)
|
| - {
|
| - uint32_t currentMicLevel(0);
|
| - uint32_t newMicLevel(0);
|
| - int32_t msecOnPlaySide;
|
| - int32_t msecOnRecordSide;
|
| -
|
| - int32_t captureDelayUs = AtomicGet32(&_captureDelayUs);
|
| - int32_t renderDelayUs = AtomicGet32(&_renderDelayUs);
|
| -
|
| - msecOnPlaySide = static_cast<int32_t> (1e-3 * (renderDelayUs +
|
| - _renderLatencyUs) + 0.5);
|
| - msecOnRecordSide = static_cast<int32_t> (1e-3 * (captureDelayUs +
|
| - _captureLatencyUs) +
|
| - 0.5);
|
| -
|
| - if (!_ptrAudioBuffer)
|
| - {
|
| - WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| - " capture AudioBuffer is invalid");
|
| - return false;
|
| - }
|
| + // TODO(xians): what if the returned size is incorrect?
|
| + if (size == ENGINE_REC_BUF_SIZE_IN_SAMPLES) {
|
| + uint32_t currentMicLevel(0);
|
| + uint32_t newMicLevel(0);
|
| + int32_t msecOnPlaySide;
|
| + int32_t msecOnRecordSide;
|
|
|
| - // store the recorded buffer (no action will be taken if the
|
| - // #recorded samples is not a full buffer)
|
| - _ptrAudioBuffer->SetRecordedBuffer((int8_t*) &recordBuffer,
|
| - (uint32_t) size);
|
| -
|
| - if (AGC())
|
| - {
|
| - // Use mod to ensure we check the volume on the first pass.
|
| - if (get_mic_volume_counter_ms_ % kGetMicVolumeIntervalMs == 0) {
|
| - get_mic_volume_counter_ms_ = 0;
|
| - // store current mic level in the audio buffer if AGC is enabled
|
| - if (MicrophoneVolume(currentMicLevel) == 0)
|
| - {
|
| - // this call does not affect the actual microphone volume
|
| - _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel);
|
| - }
|
| - }
|
| - get_mic_volume_counter_ms_ += kBufferSizeMs;
|
| - }
|
| + int32_t captureDelayUs = AtomicGet32(&_captureDelayUs);
|
| + int32_t renderDelayUs = AtomicGet32(&_renderDelayUs);
|
|
|
| - _ptrAudioBuffer->SetVQEData(msecOnPlaySide, msecOnRecordSide, 0);
|
| -
|
| - _ptrAudioBuffer->SetTypingStatus(KeyPressed());
|
| -
|
| - // deliver recorded samples at specified sample rate, mic level etc.
|
| - // to the observer using callback
|
| - _ptrAudioBuffer->DeliverRecordedData();
|
| -
|
| - if (AGC())
|
| - {
|
| - newMicLevel = _ptrAudioBuffer->NewMicLevel();
|
| - if (newMicLevel != 0)
|
| - {
|
| - // The VQE will only deliver non-zero microphone levels when
|
| - // a change is needed.
|
| - // Set this new mic level (received from the observer as return
|
| - // value in the callback).
|
| - WEBRTC_TRACE(kTraceStream, kTraceAudioDevice,
|
| - _id, " AGC change of volume: old=%u => new=%u",
|
| - currentMicLevel, newMicLevel);
|
| - if (SetMicrophoneVolume(newMicLevel) == -1)
|
| - {
|
| - WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| - " the required modification of the microphone "
|
| - "volume failed");
|
| - }
|
| - }
|
| + msecOnPlaySide =
|
| + static_cast<int32_t>(1e-3 * (renderDelayUs + _renderLatencyUs) + 0.5);
|
| + msecOnRecordSide =
|
| + static_cast<int32_t>(1e-3 * (captureDelayUs + _captureLatencyUs) + 0.5);
|
| +
|
| + if (!_ptrAudioBuffer) {
|
| + WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
|
| + " capture AudioBuffer is invalid");
|
| + return false;
|
| + }
|
| +
|
| + // store the recorded buffer (no action will be taken if the
|
| + // #recorded samples is not a full buffer)
|
| + _ptrAudioBuffer->SetRecordedBuffer((int8_t*)&recordBuffer, (uint32_t)size);
|
| +
|
| + if (AGC()) {
|
| + // Use mod to ensure we check the volume on the first pass.
|
| + if (get_mic_volume_counter_ms_ % kGetMicVolumeIntervalMs == 0) {
|
| + get_mic_volume_counter_ms_ = 0;
|
| + // store current mic level in the audio buffer if AGC is enabled
|
| + if (MicrophoneVolume(currentMicLevel) == 0) {
|
| + // this call does not affect the actual microphone volume
|
| + _ptrAudioBuffer->SetCurrentMicLevel(currentMicLevel);
|
| }
|
| + }
|
| + get_mic_volume_counter_ms_ += kBufferSizeMs;
|
| + }
|
| +
|
| + _ptrAudioBuffer->SetVQEData(msecOnPlaySide, msecOnRecordSide, 0);
|
| +
|
| + _ptrAudioBuffer->SetTypingStatus(KeyPressed());
|
| +
|
| + // deliver recorded samples at specified sample rate, mic level etc.
|
| + // to the observer using callback
|
| + _ptrAudioBuffer->DeliverRecordedData();
|
| +
|
| + if (AGC()) {
|
| + newMicLevel = _ptrAudioBuffer->NewMicLevel();
|
| + if (newMicLevel != 0) {
|
| + // The VQE will only deliver non-zero microphone levels when
|
| + // a change is needed.
|
| + // Set this new mic level (received from the observer as return
|
| + // value in the callback).
|
| + WEBRTC_TRACE(kTraceStream, kTraceAudioDevice, _id,
|
| + " AGC change of volume: old=%u => new=%u",
|
| + currentMicLevel, newMicLevel);
|
| + if (SetMicrophoneVolume(newMicLevel) == -1) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
|
| + " the required modification of the microphone "
|
| + "volume failed");
|
| + }
|
| + }
|
| }
|
| + }
|
|
|
| - return true;
|
| + return true;
|
| }
|
|
|
| bool AudioDeviceMac::KeyPressed() {
|
| bool key_down = false;
|
| // Loop through all Mac virtual key constant values.
|
| - for (unsigned int key_index = 0;
|
| - key_index < arraysize(prev_key_state_);
|
| - ++key_index) {
|
| - bool keyState = CGEventSourceKeyState(
|
| - kCGEventSourceStateHIDSystemState,
|
| - key_index);
|
| + for (unsigned int key_index = 0; key_index < arraysize(prev_key_state_);
|
| + ++key_index) {
|
| + bool keyState =
|
| + CGEventSourceKeyState(kCGEventSourceStateHIDSystemState, key_index);
|
| // A false -> true change in keymap means a key is pressed.
|
| key_down |= (keyState && !prev_key_state_[key_index]);
|
| // Save current state.
|
|
|