blob: 40850d478114dd6965ee033ffaa621e61b6f8a5f [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 "audio_mixer_manager_mac.h"
#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 WebRtc_Word32 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
// ============================================================================
WebRtc_Word32 AudioMixerManagerMac::Close()
{
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
__FUNCTION__);
CriticalSectionScoped lock(_critSect);
CloseSpeaker();
CloseMicrophone();
return 0;
}
WebRtc_Word32 AudioMixerManagerMac::CloseSpeaker()
{
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
__FUNCTION__);
CriticalSectionScoped lock(_critSect);
_outputDeviceID = kAudioObjectUnknown;
_noOutputChannels = 0;
return 0;
}
WebRtc_Word32 AudioMixerManagerMac::CloseMicrophone()
{
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s",
__FUNCTION__);
CriticalSectionScoped lock(_critSect);
_inputDeviceID = kAudioObjectUnknown;
_noInputChannels = 0;
return 0;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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);
}
WebRtc_Word32 AudioMixerManagerMac::SetSpeakerVolume(WebRtc_UWord32 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;
}
WebRtc_Word32 AudioMixerManagerMac::SpeakerVolume(WebRtc_UWord32& 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<WebRtc_UWord32> (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<WebRtc_UWord32> (255 * vol / channels + 0.5);
}
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
" AudioMixerManagerMac::SpeakerVolume() => vol=%i", vol);
return 0;
}
WebRtc_Word32
AudioMixerManagerMac::MaxSpeakerVolume(WebRtc_UWord32& 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;
}
WebRtc_Word32
AudioMixerManagerMac::MinSpeakerVolume(WebRtc_UWord32& 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;
}
WebRtc_Word32
AudioMixerManagerMac::SpeakerVolumeStepSize(WebRtc_UWord16& 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 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;
}
WebRtc_Word32 AudioMixerManagerMac::SetMicrophoneVolume(WebRtc_UWord32 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;
}
WebRtc_Word32
AudioMixerManagerMac::MicrophoneVolume(WebRtc_UWord32& 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<WebRtc_UWord32> (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<WebRtc_UWord32>
(255 * volFloat32 / channels + 0.5);
}
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id,
" AudioMixerManagerMac::MicrophoneVolume() => vol=%u",
volume);
return 0;
}
WebRtc_Word32
AudioMixerManagerMac::MaxMicrophoneVolume(WebRtc_UWord32& 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;
}
WebRtc_Word32
AudioMixerManagerMac::MinMicrophoneVolume(WebRtc_UWord32& 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;
}
WebRtc_Word32
AudioMixerManagerMac::MicrophoneVolumeStepSize(WebRtc_UWord16& 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 WebRtc_Word32 id, const char *msg,
const char *err)
{
assert(msg != NULL);
assert(err != NULL);
#ifdef WEBRTC_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