| /* | |
| * 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_DLOG(LS_INFO) << __FUNCTION__ << " created"; | |
| memset(_outputMixerStr, 0, kAdmMaxDeviceNameSize); | |
| memset(_inputMixerStr, 0, kAdmMaxDeviceNameSize); | |
| } | |
| AudioMixerManagerLinuxALSA::~AudioMixerManagerLinuxALSA() { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__ << " destroyed"; | |
| Close(); | |
| } | |
| // ============================================================================ | |
| // PUBLIC METHODS | |
| // ============================================================================ | |
| int32_t AudioMixerManagerLinuxALSA::Close() { | |
| RTC_DLOG(LS_VERBOSE) << __FUNCTION__; | |
| MutexLock lock(&mutex_); | |
| CloseSpeakerLocked(); | |
| CloseMicrophoneLocked(); | |
| return 0; | |
| } | |
| int32_t AudioMixerManagerLinuxALSA::CloseSpeaker() { | |
| MutexLock lock(&mutex_); | |
| return CloseSpeakerLocked(); | |
| } | |
| int32_t AudioMixerManagerLinuxALSA::CloseSpeakerLocked() { | |
| RTC_DLOG(LS_VERBOSE) << __FUNCTION__; | |
| 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() { | |
| MutexLock lock(&mutex_); | |
| return CloseMicrophoneLocked(); | |
| } | |
| int32_t AudioMixerManagerLinuxALSA::CloseMicrophoneLocked() { | |
| RTC_DLOG(LS_VERBOSE) << __FUNCTION__; | |
| 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 << ")"; | |
| MutexLock lock(&mutex_); | |
| 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 << ")"; | |
| MutexLock lock(&mutex_); | |
| 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_DLOG(LS_INFO) << __FUNCTION__; | |
| return (_outputMixerHandle != NULL); | |
| } | |
| bool AudioMixerManagerLinuxALSA::MicrophoneIsInitialized() const { | |
| RTC_DLOG(LS_INFO) << __FUNCTION__; | |
| return (_inputMixerHandle != NULL); | |
| } | |
| int32_t AudioMixerManagerLinuxALSA::SetSpeakerVolume(uint32_t volume) { | |
| RTC_LOG(LS_VERBOSE) << "AudioMixerManagerLinuxALSA::SetSpeakerVolume(volume=" | |
| << volume << ")"; | |
| MutexLock lock(&mutex_); | |
| 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 << ")"; | |
| MutexLock lock(&mutex_); | |
| 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 << ")"; | |
| MutexLock lock(&mutex_); | |
| 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 | |
| << ")"; | |
| MutexLock lock(&mutex_); | |
| 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 |