blob: 01f5172702d81aacab9f349e349313db0e271035 [file] [log] [blame]
/*
* 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;
}
}