| /* |
| * 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 <cassert> |
| |
| #include "audio_mixer_manager_pulse_linux.h" |
| #include "trace.h" |
| |
| extern webrtc_adm_linux_pulse::PulseAudioSymbolTable PaSymbolTable; |
| |
| // Accesses Pulse functions through our late-binding symbol table instead of |
| // directly. This way we don't have to link to libpulse, which means our binary |
| // will work on systems that don't have it. |
| #define LATE(sym) \ |
| LATESYM_GET(webrtc_adm_linux_pulse::PulseAudioSymbolTable, &PaSymbolTable, sym) |
| |
| namespace webrtc |
| { |
| |
| enum { kMaxRetryOnFailure = 2 }; |
| |
| AudioMixerManagerLinuxPulse::AudioMixerManagerLinuxPulse(const WebRtc_Word32 id) : |
| _critSect(*CriticalSectionWrapper::CreateCriticalSection()), |
| _id(id), |
| _paOutputDeviceIndex(-1), |
| _paInputDeviceIndex(-1), |
| _paPlayStream(NULL), |
| _paRecStream(NULL), |
| _paMainloop(NULL), |
| _paContext(NULL), |
| _paVolume(0), |
| _paMute(0), |
| _paVolSteps(0), |
| _paSpeakerMute(false), |
| _paSpeakerVolume(0), |
| _paChannels(0), |
| _paObjectsSet(false), |
| _callbackValues(false) |
| { |
| WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, |
| "%s constructed", __FUNCTION__); |
| } |
| |
| AudioMixerManagerLinuxPulse::~AudioMixerManagerLinuxPulse() |
| { |
| WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, |
| "%s destructed", __FUNCTION__); |
| |
| Close(); |
| |
| delete &_critSect; |
| } |
| |
| // ============================================================================ |
| // PUBLIC METHODS |
| // ============================================================================ |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::SetPulseAudioObjects( |
| pa_threaded_mainloop* mainloop, |
| pa_context* context) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", |
| __FUNCTION__); |
| |
| CriticalSectionScoped lock(_critSect); |
| |
| if (!mainloop || !context) |
| { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| " could not set PulseAudio objects for mixer"); |
| return -1; |
| } |
| |
| _paMainloop = mainloop; |
| _paContext = context; |
| _paObjectsSet = true; |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| " the PulseAudio objects for the mixer has been set"); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::Close() |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", |
| __FUNCTION__); |
| |
| CriticalSectionScoped lock(_critSect); |
| |
| CloseSpeaker(); |
| CloseMicrophone(); |
| |
| _paMainloop = NULL; |
| _paContext = NULL; |
| _paObjectsSet = false; |
| |
| return 0; |
| |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::CloseSpeaker() |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", |
| __FUNCTION__); |
| |
| CriticalSectionScoped lock(_critSect); |
| |
| // Reset the index to -1 |
| _paOutputDeviceIndex = -1; |
| _paPlayStream = NULL; |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::CloseMicrophone() |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "%s", |
| __FUNCTION__); |
| |
| CriticalSectionScoped lock(_critSect); |
| |
| // Reset the index to -1 |
| _paInputDeviceIndex = -1; |
| _paRecStream = NULL; |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::SetPlayStream(pa_stream* playStream) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| "AudioMixerManagerLinuxPulse::SetPlayStream(playStream)"); |
| |
| CriticalSectionScoped lock(_critSect); |
| _paPlayStream = playStream; |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::SetRecStream(pa_stream* recStream) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| "AudioMixerManagerLinuxPulse::SetRecStream(recStream)"); |
| |
| CriticalSectionScoped lock(_critSect); |
| _paRecStream = recStream; |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::OpenSpeaker( |
| WebRtc_UWord16 deviceIndex) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| "AudioMixerManagerLinuxPulse::OpenSpeaker(deviceIndex=%d)", |
| deviceIndex); |
| |
| CriticalSectionScoped lock(_critSect); |
| |
| // No point in opening the speaker |
| // if PA objects have not been set |
| if (!_paObjectsSet) |
| { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| " PulseAudio objects has not been set"); |
| return -1; |
| } |
| |
| // Set the index for the PulseAudio |
| // output device to control |
| _paOutputDeviceIndex = deviceIndex; |
| |
| // Init the speaker volume to the normal volume |
| _paSpeakerVolume = PA_VOLUME_NORM; |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| " the output mixer device is now open"); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::OpenMicrophone( |
| WebRtc_UWord16 deviceIndex) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| "AudioMixerManagerLinuxPulse::OpenMicrophone(deviceIndex=%d)", |
| deviceIndex); |
| |
| CriticalSectionScoped lock(_critSect); |
| |
| // No point in opening the microphone |
| // if PA objects have not been set |
| if (!_paObjectsSet) |
| { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| " PulseAudio objects have not been set"); |
| return -1; |
| } |
| |
| // Set the index for the PulseAudio |
| // input device to control |
| _paInputDeviceIndex = deviceIndex; |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| " the input mixer device is now open"); |
| |
| return 0; |
| } |
| |
| bool AudioMixerManagerLinuxPulse::SpeakerIsInitialized() const |
| { |
| WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", |
| __FUNCTION__); |
| |
| return (_paOutputDeviceIndex != -1); |
| } |
| |
| bool AudioMixerManagerLinuxPulse::MicrophoneIsInitialized() const |
| { |
| WEBRTC_TRACE(kTraceMemory, kTraceAudioDevice, _id, "%s", |
| __FUNCTION__); |
| |
| return (_paInputDeviceIndex != -1); |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::SetSpeakerVolume( |
| WebRtc_UWord32 volume) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| "AudioMixerManagerLinuxPulse::SetSpeakerVolume(volume=%u)", |
| volume); |
| |
| CriticalSectionScoped lock(_critSect); |
| |
| if (_paOutputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " output device index has not been set"); |
| return -1; |
| } |
| |
| bool setFailed(false); |
| |
| if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) |
| != PA_STREAM_UNCONNECTED)) |
| { |
| // We can only really set the volume if we have a connected stream |
| PaLock(); |
| |
| // Get the number of channels from the sample specification |
| const pa_sample_spec *spec = |
| LATE(pa_stream_get_sample_spec)(_paPlayStream); |
| if (!spec) |
| { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| " could not get sample specification"); |
| PaUnLock(); |
| return -1; |
| } |
| |
| // Set the same volume for all channels |
| pa_cvolume cVolumes; |
| LATE(pa_cvolume_set)(&cVolumes, spec->channels, volume); |
| |
| pa_operation* paOperation = NULL; |
| paOperation = LATE(pa_context_set_sink_input_volume)( |
| _paContext, |
| LATE(pa_stream_get_index)(_paPlayStream), |
| &cVolumes, |
| PaSetVolumeCallback, NULL); |
| if (!paOperation) |
| { |
| setFailed = true; |
| } |
| |
| // Don't need to wait for the completion |
| LATE(pa_operation_unref)(paOperation); |
| |
| PaUnLock(); |
| } else |
| { |
| // We have not created a stream or it's not connected to the sink |
| // Save the volume to be set at connection |
| _paSpeakerVolume = volume; |
| } |
| |
| if (setFailed) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " could not set speaker volume, error%d", |
| LATE(pa_context_errno)(_paContext)); |
| |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::SpeakerVolume(WebRtc_UWord32& volume) const |
| { |
| |
| if (_paOutputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " output device index has not been set"); |
| return -1; |
| } |
| |
| if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) |
| != PA_STREAM_UNCONNECTED)) |
| { |
| // We can only get the volume if we have a connected stream |
| if (!GetSinkInputInfo()) |
| return -1; |
| |
| volume = static_cast<WebRtc_UWord32> (_paVolume); |
| ResetCallbackVariables(); |
| } else |
| { |
| volume = _paSpeakerVolume; |
| } |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| " AudioMixerManagerLinuxPulse::SpeakerVolume() => vol=%i", |
| volume); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::MaxSpeakerVolume(WebRtc_UWord32& maxVolume) const |
| { |
| |
| if (_paOutputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " output device index has not been set"); |
| return -1; |
| } |
| |
| // PA_VOLUME_NORM corresponds to 100% (0db) |
| // but PA allows up to 150 db amplification |
| maxVolume = static_cast<WebRtc_UWord32> (PA_VOLUME_NORM); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::MinSpeakerVolume(WebRtc_UWord32& minVolume) const |
| { |
| |
| if (_paOutputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " output device index has not been set"); |
| return -1; |
| } |
| |
| minVolume = static_cast<WebRtc_UWord32> (PA_VOLUME_MUTED); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::SpeakerVolumeStepSize(WebRtc_UWord16& stepSize) const |
| { |
| |
| if (_paOutputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " output device index has not been set"); |
| return -1; |
| } |
| |
| // The sink input (stream) will always have step size = 1 |
| // There are PA_VOLUME_NORM+1 steps |
| stepSize = 1; |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| " AudioMixerManagerLinuxPulse::SpeakerVolumeStepSize() => " |
| "size=%i, stepSize"); |
| |
| // Reset members modified by callback |
| ResetCallbackVariables(); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::SpeakerVolumeIsAvailable(bool& available) |
| { |
| if (_paOutputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " output device index has not been set"); |
| return -1; |
| } |
| |
| // Always available in Pulse Audio |
| available = true; |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::SpeakerMuteIsAvailable(bool& available) |
| { |
| if (_paOutputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " output device index has not been set"); |
| return -1; |
| } |
| |
| // Always available in Pulse Audio |
| available = true; |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::SetSpeakerMute(bool enable) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| "AudioMixerManagerLinuxPulse::SetSpeakerMute(enable=%u)", |
| enable); |
| |
| CriticalSectionScoped lock(_critSect); |
| |
| if (_paOutputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " output device index has not been set"); |
| return -1; |
| } |
| |
| bool setFailed(false); |
| |
| if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) |
| != PA_STREAM_UNCONNECTED)) |
| { |
| // We can only really mute if we have a connected stream |
| PaLock(); |
| |
| pa_operation* paOperation = NULL; |
| paOperation = LATE(pa_context_set_sink_input_mute)( |
| _paContext, |
| LATE(pa_stream_get_index)(_paPlayStream), |
| (int) enable, |
| PaSetVolumeCallback, |
| NULL); |
| if (!paOperation) |
| { |
| setFailed = true; |
| } |
| |
| // Don't need to wait for the completion |
| LATE(pa_operation_unref)(paOperation); |
| |
| PaUnLock(); |
| } else |
| { |
| // We have not created a stream or it's not connected to the sink |
| // Save the mute status to be set at connection |
| _paSpeakerMute = enable; |
| } |
| |
| if (setFailed) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " could not mute speaker, error%d", |
| LATE(pa_context_errno)(_paContext)); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::SpeakerMute(bool& enabled) const |
| { |
| |
| if (_paOutputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " output device index has not been set"); |
| return -1; |
| } |
| |
| if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) |
| != PA_STREAM_UNCONNECTED)) |
| { |
| // We can only get the mute status if we have a connected stream |
| if (!GetSinkInputInfo()) |
| return -1; |
| |
| enabled = static_cast<bool> (_paMute); |
| ResetCallbackVariables(); |
| } else |
| { |
| enabled = _paSpeakerMute; |
| } |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| " AudioMixerManagerLinuxPulse::SpeakerMute() => " |
| "enabled=%i, enabled"); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::StereoPlayoutIsAvailable(bool& available) |
| { |
| if (_paOutputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " output device index has not been set"); |
| return -1; |
| } |
| |
| uint32_t deviceIndex = (uint32_t) _paOutputDeviceIndex; |
| |
| PaLock(); |
| |
| // Get the actual stream device index if we have a connected stream |
| // The device used by the stream can be changed |
| // during the call |
| if (_paPlayStream && (LATE(pa_stream_get_state)(_paPlayStream) |
| != PA_STREAM_UNCONNECTED)) |
| { |
| deviceIndex = LATE(pa_stream_get_device_index)(_paPlayStream); |
| } |
| |
| PaUnLock(); |
| |
| if (!GetSinkInfoByIndex(deviceIndex)) |
| return -1; |
| |
| available = static_cast<bool> (_paChannels == 2); |
| |
| // Reset members modified by callback |
| ResetCallbackVariables(); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable(bool& available) |
| { |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; |
| |
| PaLock(); |
| |
| // Get the actual stream device index if we have a connected stream |
| // The device used by the stream can be changed |
| // during the call |
| if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) |
| != PA_STREAM_UNCONNECTED)) |
| { |
| deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); |
| } |
| |
| pa_operation* paOperation = NULL; |
| ResetCallbackVariables(); |
| |
| // Get info for this source |
| // We want to know if the actual device can record in stereo |
| paOperation = LATE(pa_context_get_source_info_by_index)( |
| _paContext, deviceIndex, |
| PaSourceInfoCallback, |
| (void*) this); |
| |
| WaitForOperationCompletion(paOperation); |
| PaUnLock(); |
| |
| if (!_callbackValues) |
| { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| "Error getting number of input channels: %d", |
| LATE(pa_context_errno)(_paContext)); |
| return -1; |
| } |
| |
| available = static_cast<bool> (_paChannels == 2); |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| " AudioMixerManagerLinuxPulse::StereoRecordingIsAvailable()" |
| " => available=%i, available"); |
| |
| // Reset members modified by callback |
| ResetCallbackVariables(); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::MicrophoneMuteIsAvailable( |
| bool& available) |
| { |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| // Always available in Pulse Audio |
| available = true; |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::SetMicrophoneMute(bool enable) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| "AudioMixerManagerLinuxPulse::SetMicrophoneMute(enable=%u)", |
| enable); |
| |
| CriticalSectionScoped lock(_critSect); |
| |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| bool setFailed(false); |
| pa_operation* paOperation = NULL; |
| ResetCallbackVariables(); |
| |
| uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; |
| |
| PaLock(); |
| |
| // Get the actual stream device index if we have a connected stream |
| // The device used by the stream can be changed |
| // during the call |
| if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) |
| != PA_STREAM_UNCONNECTED)) |
| { |
| deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); |
| } |
| |
| // Set mute switch for the source |
| paOperation = LATE(pa_context_set_source_mute_by_index)( |
| _paContext, deviceIndex, |
| enable, |
| PaSetVolumeCallback, NULL); |
| |
| if (!paOperation) |
| { |
| setFailed = true; |
| } |
| |
| // Don't need to wait for this to complete. |
| LATE(pa_operation_unref)(paOperation); |
| |
| PaUnLock(); |
| |
| // Reset variables altered by callback |
| ResetCallbackVariables(); |
| |
| if (setFailed) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " could not mute microphone, error%d", |
| LATE(pa_context_errno)(_paContext)); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::MicrophoneMute(bool& enabled) const |
| { |
| |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; |
| |
| PaLock(); |
| |
| // Get the actual stream device index if we have a connected stream |
| // The device used by the stream can be changed |
| // during the call |
| if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) |
| != PA_STREAM_UNCONNECTED)) |
| { |
| deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); |
| } |
| |
| PaUnLock(); |
| |
| if (!GetSourceInfoByIndex(deviceIndex)) |
| return -1; |
| |
| enabled = static_cast<bool> (_paMute); |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| " AudioMixerManagerLinuxPulse::MicrophoneMute() =>" |
| " enabled=%i, enabled"); |
| |
| // Reset members modified by callback |
| ResetCallbackVariables(); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::MicrophoneBoostIsAvailable(bool& available) |
| { |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| // Always unavailable in Pulse Audio |
| // Could make it possible to use PA_VOLUME_MAX |
| // but that gives bad audio with some sound cards |
| available = false; |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::SetMicrophoneBoost(bool enable) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| "AudioMixerManagerLinuxPulse::SetMicrophoneBoost(enable=%u)", |
| enable); |
| |
| CriticalSectionScoped lock(_critSect); |
| |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| // Ensure that the selected microphone destination has a valid boost control |
| bool available(false); |
| MicrophoneBoostIsAvailable(available); |
| if (!available) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " it is not possible to enable microphone boost"); |
| return -1; |
| } |
| |
| // It is assumed that the call above fails! |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::MicrophoneBoost(bool& enabled) const |
| { |
| |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| // Microphone boost cannot be enabled on this platform! |
| enabled = false; |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::MicrophoneVolumeIsAvailable( |
| bool& available) |
| { |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| // Always available in Pulse Audio |
| available = true; |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::SetMicrophoneVolume(WebRtc_UWord32 volume) |
| { |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| "AudioMixerManagerLinuxPulse::SetMicrophoneVolume(volume=%u)", |
| volume); |
| |
| CriticalSectionScoped lock(_critSect); |
| |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| // Unlike output streams, input streams have no concept of a stream volume, |
| // only a device volume. So we have to change the volume of the device |
| // itself. |
| |
| // The device may have a different number of channels than the stream and |
| // their mapping may be different, so we don't want to use the channel count |
| // from our sample spec. We could use PA_CHANNELS_MAX to cover our bases, |
| // and the server allows that even if the device's channel count is lower, |
| // but some buggy PA clients don't like that (the pavucontrol on Hardy dies |
| // in an assert if the channel count is different). So instead we look up |
| // the actual number of channels that the device has. |
| |
| uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; |
| |
| PaLock(); |
| |
| // Get the actual stream device index if we have a connected stream |
| // The device used by the stream can be changed |
| // during the call |
| if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) |
| != PA_STREAM_UNCONNECTED)) |
| { |
| deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); |
| } |
| |
| bool setFailed(false); |
| pa_operation* paOperation = NULL; |
| ResetCallbackVariables(); |
| |
| // Get the number of channels for this source |
| paOperation |
| = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex, |
| PaSourceInfoCallback, |
| (void*) this); |
| |
| WaitForOperationCompletion(paOperation); |
| |
| if (!_callbackValues) |
| { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| "Error getting input channels: %d", |
| LATE(pa_context_errno)(_paContext)); |
| PaUnLock(); |
| return -1; |
| } |
| |
| WebRtc_UWord8 channels = _paChannels; |
| ResetCallbackVariables(); |
| |
| pa_cvolume cVolumes; |
| LATE(pa_cvolume_set)(&cVolumes, channels, volume); |
| |
| // Set the volume for the source |
| paOperation |
| = LATE(pa_context_set_source_volume_by_index)(_paContext, deviceIndex, |
| &cVolumes, |
| PaSetVolumeCallback, NULL); |
| |
| if (!paOperation) |
| { |
| setFailed = true; |
| } |
| |
| // Don't need to wait for this to complete. |
| LATE(pa_operation_unref)(paOperation); |
| |
| PaUnLock(); |
| |
| // Reset variables altered by callback |
| ResetCallbackVariables(); |
| |
| if (setFailed) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " could not set microphone volume, error%d", |
| LATE(pa_context_errno)(_paContext)); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::MicrophoneVolume(WebRtc_UWord32& volume) const |
| { |
| |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; |
| |
| PaLock(); |
| |
| // Get the actual stream device index if we have a connected stream |
| // The device used by the stream can be changed |
| // during the call |
| if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) |
| != PA_STREAM_UNCONNECTED)) |
| { |
| deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); |
| } |
| |
| PaUnLock(); |
| |
| if (!GetSourceInfoByIndex(deviceIndex)) |
| return -1; |
| |
| volume = static_cast<WebRtc_UWord32> (_paVolume); |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| " AudioMixerManagerLinuxPulse::MicrophoneVolume() => vol=%i, volume"); |
| |
| // Reset members modified by callback |
| ResetCallbackVariables(); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::MaxMicrophoneVolume(WebRtc_UWord32& maxVolume) const |
| { |
| |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| // PA_VOLUME_NORM corresponds to 100% (0db) |
| // PA allows up to 150 db amplification (PA_VOLUME_MAX) |
| // but that doesn't work well for all sound cards |
| maxVolume = static_cast<WebRtc_UWord32> (PA_VOLUME_NORM); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 |
| AudioMixerManagerLinuxPulse::MinMicrophoneVolume(WebRtc_UWord32& minVolume) const |
| { |
| |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| minVolume = static_cast<WebRtc_UWord32> (PA_VOLUME_MUTED); |
| |
| return 0; |
| } |
| |
| WebRtc_Word32 AudioMixerManagerLinuxPulse::MicrophoneVolumeStepSize( |
| WebRtc_UWord16& stepSize) const |
| { |
| |
| if (_paInputDeviceIndex == -1) |
| { |
| WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, |
| " input device index has not been set"); |
| return -1; |
| } |
| |
| uint32_t deviceIndex = (uint32_t) _paInputDeviceIndex; |
| |
| PaLock(); |
| |
| // Get the actual stream device index if we have a connected stream |
| // The device used by the stream can be changed |
| // during the call |
| if (_paRecStream && (LATE(pa_stream_get_state)(_paRecStream) |
| != PA_STREAM_UNCONNECTED)) |
| { |
| deviceIndex = LATE(pa_stream_get_device_index)(_paRecStream); |
| } |
| |
| pa_operation* paOperation = NULL; |
| ResetCallbackVariables(); |
| |
| // Get info for this source |
| paOperation |
| = LATE(pa_context_get_source_info_by_index)(_paContext, deviceIndex, |
| PaSourceInfoCallback, |
| (void*) this); |
| |
| WaitForOperationCompletion(paOperation); |
| |
| PaUnLock(); |
| |
| if (!_callbackValues) |
| { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| "Error getting step size: %d", |
| LATE(pa_context_errno)(_paContext)); |
| return -1; |
| } |
| |
| stepSize = static_cast<WebRtc_UWord16> ((PA_VOLUME_NORM + 1) / _paVolSteps); |
| |
| WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, |
| " AudioMixerManagerLinuxPulse::MicrophoneVolumeStepSize()" |
| " => size=%i, stepSize"); |
| |
| // Reset members modified by callback |
| ResetCallbackVariables(); |
| |
| return 0; |
| } |
| |
| // ============================================================================ |
| // Private Methods |
| // ============================================================================ |
| |
| void AudioMixerManagerLinuxPulse::PaSinkInfoCallback(pa_context */*c*/, |
| const pa_sink_info *i, |
| int eol, void *pThis) |
| { |
| static_cast<AudioMixerManagerLinuxPulse*> (pThis)-> PaSinkInfoCallbackHandler( |
| i, eol); |
| } |
| |
| void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallback( |
| pa_context */*c*/, |
| const pa_sink_input_info *i, |
| int eol, void *pThis) |
| { |
| static_cast<AudioMixerManagerLinuxPulse*> (pThis)-> |
| PaSinkInputInfoCallbackHandler(i, eol); |
| } |
| |
| |
| void AudioMixerManagerLinuxPulse::PaSourceInfoCallback(pa_context */*c*/, |
| const pa_source_info *i, |
| int eol, void *pThis) |
| { |
| static_cast<AudioMixerManagerLinuxPulse*> (pThis)-> |
| PaSourceInfoCallbackHandler(i, eol); |
| } |
| |
| void AudioMixerManagerLinuxPulse::PaSetVolumeCallback(pa_context * c, |
| int success, void */*pThis*/) |
| { |
| if (!success) |
| { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1, |
| " failed to set volume"); |
| } |
| } |
| |
| void AudioMixerManagerLinuxPulse::PaSinkInfoCallbackHandler( |
| const pa_sink_info *i, |
| int eol) |
| { |
| if (eol) |
| { |
| // Signal that we are done |
| LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); |
| return; |
| } |
| |
| _callbackValues = true; |
| _paChannels = i->channel_map.channels; // Get number of channels |
| pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value. |
| for (int j = 0; j < _paChannels; ++j) |
| { |
| if (paVolume < i->volume.values[j]) |
| { |
| paVolume = i->volume.values[j]; |
| } |
| } |
| _paVolume = paVolume; // get the max volume for any channel |
| _paMute = i->mute; // get mute status |
| |
| // supported since PA 0.9.15 |
| //_paVolSteps = i->n_volume_steps; // get the number of volume steps |
| // default value is PA_VOLUME_NORM+1 |
| _paVolSteps = PA_VOLUME_NORM + 1; |
| } |
| |
| void AudioMixerManagerLinuxPulse::PaSinkInputInfoCallbackHandler( |
| const pa_sink_input_info *i, |
| int eol) |
| { |
| if (eol) |
| { |
| // Signal that we are done. |
| LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); |
| return; |
| } |
| |
| _callbackValues = true; |
| _paChannels = i->channel_map.channels; // Get number of channels |
| pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value. |
| for (int j = 0; j < _paChannels; ++j) |
| { |
| if (paVolume < i->volume.values[j]) |
| { |
| paVolume = i->volume.values[j]; |
| } |
| } |
| _paVolume = paVolume; // Get the max volume for any channel |
| _paMute = i->mute; // Get mute status |
| } |
| |
| void AudioMixerManagerLinuxPulse::PaSourceInfoCallbackHandler( |
| const pa_source_info *i, |
| int eol) |
| { |
| if (eol) |
| { |
| // Signal that we are done |
| LATE(pa_threaded_mainloop_signal)(_paMainloop, 0); |
| return; |
| } |
| |
| _callbackValues = true; |
| _paChannels = i->channel_map.channels; // Get number of channels |
| pa_volume_t paVolume = PA_VOLUME_MUTED; // Minimum possible value. |
| for (int j = 0; j < _paChannels; ++j) |
| { |
| if (paVolume < i->volume.values[j]) |
| { |
| paVolume = i->volume.values[j]; |
| } |
| } |
| _paVolume = paVolume; // Get the max volume for any channel |
| _paMute = i->mute; // Get mute status |
| |
| // supported since PA 0.9.15 |
| //_paVolSteps = i->n_volume_steps; // Get the number of volume steps |
| // default value is PA_VOLUME_NORM+1 |
| _paVolSteps = PA_VOLUME_NORM + 1; |
| } |
| |
| void AudioMixerManagerLinuxPulse::ResetCallbackVariables() const |
| { |
| _paVolume = 0; |
| _paMute = 0; |
| _paVolSteps = 0; |
| _paChannels = 0; |
| _callbackValues = false; |
| } |
| |
| void AudioMixerManagerLinuxPulse::WaitForOperationCompletion( |
| pa_operation* paOperation) const |
| { |
| while (LATE(pa_operation_get_state)(paOperation) == PA_OPERATION_RUNNING) |
| { |
| LATE(pa_threaded_mainloop_wait)(_paMainloop); |
| } |
| |
| LATE(pa_operation_unref)(paOperation); |
| } |
| |
| void AudioMixerManagerLinuxPulse::PaLock() const |
| { |
| LATE(pa_threaded_mainloop_lock)(_paMainloop); |
| } |
| |
| void AudioMixerManagerLinuxPulse::PaUnLock() const |
| { |
| LATE(pa_threaded_mainloop_unlock)(_paMainloop); |
| } |
| |
| bool AudioMixerManagerLinuxPulse::GetSinkInputInfo() const { |
| pa_operation* paOperation = NULL; |
| ResetCallbackVariables(); |
| |
| PaLock(); |
| for (int retries = 0; retries < kMaxRetryOnFailure && !_callbackValues; |
| retries ++) { |
| // Get info for this stream (sink input). |
| paOperation = LATE(pa_context_get_sink_input_info)( |
| _paContext, |
| LATE(pa_stream_get_index)(_paPlayStream), |
| PaSinkInputInfoCallback, |
| (void*) this); |
| |
| WaitForOperationCompletion(paOperation); |
| } |
| PaUnLock(); |
| |
| if (!_callbackValues) { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| "GetSinkInputInfo failed to get volume info : %d", |
| LATE(pa_context_errno)(_paContext)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AudioMixerManagerLinuxPulse::GetSinkInfoByIndex( |
| int device_index) const { |
| pa_operation* paOperation = NULL; |
| ResetCallbackVariables(); |
| |
| PaLock(); |
| for (int retries = 0; retries < kMaxRetryOnFailure && !_callbackValues; |
| retries ++) { |
| paOperation = LATE(pa_context_get_sink_info_by_index)(_paContext, |
| device_index, PaSinkInfoCallback, (void*) this); |
| |
| WaitForOperationCompletion(paOperation); |
| } |
| PaUnLock(); |
| |
| if (!_callbackValues) { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| "GetSinkInfoByIndex failed to get volume info: %d", |
| LATE(pa_context_errno)(_paContext)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool AudioMixerManagerLinuxPulse::GetSourceInfoByIndex( |
| int device_index) const { |
| pa_operation* paOperation = NULL; |
| ResetCallbackVariables(); |
| |
| PaLock(); |
| for (int retries = 0; retries < kMaxRetryOnFailure && !_callbackValues; |
| retries ++) { |
| paOperation = LATE(pa_context_get_source_info_by_index)( |
| _paContext, device_index, PaSourceInfoCallback, (void*) this); |
| |
| WaitForOperationCompletion(paOperation); |
| } |
| |
| PaUnLock(); |
| |
| if (!_callbackValues) { |
| WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id, |
| "GetSourceInfoByIndex error: %d", |
| LATE(pa_context_errno)(_paContext)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } |
| |