/* * 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 "modules/audio_device/mac/audio_mixer_manager_mac.h" #include // getpid() #include "rtc_base/system/arch.h" namespace webrtc { #define WEBRTC_CA_RETURN_ON_ERR(expr) \ do { \ err = expr; \ if (err != noErr) { \ logCAMsg(rtc::LS_ERROR, "Error in " #expr, (const char*)&err); \ return -1; \ } \ } while (0) #define WEBRTC_CA_LOG_ERR(expr) \ do { \ err = expr; \ if (err != noErr) { \ logCAMsg(rtc::LS_ERROR, "Error in " #expr, (const char*)&err); \ } \ } while (0) #define WEBRTC_CA_LOG_WARN(expr) \ do { \ err = expr; \ if (err != noErr) { \ logCAMsg(rtc::LS_WARNING, "Error in " #expr, (const char*)&err); \ } \ } while (0) AudioMixerManagerMac::AudioMixerManagerMac() : _inputDeviceID(kAudioObjectUnknown), _outputDeviceID(kAudioObjectUnknown), _noInputChannels(0), _noOutputChannels(0) { RTC_DLOG(LS_INFO) << __FUNCTION__ << " created"; } AudioMixerManagerMac::~AudioMixerManagerMac() { RTC_DLOG(LS_INFO) << __FUNCTION__ << " destroyed"; Close(); } // ============================================================================ // PUBLIC METHODS // ============================================================================ int32_t AudioMixerManagerMac::Close() { RTC_DLOG(LS_VERBOSE) << __FUNCTION__; MutexLock lock(&mutex_); CloseSpeakerLocked(); CloseMicrophoneLocked(); return 0; } int32_t AudioMixerManagerMac::CloseSpeaker() { MutexLock lock(&mutex_); return CloseSpeakerLocked(); } int32_t AudioMixerManagerMac::CloseSpeakerLocked() { RTC_DLOG(LS_VERBOSE) << __FUNCTION__; _outputDeviceID = kAudioObjectUnknown; _noOutputChannels = 0; return 0; } int32_t AudioMixerManagerMac::CloseMicrophone() { MutexLock lock(&mutex_); return CloseMicrophoneLocked(); } int32_t AudioMixerManagerMac::CloseMicrophoneLocked() { RTC_DLOG(LS_VERBOSE) << __FUNCTION__; _inputDeviceID = kAudioObjectUnknown; _noInputChannels = 0; return 0; } int32_t AudioMixerManagerMac::OpenSpeaker(AudioDeviceID deviceID) { RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::OpenSpeaker(id=" << deviceID << ")"; MutexLock lock(&mutex_); 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}; // First, does it have the property? Aggregate devices don't. if (AudioObjectHasProperty(_outputDeviceID, &propertyAddress)) { size = sizeof(hogPid); WEBRTC_CA_RETURN_ON_ERR(AudioObjectGetPropertyData( _outputDeviceID, &propertyAddress, 0, NULL, &size, &hogPid)); if (hogPid == -1) { RTC_LOG(LS_VERBOSE) << "No process has hogged the output device"; } // getpid() is apparently "always successful" else if (hogPid == getpid()) { RTC_LOG(LS_VERBOSE) << "Our process has hogged the output device"; } else { RTC_LOG(LS_WARNING) << "Another process (pid = " << static_cast(hogPid) << ") has hogged the output device"; 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) { RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::OpenMicrophone(id=" << deviceID << ")"; MutexLock lock(&mutex_); 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) { RTC_LOG(LS_VERBOSE) << "No process has hogged the input device"; } // getpid() is apparently "always successful" else if (hogPid == getpid()) { RTC_LOG(LS_VERBOSE) << "Our process has hogged the input device"; } else { RTC_LOG(LS_WARNING) << "Another process (pid = " << static_cast(hogPid) << ") has hogged the input device"; 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 { RTC_DLOG(LS_INFO) << __FUNCTION__; return (_outputDeviceID != kAudioObjectUnknown); } bool AudioMixerManagerMac::MicrophoneIsInitialized() const { RTC_DLOG(LS_INFO) << __FUNCTION__; return (_inputDeviceID != kAudioObjectUnknown); } int32_t AudioMixerManagerMac::SetSpeakerVolume(uint32_t volume) { RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SetSpeakerVolume(volume=" << volume << ")"; MutexLock lock(&mutex_); if (_outputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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); RTC_DCHECK(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) { RTC_LOG(LS_WARNING) << "Unable to set a volume on any output channel"; return -1; } return 0; } int32_t AudioMixerManagerMac::SpeakerVolume(uint32_t& volume) const { if (_outputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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(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) { RTC_LOG(LS_WARNING) << "Unable to get a volume on any channel"; return -1; } RTC_DCHECK_GT(channels, 0); // vol 0.0 to 1.0 -> convert to 0 - 255 volume = static_cast(255 * vol / channels + 0.5); } RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SpeakerVolume() => vol=" << vol; return 0; } int32_t AudioMixerManagerMac::MaxSpeakerVolume(uint32_t& maxVolume) const { if (_outputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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) { RTC_LOG(LS_WARNING) << "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::SpeakerVolumeIsAvailable(bool& available) { if (_outputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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; RTC_LOG(LS_WARNING) << "Volume cannot be set for output channel " << i << ", err=" << err; return -1; } } available = true; return 0; } int32_t AudioMixerManagerMac::SpeakerMuteIsAvailable(bool& available) { if (_outputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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; RTC_LOG(LS_WARNING) << "Mute cannot be set for output channel " << i << ", err=" << err; return -1; } } available = true; return 0; } int32_t AudioMixerManagerMac::SetSpeakerMute(bool enable) { RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SetSpeakerMute(enable=" << enable << ")"; MutexLock lock(&mutex_); if (_outputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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) { RTC_LOG(LS_WARNING) << "Unable to set mute on any input channel"; return -1; } return 0; } int32_t AudioMixerManagerMac::SpeakerMute(bool& enabled) const { if (_outputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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(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) { RTC_LOG(LS_WARNING) << "Unable to get mute for any channel"; return -1; } RTC_DCHECK_GT(channels, 0); // 1 means muted enabled = static_cast(muted); } RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SpeakerMute() => enabled=" << enabled; return 0; } int32_t AudioMixerManagerMac::StereoPlayoutIsAvailable(bool& available) { if (_outputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "device ID has not been set"; return -1; } available = (_noOutputChannels == 2); return 0; } int32_t AudioMixerManagerMac::StereoRecordingIsAvailable(bool& available) { if (_inputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "device ID has not been set"; return -1; } available = (_noInputChannels == 2); return 0; } int32_t AudioMixerManagerMac::MicrophoneMuteIsAvailable(bool& available) { if (_inputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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; RTC_LOG(LS_WARNING) << "Mute cannot be set for output channel " << i << ", err=" << err; return -1; } } available = true; return 0; } int32_t AudioMixerManagerMac::SetMicrophoneMute(bool enable) { RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SetMicrophoneMute(enable=" << enable << ")"; MutexLock lock(&mutex_); if (_inputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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) { RTC_LOG(LS_WARNING) << "Unable to set mute on any input channel"; return -1; } return 0; } int32_t AudioMixerManagerMac::MicrophoneMute(bool& enabled) const { if (_inputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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(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) { RTC_LOG(LS_WARNING) << "Unable to get mute for any channel"; return -1; } RTC_DCHECK_GT(channels, 0); // 1 means muted enabled = static_cast(muted); } RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::MicrophoneMute() => enabled=" << enabled; return 0; } int32_t AudioMixerManagerMac::MicrophoneVolumeIsAvailable(bool& available) { if (_inputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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; RTC_LOG(LS_WARNING) << "Volume cannot be set for input channel " << i << ", err=" << err; return -1; } } available = true; return 0; } int32_t AudioMixerManagerMac::SetMicrophoneVolume(uint32_t volume) { RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::SetMicrophoneVolume(volume=" << volume << ")"; MutexLock lock(&mutex_); if (_inputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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); RTC_DCHECK(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) { RTC_LOG(LS_WARNING) << "Unable to set a level on any input channel"; return -1; } return 0; } int32_t AudioMixerManagerMac::MicrophoneVolume(uint32_t& volume) const { if (_inputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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(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) { RTC_LOG(LS_WARNING) << "Unable to get a level on any channel"; return -1; } RTC_DCHECK_GT(channels, 0); // vol 0.0 to 1.0 -> convert to 0 - 255 volume = static_cast(255 * volFloat32 / channels + 0.5); } RTC_LOG(LS_VERBOSE) << "AudioMixerManagerMac::MicrophoneVolume() => vol=" << volume; return 0; } int32_t AudioMixerManagerMac::MaxMicrophoneVolume(uint32_t& maxVolume) const { if (_inputDeviceID == kAudioObjectUnknown) { RTC_LOG(LS_WARNING) << "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) { RTC_LOG(LS_WARNING) << "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; } // ============================================================================ // Private Methods // ============================================================================ // CoreAudio errors are best interpreted as four character strings. void AudioMixerManagerMac::logCAMsg(const rtc::LoggingSeverity sev, const char* msg, const char* err) { RTC_DCHECK(msg != NULL); RTC_DCHECK(err != NULL); RTC_DCHECK(sev == rtc::LS_ERROR || sev == rtc::LS_WARNING); #ifdef WEBRTC_ARCH_BIG_ENDIAN switch (sev) { case rtc::LS_ERROR: RTC_LOG(LS_ERROR) << msg << ": " << err[0] << err[1] << err[2] << err[3]; break; case rtc::LS_WARNING: RTC_LOG(LS_WARNING) << msg << ": " << err[0] << err[1] << err[2] << err[3]; break; default: break; } #else // We need to flip the characters in this case. switch (sev) { case rtc::LS_ERROR: RTC_LOG(LS_ERROR) << msg << ": " << err[3] << err[2] << err[1] << err[0]; break; case rtc::LS_WARNING: RTC_LOG(LS_WARNING) << msg << ": " << err[3] << err[2] << err[1] << err[0]; break; default: break; } #endif } } // namespace webrtc // EOF