|  | /* | 
|  | *  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/linux/audio_mixer_manager_alsa_linux.h" | 
|  |  | 
|  | #include "modules/audio_device/linux/audio_device_alsa_linux.h" | 
|  | #include "rtc_base/logging.h" | 
|  |  | 
|  | // Accesses ALSA functions through our late-binding symbol table instead of | 
|  | // directly. This way we don't have to link to libasound, which means our binary | 
|  | // will work on systems that don't have it. | 
|  | #define LATE(sym)                                                            \ | 
|  | LATESYM_GET(webrtc::adm_linux_alsa::AlsaSymbolTable, GetAlsaSymbolTable(), \ | 
|  | sym) | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | AudioMixerManagerLinuxALSA::AudioMixerManagerLinuxALSA() | 
|  | : _outputMixerHandle(NULL), | 
|  | _inputMixerHandle(NULL), | 
|  | _outputMixerElement(NULL), | 
|  | _inputMixerElement(NULL) { | 
|  | RTC_LOG(LS_INFO) << __FUNCTION__ << " created"; | 
|  |  | 
|  | memset(_outputMixerStr, 0, kAdmMaxDeviceNameSize); | 
|  | memset(_inputMixerStr, 0, kAdmMaxDeviceNameSize); | 
|  | } | 
|  |  | 
|  | AudioMixerManagerLinuxALSA::~AudioMixerManagerLinuxALSA() { | 
|  | RTC_LOG(LS_INFO) << __FUNCTION__ << " destroyed"; | 
|  | Close(); | 
|  | } | 
|  |  | 
|  | // ============================================================================ | 
|  | //                                    PUBLIC METHODS | 
|  | // ============================================================================ | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::Close() { | 
|  | RTC_LOG(LS_VERBOSE) << __FUNCTION__; | 
|  |  | 
|  | rtc::CritScope lock(&_critSect); | 
|  |  | 
|  | CloseSpeaker(); | 
|  | CloseMicrophone(); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::CloseSpeaker() { | 
|  | RTC_LOG(LS_VERBOSE) << __FUNCTION__; | 
|  |  | 
|  | rtc::CritScope lock(&_critSect); | 
|  |  | 
|  | int errVal = 0; | 
|  |  | 
|  | if (_outputMixerHandle != NULL) { | 
|  | RTC_LOG(LS_VERBOSE) << "Closing playout mixer"; | 
|  | LATE(snd_mixer_free)(_outputMixerHandle); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error freeing playout mixer: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  | errVal = LATE(snd_mixer_detach)(_outputMixerHandle, _outputMixerStr); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error detaching playout mixer: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  | errVal = LATE(snd_mixer_close)(_outputMixerHandle); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error snd_mixer_close(handleMixer) errVal=" | 
|  | << errVal; | 
|  | } | 
|  | _outputMixerHandle = NULL; | 
|  | _outputMixerElement = NULL; | 
|  | } | 
|  | memset(_outputMixerStr, 0, kAdmMaxDeviceNameSize); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::CloseMicrophone() { | 
|  | RTC_LOG(LS_VERBOSE) << __FUNCTION__; | 
|  |  | 
|  | rtc::CritScope lock(&_critSect); | 
|  |  | 
|  | int errVal = 0; | 
|  |  | 
|  | if (_inputMixerHandle != NULL) { | 
|  | RTC_LOG(LS_VERBOSE) << "Closing record mixer"; | 
|  |  | 
|  | LATE(snd_mixer_free)(_inputMixerHandle); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error freeing record mixer: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  | RTC_LOG(LS_VERBOSE) << "Closing record mixer 2"; | 
|  |  | 
|  | errVal = LATE(snd_mixer_detach)(_inputMixerHandle, _inputMixerStr); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error detaching record mixer: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  | RTC_LOG(LS_VERBOSE) << "Closing record mixer 3"; | 
|  |  | 
|  | errVal = LATE(snd_mixer_close)(_inputMixerHandle); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error snd_mixer_close(handleMixer) errVal=" | 
|  | << errVal; | 
|  | } | 
|  |  | 
|  | RTC_LOG(LS_VERBOSE) << "Closing record mixer 4"; | 
|  | _inputMixerHandle = NULL; | 
|  | _inputMixerElement = NULL; | 
|  | } | 
|  | memset(_inputMixerStr, 0, kAdmMaxDeviceNameSize); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::OpenSpeaker(char* deviceName) { | 
|  | RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::OpenSpeaker(name=" | 
|  | << deviceName << ")"; | 
|  |  | 
|  | rtc::CritScope lock(&_critSect); | 
|  |  | 
|  | int errVal = 0; | 
|  |  | 
|  | // Close any existing output mixer handle | 
|  | // | 
|  | if (_outputMixerHandle != NULL) { | 
|  | RTC_LOG(LS_VERBOSE) << "Closing playout mixer"; | 
|  |  | 
|  | LATE(snd_mixer_free)(_outputMixerHandle); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error freeing playout mixer: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  | errVal = LATE(snd_mixer_detach)(_outputMixerHandle, _outputMixerStr); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error detaching playout mixer: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  | errVal = LATE(snd_mixer_close)(_outputMixerHandle); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error snd_mixer_close(handleMixer) errVal=" | 
|  | << errVal; | 
|  | } | 
|  | } | 
|  | _outputMixerHandle = NULL; | 
|  | _outputMixerElement = NULL; | 
|  |  | 
|  | errVal = LATE(snd_mixer_open)(&_outputMixerHandle, 0); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "snd_mixer_open(&_outputMixerHandle, 0) - error"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | char controlName[kAdmMaxDeviceNameSize] = {0}; | 
|  | GetControlName(controlName, deviceName); | 
|  |  | 
|  | RTC_LOG(LS_VERBOSE) << "snd_mixer_attach(_outputMixerHandle, " << controlName | 
|  | << ")"; | 
|  |  | 
|  | errVal = LATE(snd_mixer_attach)(_outputMixerHandle, controlName); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "snd_mixer_attach(_outputMixerHandle, " << controlName | 
|  | << ") error: " << LATE(snd_strerror)(errVal); | 
|  | _outputMixerHandle = NULL; | 
|  | return -1; | 
|  | } | 
|  | strcpy(_outputMixerStr, controlName); | 
|  |  | 
|  | errVal = LATE(snd_mixer_selem_register)(_outputMixerHandle, NULL, NULL); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) | 
|  | << "snd_mixer_selem_register(_outputMixerHandle, NULL, NULL), " | 
|  | << "error: " << LATE(snd_strerror)(errVal); | 
|  | _outputMixerHandle = NULL; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Load and find the proper mixer element | 
|  | if (LoadSpeakerMixerElement() < 0) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (_outputMixerHandle != NULL) { | 
|  | RTC_LOG(LS_VERBOSE) << "the output mixer device is now open (" | 
|  | << _outputMixerHandle << ")"; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::OpenMicrophone(char* deviceName) { | 
|  | RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::OpenMicrophone(name=" | 
|  | << deviceName << ")"; | 
|  |  | 
|  | rtc::CritScope lock(&_critSect); | 
|  |  | 
|  | int errVal = 0; | 
|  |  | 
|  | // Close any existing input mixer handle | 
|  | // | 
|  | if (_inputMixerHandle != NULL) { | 
|  | RTC_LOG(LS_VERBOSE) << "Closing record mixer"; | 
|  |  | 
|  | LATE(snd_mixer_free)(_inputMixerHandle); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error freeing record mixer: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  | RTC_LOG(LS_VERBOSE) << "Closing record mixer"; | 
|  |  | 
|  | errVal = LATE(snd_mixer_detach)(_inputMixerHandle, _inputMixerStr); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error detaching record mixer: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  | RTC_LOG(LS_VERBOSE) << "Closing record mixer"; | 
|  |  | 
|  | errVal = LATE(snd_mixer_close)(_inputMixerHandle); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error snd_mixer_close(handleMixer) errVal=" | 
|  | << errVal; | 
|  | } | 
|  | RTC_LOG(LS_VERBOSE) << "Closing record mixer"; | 
|  | } | 
|  | _inputMixerHandle = NULL; | 
|  | _inputMixerElement = NULL; | 
|  |  | 
|  | errVal = LATE(snd_mixer_open)(&_inputMixerHandle, 0); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "snd_mixer_open(&_inputMixerHandle, 0) - error"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | char controlName[kAdmMaxDeviceNameSize] = {0}; | 
|  | GetControlName(controlName, deviceName); | 
|  |  | 
|  | RTC_LOG(LS_VERBOSE) << "snd_mixer_attach(_inputMixerHandle, " << controlName | 
|  | << ")"; | 
|  |  | 
|  | errVal = LATE(snd_mixer_attach)(_inputMixerHandle, controlName); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "snd_mixer_attach(_inputMixerHandle, " << controlName | 
|  | << ") error: " << LATE(snd_strerror)(errVal); | 
|  |  | 
|  | _inputMixerHandle = NULL; | 
|  | return -1; | 
|  | } | 
|  | strcpy(_inputMixerStr, controlName); | 
|  |  | 
|  | errVal = LATE(snd_mixer_selem_register)(_inputMixerHandle, NULL, NULL); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) | 
|  | << "snd_mixer_selem_register(_inputMixerHandle, NULL, NULL), " | 
|  | << "error: " << LATE(snd_strerror)(errVal); | 
|  |  | 
|  | _inputMixerHandle = NULL; | 
|  | return -1; | 
|  | } | 
|  | // Load and find the proper mixer element | 
|  | if (LoadMicMixerElement() < 0) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (_inputMixerHandle != NULL) { | 
|  | RTC_LOG(LS_VERBOSE) << "the input mixer device is now open (" | 
|  | << _inputMixerHandle << ")"; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | bool AudioMixerManagerLinuxALSA::SpeakerIsInitialized() const { | 
|  | RTC_LOG(LS_INFO) << __FUNCTION__; | 
|  |  | 
|  | return (_outputMixerHandle != NULL); | 
|  | } | 
|  |  | 
|  | bool AudioMixerManagerLinuxALSA::MicrophoneIsInitialized() const { | 
|  | RTC_LOG(LS_INFO) << __FUNCTION__; | 
|  |  | 
|  | return (_inputMixerHandle != NULL); | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SetSpeakerVolume(uint32_t volume) { | 
|  | RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::SetSpeakerVolume(volume=" | 
|  | << volume << ")"; | 
|  |  | 
|  | rtc::CritScope lock(&_critSect); | 
|  |  | 
|  | if (_outputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int errVal = LATE(snd_mixer_selem_set_playback_volume_all)( | 
|  | _outputMixerElement, volume); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error changing master volume: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SpeakerVolume(uint32_t& volume) const { | 
|  | if (_outputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | long int vol(0); | 
|  |  | 
|  | int errVal = LATE(snd_mixer_selem_get_playback_volume)( | 
|  | _outputMixerElement, (snd_mixer_selem_channel_id_t)0, &vol); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error getting outputvolume: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | return -1; | 
|  | } | 
|  | RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::SpeakerVolume() => vol=" | 
|  | << vol; | 
|  |  | 
|  | volume = static_cast<uint32_t>(vol); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::MaxSpeakerVolume( | 
|  | uint32_t& maxVolume) const { | 
|  | if (_outputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avilable output mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | long int minVol(0); | 
|  | long int maxVol(0); | 
|  |  | 
|  | int errVal = LATE(snd_mixer_selem_get_playback_volume_range)( | 
|  | _outputMixerElement, &minVol, &maxVol); | 
|  |  | 
|  | RTC_LOG(LS_VERBOSE) << "Playout hardware volume range, min: " << minVol | 
|  | << ", max: " << maxVol; | 
|  |  | 
|  | if (maxVol <= minVol) { | 
|  | RTC_LOG(LS_ERROR) << "Error getting get_playback_volume_range: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  |  | 
|  | maxVolume = static_cast<uint32_t>(maxVol); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::MinSpeakerVolume( | 
|  | uint32_t& minVolume) const { | 
|  | if (_outputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | long int minVol(0); | 
|  | long int maxVol(0); | 
|  |  | 
|  | int errVal = LATE(snd_mixer_selem_get_playback_volume_range)( | 
|  | _outputMixerElement, &minVol, &maxVol); | 
|  |  | 
|  | RTC_LOG(LS_VERBOSE) << "Playout hardware volume range, min: " << minVol | 
|  | << ", max: " << maxVol; | 
|  |  | 
|  | if (maxVol <= minVol) { | 
|  | RTC_LOG(LS_ERROR) << "Error getting get_playback_volume_range: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  |  | 
|  | minVolume = static_cast<uint32_t>(minVol); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // TL: Have done testnig with these but they don't seem reliable and | 
|  | // they were therefore not added | 
|  | /* | 
|  | // ---------------------------------------------------------------------------- | 
|  | //    SetMaxSpeakerVolume | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SetMaxSpeakerVolume( | 
|  | uint32_t maxVolume) | 
|  | { | 
|  |  | 
|  | if (_outputMixerElement == NULL) | 
|  | { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | long int minVol(0); | 
|  | long int maxVol(0); | 
|  |  | 
|  | int errVal = snd_mixer_selem_get_playback_volume_range( | 
|  | _outputMixerElement, &minVol, &maxVol); | 
|  | if ((maxVol <= minVol) || (errVal != 0)) | 
|  | { | 
|  | RTC_LOG(LS_WARNING) << "Error getting playback volume range: " | 
|  | << snd_strerror(errVal); | 
|  | } | 
|  |  | 
|  | maxVol = maxVolume; | 
|  | errVal = snd_mixer_selem_set_playback_volume_range( | 
|  | _outputMixerElement, minVol, maxVol); | 
|  | RTC_LOG(LS_VERBOSE) << "Playout hardware volume range, min: " << minVol | 
|  | << ", max: " << maxVol; | 
|  | if (errVal != 0) | 
|  | { | 
|  | RTC_LOG(LS_ERROR) << "Error setting playback volume range: " | 
|  | << snd_strerror(errVal); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // ---------------------------------------------------------------------------- | 
|  | //    SetMinSpeakerVolume | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SetMinSpeakerVolume( | 
|  | uint32_t minVolume) | 
|  | { | 
|  |  | 
|  | if (_outputMixerElement == NULL) | 
|  | { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | long int minVol(0); | 
|  | long int maxVol(0); | 
|  |  | 
|  | int errVal = snd_mixer_selem_get_playback_volume_range( | 
|  | _outputMixerElement, &minVol, &maxVol); | 
|  | if ((maxVol <= minVol) || (errVal != 0)) | 
|  | { | 
|  | RTC_LOG(LS_WARNING) << "Error getting playback volume range: " | 
|  | << snd_strerror(errVal); | 
|  | } | 
|  |  | 
|  | minVol = minVolume; | 
|  | errVal = snd_mixer_selem_set_playback_volume_range( | 
|  | _outputMixerElement, minVol, maxVol); | 
|  | RTC_LOG(LS_VERBOSE) << "Playout hardware volume range, min: " << minVol | 
|  | << ", max: " << maxVol; | 
|  | if (errVal != 0) | 
|  | { | 
|  | RTC_LOG(LS_ERROR) << "Error setting playback volume range: " | 
|  | << snd_strerror(errVal); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | */ | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SpeakerVolumeIsAvailable(bool& available) { | 
|  | if (_outputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | available = LATE(snd_mixer_selem_has_playback_volume)(_outputMixerElement); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SpeakerMuteIsAvailable(bool& available) { | 
|  | if (_outputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | available = LATE(snd_mixer_selem_has_playback_switch)(_outputMixerElement); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SetSpeakerMute(bool enable) { | 
|  | RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::SetSpeakerMute(enable=" | 
|  | << enable << ")"; | 
|  |  | 
|  | rtc::CritScope lock(&_critSect); | 
|  |  | 
|  | if (_outputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Ensure that the selected speaker destination has a valid mute control. | 
|  | bool available(false); | 
|  | SpeakerMuteIsAvailable(available); | 
|  | if (!available) { | 
|  | RTC_LOG(LS_WARNING) << "it is not possible to mute the speaker"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Note value = 0 (off) means muted | 
|  | int errVal = LATE(snd_mixer_selem_set_playback_switch_all)( | 
|  | _outputMixerElement, !enable); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error setting playback switch: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SpeakerMute(bool& enabled) const { | 
|  | if (_outputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable output mixer exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Ensure that the selected speaker destination has a valid mute control. | 
|  | bool available = | 
|  | LATE(snd_mixer_selem_has_playback_switch)(_outputMixerElement); | 
|  | if (!available) { | 
|  | RTC_LOG(LS_WARNING) << "it is not possible to mute the speaker"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int value(false); | 
|  |  | 
|  | // Retrieve one boolean control value for a specified mute-control | 
|  | // | 
|  | int errVal = LATE(snd_mixer_selem_get_playback_switch)( | 
|  | _outputMixerElement, (snd_mixer_selem_channel_id_t)0, &value); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error getting playback switch: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Note value = 0 (off) means muted | 
|  | enabled = (bool)!value; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::MicrophoneMuteIsAvailable(bool& available) { | 
|  | if (_inputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | available = LATE(snd_mixer_selem_has_capture_switch)(_inputMixerElement); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SetMicrophoneMute(bool enable) { | 
|  | RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::SetMicrophoneMute(enable=" | 
|  | << enable << ")"; | 
|  |  | 
|  | rtc::CritScope lock(&_critSect); | 
|  |  | 
|  | if (_inputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Ensure that the selected microphone destination has a valid mute control. | 
|  | bool available(false); | 
|  | MicrophoneMuteIsAvailable(available); | 
|  | if (!available) { | 
|  | RTC_LOG(LS_WARNING) << "it is not possible to mute the microphone"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Note value = 0 (off) means muted | 
|  | int errVal = | 
|  | LATE(snd_mixer_selem_set_capture_switch_all)(_inputMixerElement, !enable); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error setting capture switch: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::MicrophoneMute(bool& enabled) const { | 
|  | if (_inputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable input mixer exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Ensure that the selected microphone destination has a valid mute control. | 
|  | bool available = LATE(snd_mixer_selem_has_capture_switch)(_inputMixerElement); | 
|  | if (!available) { | 
|  | RTC_LOG(LS_WARNING) << "it is not possible to mute the microphone"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int value(false); | 
|  |  | 
|  | // Retrieve one boolean control value for a specified mute-control | 
|  | // | 
|  | int errVal = LATE(snd_mixer_selem_get_capture_switch)( | 
|  | _inputMixerElement, (snd_mixer_selem_channel_id_t)0, &value); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error getting capture switch: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // Note value = 0 (off) means muted | 
|  | enabled = (bool)!value; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::MicrophoneVolumeIsAvailable( | 
|  | bool& available) { | 
|  | if (_inputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | available = LATE(snd_mixer_selem_has_capture_volume)(_inputMixerElement); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SetMicrophoneVolume(uint32_t volume) { | 
|  | RTC_LOG(LS_VERBOSE) | 
|  | << "AudioMixerManagerLinuxALSA::SetMicrophoneVolume(volume=" << volume | 
|  | << ")"; | 
|  |  | 
|  | rtc::CritScope lock(&_critSect); | 
|  |  | 
|  | if (_inputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int errVal = | 
|  | LATE(snd_mixer_selem_set_capture_volume_all)(_inputMixerElement, volume); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error changing microphone volume: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | // TL: Have done testnig with these but they don't seem reliable and | 
|  | // they were therefore not added | 
|  | /* | 
|  | // ---------------------------------------------------------------------------- | 
|  | //    SetMaxMicrophoneVolume | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SetMaxMicrophoneVolume( | 
|  | uint32_t maxVolume) | 
|  | { | 
|  |  | 
|  | if (_inputMixerElement == NULL) | 
|  | { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | long int minVol(0); | 
|  | long int maxVol(0); | 
|  |  | 
|  | int errVal = snd_mixer_selem_get_capture_volume_range(_inputMixerElement, | 
|  | &minVol, &maxVol); | 
|  | if ((maxVol <= minVol) || (errVal != 0)) | 
|  | { | 
|  | RTC_LOG(LS_WARNING) << "Error getting capture volume range: " | 
|  | << snd_strerror(errVal); | 
|  | } | 
|  |  | 
|  | maxVol = (long int)maxVolume; | 
|  | printf("min %d max %d", minVol, maxVol); | 
|  | errVal = snd_mixer_selem_set_capture_volume_range(_inputMixerElement, minVol, | 
|  | maxVol); RTC_LOG(LS_VERBOSE) << "Capture hardware volume range, min: " << | 
|  | minVol | 
|  | << ", max: " << maxVol; | 
|  | if (errVal != 0) | 
|  | { | 
|  | RTC_LOG(LS_ERROR) << "Error setting capture volume range: " | 
|  | << snd_strerror(errVal); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // ---------------------------------------------------------------------------- | 
|  | //    SetMinMicrophoneVolume | 
|  | // ---------------------------------------------------------------------------- | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::SetMinMicrophoneVolume( | 
|  | uint32_t minVolume) | 
|  | { | 
|  |  | 
|  | if (_inputMixerElement == NULL) | 
|  | { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable output mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | long int minVol(0); | 
|  | long int maxVol(0); | 
|  |  | 
|  | int errVal = snd_mixer_selem_get_capture_volume_range( | 
|  | _inputMixerElement, &minVol, &maxVol); | 
|  | if (maxVol <= minVol) | 
|  | { | 
|  | //maxVol = 255; | 
|  | RTC_LOG(LS_WARNING) << "Error getting capture volume range: " | 
|  | << snd_strerror(errVal); | 
|  | } | 
|  |  | 
|  | printf("min %d max %d", minVol, maxVol); | 
|  | minVol = (long int)minVolume; | 
|  | errVal = snd_mixer_selem_set_capture_volume_range( | 
|  | _inputMixerElement, minVol, maxVol); | 
|  | RTC_LOG(LS_VERBOSE) << "Capture hardware volume range, min: " << minVol | 
|  | << ", max: " << maxVol; | 
|  | if (errVal != 0) | 
|  | { | 
|  | RTC_LOG(LS_ERROR) << "Error setting capture volume range: " | 
|  | << snd_strerror(errVal); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | */ | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::MicrophoneVolume(uint32_t& volume) const { | 
|  | if (_inputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | long int vol(0); | 
|  |  | 
|  | int errVal = LATE(snd_mixer_selem_get_capture_volume)( | 
|  | _inputMixerElement, (snd_mixer_selem_channel_id_t)0, &vol); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "Error getting inputvolume: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | return -1; | 
|  | } | 
|  | RTC_LOG(LS_VERBOSE) | 
|  | << "AudioMixerManagerLinuxALSA::MicrophoneVolume() => vol=" << vol; | 
|  |  | 
|  | volume = static_cast<uint32_t>(vol); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::MaxMicrophoneVolume( | 
|  | uint32_t& maxVolume) const { | 
|  | if (_inputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | long int minVol(0); | 
|  | long int maxVol(0); | 
|  |  | 
|  | // check if we have mic volume at all | 
|  | if (!LATE(snd_mixer_selem_has_capture_volume)(_inputMixerElement)) { | 
|  | RTC_LOG(LS_ERROR) << "No microphone volume available"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | int errVal = LATE(snd_mixer_selem_get_capture_volume_range)( | 
|  | _inputMixerElement, &minVol, &maxVol); | 
|  |  | 
|  | RTC_LOG(LS_VERBOSE) << "Microphone hardware volume range, min: " << minVol | 
|  | << ", max: " << maxVol; | 
|  | if (maxVol <= minVol) { | 
|  | RTC_LOG(LS_ERROR) << "Error getting microphone volume range: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  |  | 
|  | maxVolume = static_cast<uint32_t>(maxVol); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::MinMicrophoneVolume( | 
|  | uint32_t& minVolume) const { | 
|  | if (_inputMixerElement == NULL) { | 
|  | RTC_LOG(LS_WARNING) << "no avaliable input mixer element exists"; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | long int minVol(0); | 
|  | long int maxVol(0); | 
|  |  | 
|  | int errVal = LATE(snd_mixer_selem_get_capture_volume_range)( | 
|  | _inputMixerElement, &minVol, &maxVol); | 
|  |  | 
|  | RTC_LOG(LS_VERBOSE) << "Microphone hardware volume range, min: " << minVol | 
|  | << ", max: " << maxVol; | 
|  | if (maxVol <= minVol) { | 
|  | RTC_LOG(LS_ERROR) << "Error getting microphone volume range: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | } | 
|  |  | 
|  | minVolume = static_cast<uint32_t>(minVol); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | // ============================================================================ | 
|  | //                                 Private Methods | 
|  | // ============================================================================ | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::LoadMicMixerElement() const { | 
|  | int errVal = LATE(snd_mixer_load)(_inputMixerHandle); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "snd_mixer_load(_inputMixerHandle), error: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | _inputMixerHandle = NULL; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | snd_mixer_elem_t* elem = NULL; | 
|  | snd_mixer_elem_t* micElem = NULL; | 
|  | unsigned mixerIdx = 0; | 
|  | const char* selemName = NULL; | 
|  |  | 
|  | // Find and store handles to the right mixer elements | 
|  | for (elem = LATE(snd_mixer_first_elem)(_inputMixerHandle); elem; | 
|  | elem = LATE(snd_mixer_elem_next)(elem), mixerIdx++) { | 
|  | if (LATE(snd_mixer_selem_is_active)(elem)) { | 
|  | selemName = LATE(snd_mixer_selem_get_name)(elem); | 
|  | if (strcmp(selemName, "Capture") == 0)  // "Capture", "Mic" | 
|  | { | 
|  | _inputMixerElement = elem; | 
|  | RTC_LOG(LS_VERBOSE) << "Capture element set"; | 
|  | } else if (strcmp(selemName, "Mic") == 0) { | 
|  | micElem = elem; | 
|  | RTC_LOG(LS_VERBOSE) << "Mic element found"; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (_inputMixerElement) { | 
|  | // Use the first Capture element that is found | 
|  | // The second one may not work | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (_inputMixerElement == NULL) { | 
|  | // We didn't find a Capture handle, use Mic. | 
|  | if (micElem != NULL) { | 
|  | _inputMixerElement = micElem; | 
|  | RTC_LOG(LS_VERBOSE) << "Using Mic as capture volume."; | 
|  | } else { | 
|  | _inputMixerElement = NULL; | 
|  | RTC_LOG(LS_ERROR) << "Could not find capture volume on the mixer."; | 
|  |  | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int32_t AudioMixerManagerLinuxALSA::LoadSpeakerMixerElement() const { | 
|  | int errVal = LATE(snd_mixer_load)(_outputMixerHandle); | 
|  | if (errVal < 0) { | 
|  | RTC_LOG(LS_ERROR) << "snd_mixer_load(_outputMixerHandle), error: " | 
|  | << LATE(snd_strerror)(errVal); | 
|  | _outputMixerHandle = NULL; | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | snd_mixer_elem_t* elem = NULL; | 
|  | snd_mixer_elem_t* masterElem = NULL; | 
|  | snd_mixer_elem_t* speakerElem = NULL; | 
|  | unsigned mixerIdx = 0; | 
|  | const char* selemName = NULL; | 
|  |  | 
|  | // Find and store handles to the right mixer elements | 
|  | for (elem = LATE(snd_mixer_first_elem)(_outputMixerHandle); elem; | 
|  | elem = LATE(snd_mixer_elem_next)(elem), mixerIdx++) { | 
|  | if (LATE(snd_mixer_selem_is_active)(elem)) { | 
|  | selemName = LATE(snd_mixer_selem_get_name)(elem); | 
|  | RTC_LOG(LS_VERBOSE) << "snd_mixer_selem_get_name " << mixerIdx << ": " | 
|  | << selemName << " =" << elem; | 
|  |  | 
|  | // "Master", "PCM", "Wave", "Master Mono", "PC Speaker", "PCM", "Wave" | 
|  | if (strcmp(selemName, "PCM") == 0) { | 
|  | _outputMixerElement = elem; | 
|  | RTC_LOG(LS_VERBOSE) << "PCM element set"; | 
|  | } else if (strcmp(selemName, "Master") == 0) { | 
|  | masterElem = elem; | 
|  | RTC_LOG(LS_VERBOSE) << "Master element found"; | 
|  | } else if (strcmp(selemName, "Speaker") == 0) { | 
|  | speakerElem = elem; | 
|  | RTC_LOG(LS_VERBOSE) << "Speaker element found"; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (_outputMixerElement) { | 
|  | // We have found the element we want | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // If we didn't find a PCM Handle, use Master or Speaker | 
|  | if (_outputMixerElement == NULL) { | 
|  | if (masterElem != NULL) { | 
|  | _outputMixerElement = masterElem; | 
|  | RTC_LOG(LS_VERBOSE) << "Using Master as output volume."; | 
|  | } else if (speakerElem != NULL) { | 
|  | _outputMixerElement = speakerElem; | 
|  | RTC_LOG(LS_VERBOSE) << "Using Speaker as output volume."; | 
|  | } else { | 
|  | _outputMixerElement = NULL; | 
|  | RTC_LOG(LS_ERROR) << "Could not find output volume in the mixer."; | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void AudioMixerManagerLinuxALSA::GetControlName(char* controlName, | 
|  | char* deviceName) const { | 
|  | // Example | 
|  | // deviceName: "front:CARD=Intel,DEV=0" | 
|  | // controlName: "hw:CARD=Intel" | 
|  | char* pos1 = strchr(deviceName, ':'); | 
|  | char* pos2 = strchr(deviceName, ','); | 
|  | if (!pos2) { | 
|  | // Can also be default:CARD=Intel | 
|  | pos2 = &deviceName[strlen(deviceName)]; | 
|  | } | 
|  | if (pos1 && pos2) { | 
|  | strcpy(controlName, "hw"); | 
|  | int nChar = (int)(pos2 - pos1); | 
|  | strncpy(&controlName[2], pos1, nChar); | 
|  | controlName[2 + nChar] = '\0'; | 
|  | } else { | 
|  | strcpy(controlName, deviceName); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |