|  | /* | 
|  | *  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/voice_engine/voe_volume_control_impl.h" | 
|  |  | 
|  | #include "webrtc/system_wrappers/include/trace.h" | 
|  | #include "webrtc/voice_engine/channel.h" | 
|  | #include "webrtc/voice_engine/include/voe_errors.h" | 
|  | #include "webrtc/voice_engine/output_mixer.h" | 
|  | #include "webrtc/voice_engine/transmit_mixer.h" | 
|  | #include "webrtc/voice_engine/voice_engine_impl.h" | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | VoEVolumeControl* VoEVolumeControl::GetInterface(VoiceEngine* voiceEngine) { | 
|  | #ifndef WEBRTC_VOICE_ENGINE_VOLUME_CONTROL_API | 
|  | return NULL; | 
|  | #else | 
|  | if (NULL == voiceEngine) { | 
|  | return NULL; | 
|  | } | 
|  | VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine); | 
|  | s->AddRef(); | 
|  | return s; | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #ifdef WEBRTC_VOICE_ENGINE_VOLUME_CONTROL_API | 
|  |  | 
|  | VoEVolumeControlImpl::VoEVolumeControlImpl(voe::SharedData* shared) | 
|  | : _shared(shared) { | 
|  | WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "VoEVolumeControlImpl::VoEVolumeControlImpl() - ctor"); | 
|  | } | 
|  |  | 
|  | VoEVolumeControlImpl::~VoEVolumeControlImpl() { | 
|  | WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "VoEVolumeControlImpl::~VoEVolumeControlImpl() - dtor"); | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::SetSpeakerVolume(unsigned int volume) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetSpeakerVolume(volume=%u)", volume); | 
|  |  | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | if (volume > kMaxVolumeLevel) { | 
|  | _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, | 
|  | "SetSpeakerVolume() invalid argument"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | uint32_t maxVol(0); | 
|  | uint32_t spkrVol(0); | 
|  |  | 
|  | // scale: [0,kMaxVolumeLevel] -> [0,MaxSpeakerVolume] | 
|  | if (_shared->audio_device()->MaxSpeakerVolume(&maxVol) != 0) { | 
|  | _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError, | 
|  | "SetSpeakerVolume() failed to get max volume"); | 
|  | return -1; | 
|  | } | 
|  | // Round the value and avoid floating computation. | 
|  | spkrVol = (uint32_t)((volume * maxVol + (int)(kMaxVolumeLevel / 2)) / | 
|  | (kMaxVolumeLevel)); | 
|  |  | 
|  | // set the actual volume using the audio mixer | 
|  | if (_shared->audio_device()->SetSpeakerVolume(spkrVol) != 0) { | 
|  | _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError, | 
|  | "SetSpeakerVolume() failed to set speaker volume"); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::GetSpeakerVolume(unsigned int& volume) { | 
|  |  | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | uint32_t spkrVol(0); | 
|  | uint32_t maxVol(0); | 
|  |  | 
|  | if (_shared->audio_device()->SpeakerVolume(&spkrVol) != 0) { | 
|  | _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError, | 
|  | "GetSpeakerVolume() unable to get speaker volume"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // scale: [0, MaxSpeakerVolume] -> [0, kMaxVolumeLevel] | 
|  | if (_shared->audio_device()->MaxSpeakerVolume(&maxVol) != 0) { | 
|  | _shared->SetLastError( | 
|  | VE_GET_MIC_VOL_ERROR, kTraceError, | 
|  | "GetSpeakerVolume() unable to get max speaker volume"); | 
|  | return -1; | 
|  | } | 
|  | // Round the value and avoid floating computation. | 
|  | volume = | 
|  | (uint32_t)((spkrVol * kMaxVolumeLevel + (int)(maxVol / 2)) / (maxVol)); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::SetMicVolume(unsigned int volume) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetMicVolume(volume=%u)", volume); | 
|  |  | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | if (volume > kMaxVolumeLevel) { | 
|  | _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, | 
|  | "SetMicVolume() invalid argument"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | uint32_t maxVol(0); | 
|  | uint32_t micVol(0); | 
|  |  | 
|  | // scale: [0, kMaxVolumeLevel] -> [0,MaxMicrophoneVolume] | 
|  | if (_shared->audio_device()->MaxMicrophoneVolume(&maxVol) != 0) { | 
|  | _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError, | 
|  | "SetMicVolume() failed to get max volume"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (volume == kMaxVolumeLevel) { | 
|  | // On Linux running pulse, users are able to set the volume above 100% | 
|  | // through the volume control panel, where the +100% range is digital | 
|  | // scaling. WebRTC does not support setting the volume above 100%, and | 
|  | // simply ignores changing the volume if the user tries to set it to | 
|  | // |kMaxVolumeLevel| while the current volume is higher than |maxVol|. | 
|  | if (_shared->audio_device()->MicrophoneVolume(&micVol) != 0) { | 
|  | _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError, | 
|  | "SetMicVolume() unable to get microphone volume"); | 
|  | return -1; | 
|  | } | 
|  | if (micVol >= maxVol) | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // Round the value and avoid floating point computation. | 
|  | micVol = (uint32_t)((volume * maxVol + (int)(kMaxVolumeLevel / 2)) / | 
|  | (kMaxVolumeLevel)); | 
|  |  | 
|  | // set the actual volume using the audio mixer | 
|  | if (_shared->audio_device()->SetMicrophoneVolume(micVol) != 0) { | 
|  | _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError, | 
|  | "SetMicVolume() failed to set mic volume"); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::GetMicVolume(unsigned int& volume) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | uint32_t micVol(0); | 
|  | uint32_t maxVol(0); | 
|  |  | 
|  | if (_shared->audio_device()->MicrophoneVolume(&micVol) != 0) { | 
|  | _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError, | 
|  | "GetMicVolume() unable to get microphone volume"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // scale: [0, MaxMicrophoneVolume] -> [0, kMaxVolumeLevel] | 
|  | if (_shared->audio_device()->MaxMicrophoneVolume(&maxVol) != 0) { | 
|  | _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError, | 
|  | "GetMicVolume() unable to get max microphone volume"); | 
|  | return -1; | 
|  | } | 
|  | if (micVol < maxVol) { | 
|  | // Round the value and avoid floating point calculation. | 
|  | volume = | 
|  | (uint32_t)((micVol * kMaxVolumeLevel + (int)(maxVol / 2)) / (maxVol)); | 
|  | } else { | 
|  | // Truncate the value to the kMaxVolumeLevel. | 
|  | volume = kMaxVolumeLevel; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::SetInputMute(int channel, bool enable) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetInputMute(channel=%d, enable=%d)", channel, enable); | 
|  |  | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | if (channel == -1) { | 
|  | // Mute before demultiplexing <=> affects all channels | 
|  | return _shared->transmit_mixer()->SetMute(enable); | 
|  | } | 
|  | // Mute after demultiplexing <=> affects one channel only | 
|  | voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
|  | voe::Channel* channelPtr = ch.channel(); | 
|  | if (channelPtr == NULL) { | 
|  | _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, | 
|  | "SetInputMute() failed to locate channel"); | 
|  | return -1; | 
|  | } | 
|  | return channelPtr->SetInputMute(enable); | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::GetInputMute(int channel, bool& enabled) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | if (channel == -1) { | 
|  | enabled = _shared->transmit_mixer()->Mute(); | 
|  | } else { | 
|  | voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
|  | voe::Channel* channelPtr = ch.channel(); | 
|  | if (channelPtr == NULL) { | 
|  | _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, | 
|  | "SetInputMute() failed to locate channel"); | 
|  | return -1; | 
|  | } | 
|  | enabled = channelPtr->InputMute(); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::GetSpeechInputLevel(unsigned int& level) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | int8_t currentLevel = _shared->transmit_mixer()->AudioLevel(); | 
|  | level = static_cast<unsigned int>(currentLevel); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::GetSpeechOutputLevel(int channel, | 
|  | unsigned int& level) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | if (channel == -1) { | 
|  | return _shared->output_mixer()->GetSpeechOutputLevel((uint32_t&)level); | 
|  | } else { | 
|  | voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
|  | voe::Channel* channelPtr = ch.channel(); | 
|  | if (channelPtr == NULL) { | 
|  | _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, | 
|  | "GetSpeechOutputLevel() failed to locate channel"); | 
|  | return -1; | 
|  | } | 
|  | channelPtr->GetSpeechOutputLevel((uint32_t&)level); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::GetSpeechInputLevelFullRange(unsigned int& level) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | int16_t currentLevel = _shared->transmit_mixer()->AudioLevelFullRange(); | 
|  | level = static_cast<unsigned int>(currentLevel); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::GetSpeechOutputLevelFullRange(int channel, | 
|  | unsigned int& level) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | if (channel == -1) { | 
|  | return _shared->output_mixer()->GetSpeechOutputLevelFullRange( | 
|  | (uint32_t&)level); | 
|  | } else { | 
|  | voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
|  | voe::Channel* channelPtr = ch.channel(); | 
|  | if (channelPtr == NULL) { | 
|  | _shared->SetLastError( | 
|  | VE_CHANNEL_NOT_VALID, kTraceError, | 
|  | "GetSpeechOutputLevelFullRange() failed to locate channel"); | 
|  | return -1; | 
|  | } | 
|  | channelPtr->GetSpeechOutputLevelFullRange((uint32_t&)level); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::SetChannelOutputVolumeScaling(int channel, | 
|  | float scaling) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetChannelOutputVolumeScaling(channel=%d, scaling=%3.2f)", | 
|  | channel, scaling); | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | if (scaling < kMinOutputVolumeScaling || scaling > kMaxOutputVolumeScaling) { | 
|  | _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, | 
|  | "SetChannelOutputVolumeScaling() invalid parameter"); | 
|  | return -1; | 
|  | } | 
|  | voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
|  | voe::Channel* channelPtr = ch.channel(); | 
|  | if (channelPtr == NULL) { | 
|  | _shared->SetLastError( | 
|  | VE_CHANNEL_NOT_VALID, kTraceError, | 
|  | "SetChannelOutputVolumeScaling() failed to locate channel"); | 
|  | return -1; | 
|  | } | 
|  | return channelPtr->SetChannelOutputVolumeScaling(scaling); | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::GetChannelOutputVolumeScaling(int channel, | 
|  | float& scaling) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  | voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
|  | voe::Channel* channelPtr = ch.channel(); | 
|  | if (channelPtr == NULL) { | 
|  | _shared->SetLastError( | 
|  | VE_CHANNEL_NOT_VALID, kTraceError, | 
|  | "GetChannelOutputVolumeScaling() failed to locate channel"); | 
|  | return -1; | 
|  | } | 
|  | return channelPtr->GetChannelOutputVolumeScaling(scaling); | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::SetOutputVolumePan(int channel, | 
|  | float left, | 
|  | float right) { | 
|  | WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
|  | "SetOutputVolumePan(channel=%d, left=%2.1f, right=%2.1f)", | 
|  | channel, left, right); | 
|  |  | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | bool available(false); | 
|  | _shared->audio_device()->StereoPlayoutIsAvailable(&available); | 
|  | if (!available) { | 
|  | _shared->SetLastError(VE_FUNC_NO_STEREO, kTraceError, | 
|  | "SetOutputVolumePan() stereo playout not supported"); | 
|  | return -1; | 
|  | } | 
|  | if ((left < kMinOutputVolumePanning) || (left > kMaxOutputVolumePanning) || | 
|  | (right < kMinOutputVolumePanning) || (right > kMaxOutputVolumePanning)) { | 
|  | _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, | 
|  | "SetOutputVolumePan() invalid parameter"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (channel == -1) { | 
|  | // Master balance (affectes the signal after output mixing) | 
|  | return _shared->output_mixer()->SetOutputVolumePan(left, right); | 
|  | } | 
|  | // Per-channel balance (affects the signal before output mixing) | 
|  | voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
|  | voe::Channel* channelPtr = ch.channel(); | 
|  | if (channelPtr == NULL) { | 
|  | _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, | 
|  | "SetOutputVolumePan() failed to locate channel"); | 
|  | return -1; | 
|  | } | 
|  | return channelPtr->SetOutputVolumePan(left, right); | 
|  | } | 
|  |  | 
|  | int VoEVolumeControlImpl::GetOutputVolumePan(int channel, | 
|  | float& left, | 
|  | float& right) { | 
|  | if (!_shared->statistics().Initialized()) { | 
|  | _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | bool available(false); | 
|  | _shared->audio_device()->StereoPlayoutIsAvailable(&available); | 
|  | if (!available) { | 
|  | _shared->SetLastError(VE_FUNC_NO_STEREO, kTraceError, | 
|  | "GetOutputVolumePan() stereo playout not supported"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (channel == -1) { | 
|  | return _shared->output_mixer()->GetOutputVolumePan(left, right); | 
|  | } | 
|  | voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
|  | voe::Channel* channelPtr = ch.channel(); | 
|  | if (channelPtr == NULL) { | 
|  | _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, | 
|  | "GetOutputVolumePan() failed to locate channel"); | 
|  | return -1; | 
|  | } | 
|  | return channelPtr->GetOutputVolumePan(left, right); | 
|  | } | 
|  |  | 
|  | #endif  // #ifdef WEBRTC_VOICE_ENGINE_VOLUME_CONTROL_API | 
|  |  | 
|  | }  // namespace webrtc |