blob: e7e07546956ef46ab446442751add795efd4c835 [file] [log] [blame]
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/audio_device/mac/audio_mixer_manager_mac.h"
#include "webrtc/system_wrappers/include/trace.h"
#include <unistd.h> // getpid()
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)
AudioMixerManagerMac::AudioMixerManagerMac(const int32_t id)
: _critSect(*CriticalSectionWrapper::CreateCriticalSection()),
_id(id),
_inputDeviceID(kAudioObjectUnknown),
_outputDeviceID(kAudioObjectUnknown),
_noInputChannels(0),
_noOutputChannels(0) {
WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s constructed",
__FUNCTION__);
}
AudioMixerManagerMac::~AudioMixerManagerMac() {
WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s destructed",
__FUNCTION__);
Close();
delete &_critSect;
}
// ============================================================================
// PUBLIC METHODS
// ============================================================================
int32_t AudioMixerManagerMac::Close() {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
CriticalSectionScoped lock(&_critSect);
CloseSpeaker();
CloseMicrophone();
return 0;
}
int32_t AudioMixerManagerMac::CloseSpeaker() {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
CriticalSectionScoped lock(&_critSect);
_outputDeviceID = kAudioObjectUnknown;
_noOutputChannels = 0;
return 0;
}
int32_t AudioMixerManagerMac::CloseMicrophone() {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", __FUNCTION__);
CriticalSectionScoped lock(&_critSect);
_inputDeviceID = kAudioObjectUnknown;
_noInputChannels = 0;
return 0;
}
int32_t AudioMixerManagerMac::OpenSpeaker(AudioDeviceID deviceID) {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
"AudioMixerManagerMac::OpenSpeaker(id=%d)", deviceID);
CriticalSectionScoped lock(&_critSect);
OSStatus err = noErr;
UInt32 size = 0;
pid_t hogPid = -1;
_outputDeviceID = deviceID;
// Check which process, if any, has hogged the device.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeOutput, 0};
size = sizeof(hogPid);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_outputDeviceID, &propertyAddress, 0, NULL, &size, &hogPid));
if (hogPid == -1) {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
" No process has hogged the input device");
}
// getpid() is apparently "always successful"
else if (hogPid == getpid()) {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
" Our process has hogged the input device");
} else {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Another process (pid = %d) has hogged the input device",
static_cast<int>(hogPid));
return -1;
}
// get number of channels from stream format
propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
// Get the stream format, to be able to read the number of channels.
AudioStreamBasicDescription streamFormat;
size = sizeof(AudioStreamBasicDescription);
memset(&streamFormat, 0, size);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_outputDeviceID, &propertyAddress, 0, NULL, &size, &streamFormat));
_noOutputChannels = streamFormat.mChannelsPerFrame;
return 0;
}
int32_t AudioMixerManagerMac::OpenMicrophone(AudioDeviceID deviceID) {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
"AudioMixerManagerMac::OpenMicrophone(id=%d)", deviceID);
CriticalSectionScoped lock(&_critSect);
OSStatus err = noErr;
UInt32 size = 0;
pid_t hogPid = -1;
_inputDeviceID = deviceID;
// Check which process, if any, has hogged the device.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyHogMode, kAudioDevicePropertyScopeInput, 0};
size = sizeof(hogPid);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_inputDeviceID, &propertyAddress, 0, NULL, &size, &hogPid));
if (hogPid == -1) {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
" No process has hogged the input device");
}
// getpid() is apparently "always successful"
else if (hogPid == getpid()) {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
" Our process has hogged the input device");
} else {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Another process (pid = %d) has hogged the input device",
static_cast<int>(hogPid));
return -1;
}
// get number of channels from stream format
propertyAddress.mSelector = kAudioDevicePropertyStreamFormat;
// Get the stream format, to be able to read the number of channels.
AudioStreamBasicDescription streamFormat;
size = sizeof(AudioStreamBasicDescription);
memset(&streamFormat, 0, size);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_inputDeviceID, &propertyAddress, 0, NULL, &size, &streamFormat));
_noInputChannels = streamFormat.mChannelsPerFrame;
return 0;
}
bool AudioMixerManagerMac::SpeakerIsInitialized() const {
WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__);
return (_outputDeviceID != kAudioObjectUnknown);
}
bool AudioMixerManagerMac::MicrophoneIsInitialized() const {
WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", __FUNCTION__);
return (_inputDeviceID != kAudioObjectUnknown);
}
int32_t AudioMixerManagerMac::SetSpeakerVolume(uint32_t volume) {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
"AudioMixerManagerMac::SetSpeakerVolume(volume=%u)", volume);
CriticalSectionScoped lock(&_critSect);
if (_outputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
UInt32 size = 0;
bool success = false;
// volume range is 0.0 - 1.0, convert from 0 -255
const Float32 vol = (Float32)(volume / 255.0);
assert(vol <= 1.0 && vol >= 0.0);
// Does the capture device have a master volume control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 0};
Boolean isSettable = false;
err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
size = sizeof(vol);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
_outputDeviceID, &propertyAddress, 0, NULL, size, &vol));
return 0;
}
// Otherwise try to set each channel.
for (UInt32 i = 1; i <= _noOutputChannels; i++) {
propertyAddress.mElement = i;
isSettable = false;
err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
size = sizeof(vol);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
_outputDeviceID, &propertyAddress, 0, NULL, size, &vol));
}
success = true;
}
if (!success) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Unable to set a volume on any output channel");
return -1;
}
return 0;
}
int32_t AudioMixerManagerMac::SpeakerVolume(uint32_t& volume) const {
if (_outputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
UInt32 size = 0;
unsigned int channels = 0;
Float32 channelVol = 0;
Float32 vol = 0;
// Does the device have a master volume control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 0};
Boolean hasProperty =
AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
if (hasProperty) {
size = sizeof(vol);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_outputDeviceID, &propertyAddress, 0, NULL, &size, &vol));
// vol 0.0 to 1.0 -> convert to 0 - 255
volume = static_cast<uint32_t>(vol * 255 + 0.5);
} else {
// Otherwise get the average volume across channels.
vol = 0;
for (UInt32 i = 1; i <= _noOutputChannels; i++) {
channelVol = 0;
propertyAddress.mElement = i;
hasProperty = AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
if (hasProperty) {
size = sizeof(channelVol);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_outputDeviceID, &propertyAddress, 0, NULL, &size, &channelVol));
vol += channelVol;
channels++;
}
}
if (channels == 0) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Unable to get a volume on any channel");
return -1;
}
assert(channels > 0);
// vol 0.0 to 1.0 -> convert to 0 - 255
volume = static_cast<uint32_t>(255 * vol / channels + 0.5);
}
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
" AudioMixerManagerMac::SpeakerVolume() => vol=%i", vol);
return 0;
}
int32_t AudioMixerManagerMac::MaxSpeakerVolume(uint32_t& maxVolume) const {
if (_outputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
// volume range is 0.0 to 1.0
// we convert that to 0 - 255
maxVolume = 255;
return 0;
}
int32_t AudioMixerManagerMac::MinSpeakerVolume(uint32_t& minVolume) const {
if (_outputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
// volume range is 0.0 to 1.0
// we convert that to 0 - 255
minVolume = 0;
return 0;
}
int32_t AudioMixerManagerMac::SpeakerVolumeStepSize(uint16_t& stepSize) const {
if (_outputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
// volume range is 0.0 to 1.0
// we convert that to 0 - 255
stepSize = 1;
return 0;
}
int32_t AudioMixerManagerMac::SpeakerVolumeIsAvailable(bool& available) {
if (_outputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
// Does the capture device have a master volume control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeOutput, 0};
Boolean isSettable = false;
err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
available = true;
return 0;
}
// Otherwise try to set each channel.
for (UInt32 i = 1; i <= _noOutputChannels; i++) {
propertyAddress.mElement = i;
isSettable = false;
err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
&isSettable);
if (err != noErr || !isSettable) {
available = false;
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Volume cannot be set for output channel %d, err=%d", i,
err);
return -1;
}
}
available = true;
return 0;
}
int32_t AudioMixerManagerMac::SpeakerMuteIsAvailable(bool& available) {
if (_outputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
// Does the capture device have a master mute control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, 0};
Boolean isSettable = false;
err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
available = true;
return 0;
}
// Otherwise try to set each channel.
for (UInt32 i = 1; i <= _noOutputChannels; i++) {
propertyAddress.mElement = i;
isSettable = false;
err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
&isSettable);
if (err != noErr || !isSettable) {
available = false;
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Mute cannot be set for output channel %d, err=%d", i, err);
return -1;
}
}
available = true;
return 0;
}
int32_t AudioMixerManagerMac::SetSpeakerMute(bool enable) {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
"AudioMixerManagerMac::SetSpeakerMute(enable=%u)", enable);
CriticalSectionScoped lock(&_critSect);
if (_outputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
UInt32 size = 0;
UInt32 mute = enable ? 1 : 0;
bool success = false;
// Does the render device have a master mute control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, 0};
Boolean isSettable = false;
err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
size = sizeof(mute);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
_outputDeviceID, &propertyAddress, 0, NULL, size, &mute));
return 0;
}
// Otherwise try to set each channel.
for (UInt32 i = 1; i <= _noOutputChannels; i++) {
propertyAddress.mElement = i;
isSettable = false;
err = AudioObjectIsPropertySettable(_outputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
size = sizeof(mute);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
_outputDeviceID, &propertyAddress, 0, NULL, size, &mute));
}
success = true;
}
if (!success) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Unable to set mute on any input channel");
return -1;
}
return 0;
}
int32_t AudioMixerManagerMac::SpeakerMute(bool& enabled) const {
if (_outputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
UInt32 size = 0;
unsigned int channels = 0;
UInt32 channelMuted = 0;
UInt32 muted = 0;
// Does the device have a master volume control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyMute, kAudioDevicePropertyScopeOutput, 0};
Boolean hasProperty =
AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
if (hasProperty) {
size = sizeof(muted);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_outputDeviceID, &propertyAddress, 0, NULL, &size, &muted));
// 1 means muted
enabled = static_cast<bool>(muted);
} else {
// Otherwise check if all channels are muted.
for (UInt32 i = 1; i <= _noOutputChannels; i++) {
muted = 0;
propertyAddress.mElement = i;
hasProperty = AudioObjectHasProperty(_outputDeviceID, &propertyAddress);
if (hasProperty) {
size = sizeof(channelMuted);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_outputDeviceID, &propertyAddress, 0, NULL, &size, &channelMuted));
muted = (muted && channelMuted);
channels++;
}
}
if (channels == 0) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Unable to get mute for any channel");
return -1;
}
assert(channels > 0);
// 1 means muted
enabled = static_cast<bool>(muted);
}
WEBRTC_TRACE(
kTraceInfo, kTraceAudioDevice, _id,
" AudioMixerManagerMac::SpeakerMute() => enabled=%d, enabled");
return 0;
}
int32_t AudioMixerManagerMac::StereoPlayoutIsAvailable(bool& available) {
if (_outputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
available = (_noOutputChannels == 2);
return 0;
}
int32_t AudioMixerManagerMac::StereoRecordingIsAvailable(bool& available) {
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
available = (_noInputChannels == 2);
return 0;
}
int32_t AudioMixerManagerMac::MicrophoneMuteIsAvailable(bool& available) {
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
// Does the capture device have a master mute control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput, 0};
Boolean isSettable = false;
err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
available = true;
return 0;
}
// Otherwise try to set each channel.
for (UInt32 i = 1; i <= _noInputChannels; i++) {
propertyAddress.mElement = i;
isSettable = false;
err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
&isSettable);
if (err != noErr || !isSettable) {
available = false;
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Mute cannot be set for output channel %d, err=%d", i, err);
return -1;
}
}
available = true;
return 0;
}
int32_t AudioMixerManagerMac::SetMicrophoneMute(bool enable) {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
"AudioMixerManagerMac::SetMicrophoneMute(enable=%u)", enable);
CriticalSectionScoped lock(&_critSect);
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
UInt32 size = 0;
UInt32 mute = enable ? 1 : 0;
bool success = false;
// Does the capture device have a master mute control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput, 0};
Boolean isSettable = false;
err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
size = sizeof(mute);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
_inputDeviceID, &propertyAddress, 0, NULL, size, &mute));
return 0;
}
// Otherwise try to set each channel.
for (UInt32 i = 1; i <= _noInputChannels; i++) {
propertyAddress.mElement = i;
isSettable = false;
err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
size = sizeof(mute);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
_inputDeviceID, &propertyAddress, 0, NULL, size, &mute));
}
success = true;
}
if (!success) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Unable to set mute on any input channel");
return -1;
}
return 0;
}
int32_t AudioMixerManagerMac::MicrophoneMute(bool& enabled) const {
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
UInt32 size = 0;
unsigned int channels = 0;
UInt32 channelMuted = 0;
UInt32 muted = 0;
// Does the device have a master volume control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyMute, kAudioDevicePropertyScopeInput, 0};
Boolean hasProperty =
AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
if (hasProperty) {
size = sizeof(muted);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_inputDeviceID, &propertyAddress, 0, NULL, &size, &muted));
// 1 means muted
enabled = static_cast<bool>(muted);
} else {
// Otherwise check if all channels are muted.
for (UInt32 i = 1; i <= _noInputChannels; i++) {
muted = 0;
propertyAddress.mElement = i;
hasProperty = AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
if (hasProperty) {
size = sizeof(channelMuted);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_inputDeviceID, &propertyAddress, 0, NULL, &size, &channelMuted));
muted = (muted && channelMuted);
channels++;
}
}
if (channels == 0) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Unable to get mute for any channel");
return -1;
}
assert(channels > 0);
// 1 means muted
enabled = static_cast<bool>(muted);
}
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
" AudioMixerManagerMac::MicrophoneMute() => enabled=%d",
enabled);
return 0;
}
int32_t AudioMixerManagerMac::MicrophoneBoostIsAvailable(bool& available) {
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
available = false; // No AudioObjectPropertySelector value for Mic Boost
return 0;
}
int32_t AudioMixerManagerMac::SetMicrophoneBoost(bool enable) {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
"AudioMixerManagerMac::SetMicrophoneBoost(enable=%u)", enable);
CriticalSectionScoped lock(&_critSect);
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
// Ensure that the selected microphone has a valid boost control.
bool available(false);
MicrophoneBoostIsAvailable(available);
if (!available) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" it is not possible to enable microphone boost");
return -1;
}
// It is assumed that the call above fails!
return 0;
}
int32_t AudioMixerManagerMac::MicrophoneBoost(bool& enabled) const {
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
// Microphone boost cannot be enabled on this platform!
enabled = false;
return 0;
}
int32_t AudioMixerManagerMac::MicrophoneVolumeIsAvailable(bool& available) {
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
// Does the capture device have a master volume control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, 0};
Boolean isSettable = false;
err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
available = true;
return 0;
}
// Otherwise try to set each channel.
for (UInt32 i = 1; i <= _noInputChannels; i++) {
propertyAddress.mElement = i;
isSettable = false;
err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
&isSettable);
if (err != noErr || !isSettable) {
available = false;
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Volume cannot be set for input channel %d, err=%d", i,
err);
return -1;
}
}
available = true;
return 0;
}
int32_t AudioMixerManagerMac::SetMicrophoneVolume(uint32_t volume) {
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
"AudioMixerManagerMac::SetMicrophoneVolume(volume=%u)", volume);
CriticalSectionScoped lock(&_critSect);
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
UInt32 size = 0;
bool success = false;
// volume range is 0.0 - 1.0, convert from 0 - 255
const Float32 vol = (Float32)(volume / 255.0);
assert(vol <= 1.0 && vol >= 0.0);
// Does the capture device have a master volume control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, 0};
Boolean isSettable = false;
err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
size = sizeof(vol);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
_inputDeviceID, &propertyAddress, 0, NULL, size, &vol));
return 0;
}
// Otherwise try to set each channel.
for (UInt32 i = 1; i <= _noInputChannels; i++) {
propertyAddress.mElement = i;
isSettable = false;
err = AudioObjectIsPropertySettable(_inputDeviceID, &propertyAddress,
&isSettable);
if (err == noErr && isSettable) {
size = sizeof(vol);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectSetPropertyData(
_inputDeviceID, &propertyAddress, 0, NULL, size, &vol));
}
success = true;
}
if (!success) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Unable to set a level on any input channel");
return -1;
}
return 0;
}
int32_t AudioMixerManagerMac::MicrophoneVolume(uint32_t& volume) const {
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
OSStatus err = noErr;
UInt32 size = 0;
unsigned int channels = 0;
Float32 channelVol = 0;
Float32 volFloat32 = 0;
// Does the device have a master volume control?
// If so, use it exclusively.
AudioObjectPropertyAddress propertyAddress = {
kAudioDevicePropertyVolumeScalar, kAudioDevicePropertyScopeInput, 0};
Boolean hasProperty =
AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
if (hasProperty) {
size = sizeof(volFloat32);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_inputDeviceID, &propertyAddress, 0, NULL, &size, &volFloat32));
// vol 0.0 to 1.0 -> convert to 0 - 255
volume = static_cast<uint32_t>(volFloat32 * 255 + 0.5);
} else {
// Otherwise get the average volume across channels.
volFloat32 = 0;
for (UInt32 i = 1; i <= _noInputChannels; i++) {
channelVol = 0;
propertyAddress.mElement = i;
hasProperty = AudioObjectHasProperty(_inputDeviceID, &propertyAddress);
if (hasProperty) {
size = sizeof(channelVol);
WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData(
_inputDeviceID, &propertyAddress, 0, NULL, &size, &channelVol));
volFloat32 += channelVol;
channels++;
}
}
if (channels == 0) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" Unable to get a level on any channel");
return -1;
}
assert(channels > 0);
// vol 0.0 to 1.0 -> convert to 0 - 255
volume = static_cast<uint32_t>(255 * volFloat32 / channels + 0.5);
}
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
" AudioMixerManagerMac::MicrophoneVolume() => vol=%u",
volume);
return 0;
}
int32_t AudioMixerManagerMac::MaxMicrophoneVolume(uint32_t& maxVolume) const {
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
// volume range is 0.0 to 1.0
// we convert that to 0 - 255
maxVolume = 255;
return 0;
}
int32_t AudioMixerManagerMac::MinMicrophoneVolume(uint32_t& minVolume) const {
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
// volume range is 0.0 to 1.0
// we convert that to 0 - 10
minVolume = 0;
return 0;
}
int32_t AudioMixerManagerMac::MicrophoneVolumeStepSize(
uint16_t& stepSize) const {
if (_inputDeviceID == kAudioObjectUnknown) {
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id,
" device ID has not been set");
return -1;
}
// volume range is 0.0 to 1.0
// we convert that to 0 - 10
stepSize = 1;
return 0;
}
// ============================================================================
// Private Methods
// ============================================================================
// CoreAudio errors are best interpreted as four character strings.
void AudioMixerManagerMac::logCAMsg(const TraceLevel level,
const TraceModule module,
const int32_t id,
const char* msg,
const char* err) {
assert(msg != NULL);
assert(err != NULL);
#ifdef WEBRTC_ARCH_BIG_ENDIAN
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);
#endif
}
} // namespace webrtc
// EOF