Commit 52508664 authored by Андрей Лухнов's avatar Андрей Лухнов Committed by Tristan Matthews
Browse files

* #12014: add ability to select pulse devices in daemon


Signed-off-by: default avatarTristan Matthews <tristan.matthews@savoirfairelinux.com>
parent 03c82382
......@@ -68,15 +68,14 @@ void AlsaThread::run()
}
// Constructor
AlsaLayer::AlsaLayer()
: indexIn_(audioPref.getCardin())
, indexOut_(audioPref.getCardout())
, indexRing_(audioPref.getCardring())
AlsaLayer::AlsaLayer(const AudioPreference &pref)
: indexIn_(pref.getCardin())
, indexOut_(pref.getCardout())
, indexRing_(pref.getCardring())
, playbackHandle_(NULL)
, ringtoneHandle_(NULL)
, captureHandle_(NULL)
, audioPlugin_(audioPref.getPlugin())
// , IDSoundCards_()
, audioPlugin_(pref.getPlugin())
, is_playback_prepared_(false)
, is_capture_prepared_(false)
, is_playback_running_(false)
......@@ -85,8 +84,8 @@ AlsaLayer::AlsaLayer()
, is_capture_open_(false)
, audioThread_(NULL)
{
setCaptureGain(Manager::instance().audioPreference.getVolumemic());
setPlaybackGain(Manager::instance().audioPreference.getVolumespkr());
setCaptureGain(pref.getVolumemic());
setPlaybackGain(pref.getVolumespkr());
}
// Destructor
......@@ -510,9 +509,8 @@ AlsaLayer::getAudioDeviceIndexMap(bool getCapture) const
bool
AlsaLayer::soundCardIndexExists(int card, int stream)
AlsaLayer::soundCardIndexExists(int card, PCMType stream)
{
snd_ctl_t* handle;
snd_pcm_info_t *pcminfo;
snd_pcm_info_alloca(&pcminfo);
std::string name("hw:");
......@@ -520,6 +518,7 @@ AlsaLayer::soundCardIndexExists(int card, int stream)
ss << card;
name.append(ss.str());
snd_ctl_t* handle;
if (snd_ctl_open(&handle, name.c_str(), 0) != 0)
return false;
......@@ -547,6 +546,24 @@ AlsaLayer::getAudioDeviceIndex(const std::string &description) const
return 0;
}
std::string
AlsaLayer::getAudioDeviceName(int index, PCMType type) const
{
// a bit ugly and wrong.. i do not know how to implement it better in alsalayer.
// in addition, for now it is used in pulselayer only due to alsa and pulse layers api differences.
// but after some tweaking in alsalayer, it could be used in it too.
switch (type) {
case SFL_PCM_PLAYBACK:
case SFL_PCM_RINGTONE:
return getPlaybackDeviceList().at(index);
case SFL_PCM_CAPTURE:
return getCaptureDeviceList().at(index);
default:
ERROR("Unexpected type %d", type);
return "";
}
}
void AlsaLayer::capture()
{
unsigned int mainBufferSampleRate = Manager::instance().getMainBuffer()->getInternalSamplingRate();
......@@ -711,3 +728,20 @@ void AlsaLayer::audioCallback()
if (is_capture_running_)
capture();
}
void AlsaLayer::updatePreference(AudioPreference &preference, int index, PCMType type)
{
switch (type) {
case SFL_PCM_PLAYBACK:
preference.setCardout(index);
break;
case AudioLayer::SFL_PCM_CAPTURE:
preference.setCardin(index);
break;
case AudioLayer::SFL_PCM_RINGTONE:
preference.setCardring(index);
break;
default:
break;
}
}
......@@ -53,7 +53,7 @@ class AlsaLayer : public AudioLayer {
/**
* Constructor
*/
AlsaLayer();
AlsaLayer(const AudioPreference &pref);
/**
* Destructor
......@@ -99,7 +99,7 @@ class AlsaLayer : public AudioLayer {
* @return bool True if it exists and supports the mode
* false otherwise
*/
static bool soundCardIndexExists(int card, int stream);
static bool soundCardIndexExists(int card, PCMType stream);
/**
* An index is associated with its string description
......@@ -107,6 +107,7 @@ class AlsaLayer : public AudioLayer {
* @return int Its index
*/
int getAudioDeviceIndex(const std::string &description) const;
std::string getAudioDeviceName(int index, PCMType type) const;
void playback(int maxSamples);
void capture();
......@@ -204,6 +205,8 @@ class AlsaLayer : public AudioLayer {
*/
int read(void* buffer, int toCopy);
virtual void updatePreference(AudioPreference &pref, int index, PCMType type);
/**
* Handles to manipulate playback stream
* ALSA Library API
......
......@@ -43,7 +43,6 @@ AudioLayer::AudioLayer()
, sampleRate_(Manager::instance().getMainBuffer()->getInternalSamplingRate())
, mutex_()
, dcblocker_()
, audioPref(Manager::instance().audioPreference)
, converter_(sampleRate_)
, lastNotificationTime_(0)
{
......
......@@ -55,16 +55,27 @@ class Time;
}
class AudioLayer {
private:
NON_COPYABLE(AudioLayer);
public:
enum PCMType {
SFL_PCM_BOTH = 0x0021, /** To open both playback and capture devices */
SFL_PCM_PLAYBACK = 0x0022, /** To open playback device only */
SFL_PCM_CAPTURE = 0x0023, /** To open capture device only */
SFL_PCM_RINGTONE = 0x0024 /** To open the ringtone device only */
};
AudioLayer();
virtual ~AudioLayer() {}
virtual std::vector<std::string> getCaptureDeviceList() const = 0;
virtual std::vector<std::string> getPlaybackDeviceList() const = 0;
virtual int getAudioDeviceIndex(const std::string& name) const = 0;
virtual std::string getAudioDeviceName(int index, PCMType type) const = 0;
/**
* Start the capture stream and prepare the playback stream.
* The playback starts accordingly to its threshold
......@@ -167,7 +178,7 @@ class AudioLayer {
return &mutex_;
}
/**
/**
* Emit an audio notification on incoming calls
*/
void notifyIncomingCall();
......@@ -182,10 +193,11 @@ class AudioLayer {
*/
static unsigned int playbackGain_;
protected:
virtual void updatePreference(AudioPreference &pref, int index, PCMType type) = 0;
protected:
/**
* Wether or not the audio layer stream is started
* Whether or not the audio layer stream is started
*/
bool isStarted_;
......@@ -210,11 +222,6 @@ class AudioLayer {
*/
DcBlocker dcblocker_;
/**
* Configuration file for this
*/
AudioPreference &audioPref;
/**
* Manage sampling rate conversion
*/
......
......@@ -38,7 +38,7 @@
#include "logger.h"
#include "manager.h"
#include <stdlib.h>
#include <cstdlib>
#include <fstream>
namespace {
......@@ -70,7 +70,7 @@ std::ofstream outfileResampled ("testMicOuputResampled.raw", std::ifstream::bina
std::ofstream outfile("testMicOuput.raw", std::ifstream::binary);
#endif
PulseLayer::PulseLayer()
PulseLayer::PulseLayer(AudioPreference &pref)
: playback_(0)
, record_(0)
, ringtone_(0)
......@@ -80,10 +80,12 @@ PulseLayer::PulseLayer()
, mic_buf_size_(0)
, context_(0)
, mainloop_(pa_threaded_mainloop_new())
, enumeratingSinks_(false)
, enumeratingSources_(false)
, preference_(pref)
{
setenv("PULSE_PROP_media.role", "phone", 1);
if (!mainloop_)
throw std::runtime_error("Couldn't create pulseaudio mainloop");
......@@ -174,24 +176,30 @@ void PulseLayer::context_state_callback(pa_context* c, void *user_data)
void PulseLayer::updateSinkList()
{
sinkList_.clear();
enumeratingSinks_ = true;
pa_context_get_sink_info_list(context_, sink_input_info_callback, this);
}
void PulseLayer::updateSourceList()
{
sourceList_.clear();
enumeratingSources_ = true;
pa_context_get_source_info_list(context_, source_input_info_callback, this);
}
bool PulseLayer::inSinkList(const std::string &deviceName) const
{
return std::find(sinkList_.begin(), sinkList_.end(), deviceName) != sinkList_.end();
bool found = std::find(sinkList_.begin(), sinkList_.end(), deviceName) != sinkList_.end();
DEBUG("seeking for %s in sinks. %s found", deviceName.c_str(), found?"":"NOT");
return found;
}
bool PulseLayer::inSourceList(const std::string &deviceName) const
{
return std::find(sourceList_.begin(), sourceList_.end(), deviceName) != sourceList_.end();
bool found = std::find(sourceList_.begin(), sourceList_.end(), deviceName) != sourceList_.end();
DEBUG("seeking for %s in sources. %s found", deviceName.c_str(), found?"":"NOT");
return found;
}
std::vector<std::string> PulseLayer::getCaptureDeviceList() const
......@@ -204,11 +212,37 @@ std::vector<std::string> PulseLayer::getPlaybackDeviceList() const
return sinkList_;
}
int PulseLayer::getAudioDeviceIndex(const std::string& name) const
{
int index = std::distance(sourceList_.begin(), std::find(sourceList_.begin(), sourceList_.end(), name));
if (index == std::distance(sourceList_.begin(), sourceList_.end())) {
// not found in sources, search in sinks then
index = std::distance(sinkList_.begin(), std::find(sinkList_.begin(), sinkList_.end(), name));
}
return index;
}
std::string PulseLayer::getAudioDeviceName(int index, PCMType type) const
{
switch (type) {
case SFL_PCM_PLAYBACK:
case SFL_PCM_RINGTONE:
return sinkList_.at(index);
case SFL_PCM_CAPTURE:
return sourceList_.at(index);
default:
return "";
}
}
void PulseLayer::createStreams(pa_context* c)
{
std::string playbackDevice(audioPref.getDevicePlayback());
std::string captureDevice(audioPref.getDeviceRecord());
std::string ringtoneDevice(audioPref.getDeviceRingtone());
while (enumeratingSinks_ or enumeratingSources_)
ost::Thread::sleep(20 /* ms */);
std::string playbackDevice(preference_.getDevicePlayback());
std::string captureDevice(preference_.getDeviceRecord());
std::string ringtoneDevice(preference_.getDeviceRingtone());
std::string defaultDevice = "";
DEBUG("Devices:\n playback: %s\n record: %s\n ringtone: %s",
......@@ -246,7 +280,7 @@ void PulseLayer::disconnectAudioStream()
const char *name = pa_stream_get_device_name(playback_->pulseStream());
if (name && *name)
audioPref.setDevicePlayback(name);
preference_.setDevicePlayback(name);
}
delete playback_;
......@@ -258,7 +292,7 @@ void PulseLayer::disconnectAudioStream()
const char *name = pa_stream_get_device_name(ringtone_->pulseStream());
if (name && *name)
audioPref.setDeviceRingtone(name);
preference_.setDeviceRingtone(name);
}
delete ringtone_;
......@@ -270,7 +304,7 @@ void PulseLayer::disconnectAudioStream()
const char *name = pa_stream_get_device_name(record_->pulseStream());
if (name && *name)
audioPref.setDeviceRecord(name);
preference_.setDeviceRecord(name);
}
delete record_;
......@@ -533,7 +567,7 @@ void PulseLayer::context_changed_callback(pa_context* c, pa_subscription_event_t
DEBUG("PA_SUBSCRIPTION_EVENT_TYPE_MASK");
break;
default:
DEBUG("Unknown event type %d", t);
DEBUG("Unknown event type 0x%x", t);
}
}
......@@ -542,10 +576,12 @@ void PulseLayer::source_input_info_callback(pa_context *c UNUSED, const pa_sourc
{
char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
if (eol)
if (eol) {
static_cast<PulseLayer *>(userdata)->enumeratingSources_ = false;
return;
}
DEBUG("Sink %u\n"
DEBUG("Source %u\n"
" Name: %s\n"
" Driver: %s\n"
" Description: %s\n"
......@@ -577,8 +613,10 @@ void PulseLayer::sink_input_info_callback(pa_context *c UNUSED, const pa_sink_in
{
char s[PA_SAMPLE_SPEC_SNPRINT_MAX], cv[PA_CVOLUME_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX];
if (eol)
if (eol) {
static_cast<PulseLayer *>(userdata)->enumeratingSinks_ = false;
return;
}
DEBUG("Sink %u\n"
" Name: %s\n"
......@@ -608,3 +646,23 @@ void PulseLayer::sink_input_info_callback(pa_context *c UNUSED, const pa_sink_in
static_cast<PulseLayer *>(userdata)->sinkList_.push_back(i->name);
}
void PulseLayer::updatePreference(AudioPreference &preference, int index, PCMType type)
{
const std::string devName(getAudioDeviceName(index, type));
switch (type) {
case SFL_PCM_PLAYBACK:
DEBUG("setting %s for playback", devName.c_str());
preference.setDevicePlayback(devName);
break;
case SFL_PCM_CAPTURE:
DEBUG("setting %s for capture", devName.c_str());
preference.setDeviceRecord(devName);
break;
case SFL_PCM_RINGTONE:
DEBUG("setting %s for ringer", devName.c_str());
preference.setDeviceRingtone(devName);
break;
default:
break;
}
}
......@@ -39,11 +39,12 @@
#include "audio/audiolayer.h"
#include "noncopyable.h"
class AudioPreference;
class AudioStream;
class PulseLayer : public AudioLayer {
public:
PulseLayer();
PulseLayer(AudioPreference &pref);
~PulseLayer();
/**
......@@ -64,12 +65,13 @@ class PulseLayer : public AudioLayer {
virtual std::vector<std::string> getCaptureDeviceList() const;
virtual std::vector<std::string> getPlaybackDeviceList() const;
int getAudioDeviceIndex(const std::string& name) const;
std::string getAudioDeviceName(int index, PCMType type) const;
virtual void startStream();
virtual void stopStream();
private:
static void context_state_callback(pa_context* c, void* user_data);
static void context_changed_callback(pa_context* c,
......@@ -82,6 +84,8 @@ class PulseLayer : public AudioLayer {
const pa_sink_info *i,
int eol, void *userdata);
virtual void updatePreference(AudioPreference &pref, int index, PCMType type);
NON_COPYABLE(PulseLayer);
/**
......@@ -129,6 +133,9 @@ class PulseLayer : public AudioLayer {
/** PulseAudio context and asynchronous loop */
pa_context* context_;
pa_threaded_mainloop* mainloop_;
bool enumeratingSinks_;
bool enumeratingSources_;
AudioPreference &preference_;
friend class AudioLayerTest;
};
......
......@@ -44,6 +44,7 @@
#include "fileutils.h"
#include "sip/sipaccount.h"
#include "../history/historynamecache.h"
#include "../audio/audiolayer.h"
namespace {
const char* SERVER_PATH = "/org/sflphone/SFLphone/ConfigurationManager";
......@@ -240,17 +241,17 @@ std::vector<std::string> ConfigurationManager::getAudioInputDeviceList()
void ConfigurationManager::setAudioOutputDevice(const int32_t& index)
{
return Manager::instance().setAudioDevice(index, SFL_PCM_PLAYBACK);
return Manager::instance().setAudioDevice(index, AudioLayer::SFL_PCM_PLAYBACK);
}
void ConfigurationManager::setAudioInputDevice(const int32_t& index)
{
return Manager::instance().setAudioDevice(index, SFL_PCM_CAPTURE);
return Manager::instance().setAudioDevice(index, AudioLayer::SFL_PCM_CAPTURE);
}
void ConfigurationManager::setAudioRingtoneDevice(const int32_t& index)
{
return Manager::instance().setAudioDevice(index, SFL_PCM_RINGTONE);
return Manager::instance().setAudioDevice(index, AudioLayer::SFL_PCM_RINGTONE);
}
std::vector<std::string> ConfigurationManager::getCurrentAudioDevicesIndex()
......
......@@ -52,11 +52,6 @@ const char * const ZRTP_ZID_FILENAME = "sfl.zid";
#define PCM_DSNOOP "plug:dsnoop" /** Alsa plugin for microphone sharing */
#define PCM_DMIX_DSNOOP "dmix/dsnoop" /** Audio profile using Alsa dmix/dsnoop */
#define SFL_PCM_BOTH 0x0021 /** To open both playback and capture devices */
#define SFL_PCM_PLAYBACK 0x0022 /** To open playback device only */
#define SFL_PCM_CAPTURE 0x0023 /** To open capture device only */
#define SFL_PCM_RINGTONE 0x0024
#define RINGTONE_ENABLED TRUE_STR /** Custom ringtone enable or not */
#define DISPLAY_DIALPAD TRUE_STR /** Display dialpad or not */
#define DISPLAY_VOLUME_CONTROLS TRUE_STR /** Display the volume controls or not */
......
......@@ -1883,32 +1883,17 @@ void ManagerImpl::setAudioPlugin(const std::string& audioPlugin)
/**
* Set audio output device
*/
void ManagerImpl::setAudioDevice(const int index, int streamType)
void ManagerImpl::setAudioDevice(int index, AudioLayer::PCMType type)
{
ost::MutexLock lock(audioLayerMutex_);
AlsaLayer *alsaLayer = dynamic_cast<AlsaLayer*>(audiodriver_);
if (!alsaLayer) {
ERROR("Can't find alsa device");
if (!audiodriver_) {
ERROR("Audio driver not initialized");
return ;
}
bool wasStarted = audiodriver_->isStarted();
switch (streamType) {
case SFL_PCM_PLAYBACK:
audioPreference.setCardout(index);
break;
case SFL_PCM_CAPTURE:
audioPreference.setCardin(index);
break;
case SFL_PCM_RINGTONE:
audioPreference.setCardring(index);
break;
default:
break;
}
const bool wasStarted = audiodriver_->isStarted();
audiodriver_->updatePreference(audioPreference, index, type);
// Recreate audio driver with new settings
delete audiodriver_;
......@@ -2165,12 +2150,7 @@ int ManagerImpl::getAudioDeviceIndex(const std::string &name)
return soundCardIndex;
}
AlsaLayer *alsalayer = dynamic_cast<AlsaLayer *>(audiodriver_);
if (alsalayer)
soundCardIndex = alsalayer -> getAudioDeviceIndex(name);
return soundCardIndex;
return audiodriver_->getAudioDeviceIndex(name);
}
std::string ManagerImpl::getCurrentAudioOutputPlugin() const
......
......@@ -48,6 +48,7 @@
#include "call.h"
#include "conference.h"
#include "audio/audiolayer.h"
#include "audio/sound/tone.h" // for Tone::TONEID declaration
#include "audio/codecs/audiocodecfactory.h"
......@@ -515,7 +516,7 @@ class ManagerImpl {
* @param index The index of the soundcard
* @param the type of stream, either SFL_PCM_PLAYBACK, SFL_PCM_CAPTURE, SFL_PCM_RINGTONE
*/
void setAudioDevice(const int index, const int streamType);
void setAudioDevice(int index, AudioLayer::PCMType streamType);
/**
* Get list of supported audio output device
......
......@@ -351,7 +351,7 @@ AudioPreference::AudioPreference() :
{}
namespace {
void checkSoundCard(int &card, int stream)
void checkSoundCard(int &card, AudioLayer::PCMType stream)
{
if (not AlsaLayer::soundCardIndexExists(card, stream)) {
WARN(" Card with index %d doesn't exist or is unusable.", card);
......@@ -363,13 +363,13 @@ void checkSoundCard(int &card, int stream)
AudioLayer* AudioPreference::createAudioLayer()
{
if (audioApi_ == PULSEAUDIO_API_STR and system("ps -C pulseaudio > /dev/null") == 0)
return new PulseLayer;
return new PulseLayer(*this);
else {
audioApi_ = ALSA_API_STR;
checkSoundCard(cardin_, SFL_PCM_CAPTURE);
checkSoundCard(cardout_, SFL_PCM_PLAYBACK);
checkSoundCard(cardring_, SFL_PCM_RINGTONE);
return new AlsaLayer;
checkSoundCard(cardin_, AudioLayer::SFL_PCM_CAPTURE);
checkSoundCard(cardout_, AudioLayer::SFL_PCM_PLAYBACK);
checkSoundCard(cardring_, AudioLayer::SFL_PCM_RINGTONE);
return new AlsaLayer(*this);
}
}
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment