[DEV] try to add a list of device for pulse

This commit is contained in:
Edouard DUPIN 2015-06-13 11:50:30 +02:00
parent 57c9cc1132
commit 4b5bbd9626
6 changed files with 250 additions and 40 deletions

View File

@ -19,15 +19,15 @@ void audio::orchestra::DeviceInfo::display(int32_t _tabNumber) const {
for (int32_t iii=0; iii<_tabNumber; ++iii) {
space += " ";
}
ATA_INFO(space + "probe=" << probed);
ATA_INFO(space + "name=" << name);
ATA_INFO(space + "outputChannels=" << outputChannels);
ATA_INFO(space + "inputChannels=" << inputChannels);
ATA_INFO(space + "duplexChannels=" << duplexChannels);
ATA_INFO(space + "isDefaultOutput=" << (isDefaultOutput==true?"true":"false"));
ATA_INFO(space + "isDefaultInput=" << (isDefaultInput==true?"true":"false"));
ATA_INFO(space + "rates=" << sampleRates);
ATA_INFO(space + "native Format: " << nativeFormats);
ATA_PRINT(space + "probe=" << probed);
ATA_PRINT(space + "name=" << name);
ATA_PRINT(space + "outputChannels=" << outputChannels);
ATA_PRINT(space + "inputChannels=" << inputChannels);
ATA_PRINT(space + "duplexChannels=" << duplexChannels);
ATA_PRINT(space + "isDefaultOutput=" << (isDefaultOutput==true?"true":"false"));
ATA_PRINT(space + "isDefaultInput=" << (isDefaultInput==true?"true":"false"));
ATA_PRINT(space + "rates=" << sampleRates);
ATA_PRINT(space + "native Format: " << nativeFormats);
}
std::ostream& audio::orchestra::operator <<(std::ostream& _os, const audio::orchestra::DeviceInfo& _obj) {

View File

@ -16,6 +16,7 @@
#include <pulse/simple.h>
#include <cstdio>
#include <etk/thread/tools.h>
#include <audio/orchestra/api/PulseDeviceList.h>
#undef __class__
#define __class__ "api::Pulse"
@ -53,15 +54,13 @@ namespace audio {
namespace api {
class PulsePrivate {
public:
pa_simple *s_play;
pa_simple *s_rec;
pa_simple* handle;
std11::shared_ptr<std11::thread> thread;
bool threadRunning;
std11::condition_variable runnable_cv;
bool runnable;
PulsePrivate() :
s_play(0),
s_rec(0),
handle(0),
threadRunning(false),
runnable(false) {
@ -82,16 +81,23 @@ audio::orchestra::api::Pulse::~Pulse() {
}
uint32_t audio::orchestra::api::Pulse::getDeviceCount() {
return 1;
#if 0
std::vector<audio::orchestra::api::pulse::Element> list = audio::orchestra::api::pulse::getDeviceList();
return list.size();
#else
return 1;
#endif
}
audio::orchestra::DeviceInfo audio::orchestra::api::Pulse::getDeviceInfo(uint32_t _device) {
audio::orchestra::DeviceInfo info;
//std::vector<audio::orchestra::api::pulse::Element> list = audio::orchestra::api::pulse::getDeviceList();
// TODO : Do it better... it is a little poor ...
info.probed = true;
info.name = "PulseAudio";
info.outputChannels = 2;
info.inputChannels = 2;
info.duplexChannels = 2;
info.duplexChannels = 0;
info.isDefaultOutput = true;
info.isDefaultInput = true;
for (const uint32_t *sr = SUPPORTED_SAMPLERATES; *sr; ++sr) {
@ -124,13 +130,11 @@ enum audio::orchestra::error audio::orchestra::api::Pulse::closeStream() {
}
m_mutex.unlock();
m_private->thread->join();
if (m_private->s_play) {
pa_simple_flush(m_private->s_play, nullptr);
pa_simple_free(m_private->s_play);
}
if (m_private->s_rec) {
pa_simple_free(m_private->s_rec);
if (m_mode == audio::orchestra::mode_output) {
pa_simple_flush(m_private->handle, nullptr);
}
pa_simple_free(m_private->handle);
m_private->handle = nullptr;
m_userBuffer[0].clear();
m_userBuffer[1].clear();
m_state = audio::orchestra::state_closed;
@ -173,8 +177,7 @@ void audio::orchestra::api::Pulse::callbackEventOneCycle() {
}
int32_t pa_error;
size_t bytes;
if ( m_mode == audio::orchestra::mode_output
|| m_mode == audio::orchestra::mode_duplex) {
if (m_mode == audio::orchestra::mode_output) {
if (m_doConvertBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)]) {
convertBuffer(m_deviceBuffer,
&m_userBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)][0],
@ -183,18 +186,18 @@ void audio::orchestra::api::Pulse::callbackEventOneCycle() {
} else {
bytes = m_nUserChannels[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)] * m_bufferSize * audio::getFormatBytes(m_userFormat);
}
if (pa_simple_write(m_private->s_play, pulse_out, bytes, &pa_error) < 0) {
if (pa_simple_write(m_private->handle, pulse_out, bytes, &pa_error) < 0) {
ATA_ERROR("audio write error, " << pa_strerror(pa_error) << ".");
return;
}
}
if (m_mode == audio::orchestra::mode_input || m_mode == audio::orchestra::mode_duplex) {
if (m_mode == audio::orchestra::mode_input) {
if (m_doConvertBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_input)]) {
bytes = m_nDeviceChannels[audio::orchestra::modeToIdTable(audio::orchestra::mode_input)] * m_bufferSize * audio::getFormatBytes(m_deviceFormat[audio::orchestra::modeToIdTable(audio::orchestra::mode_input)]);
} else {
bytes = m_nUserChannels[audio::orchestra::modeToIdTable(audio::orchestra::mode_input)] * m_bufferSize * audio::getFormatBytes(m_userFormat);
}
if (pa_simple_read(m_private->s_rec, pulse_in, bytes, &pa_error) < 0) {
if (pa_simple_read(m_private->handle, pulse_in, bytes, &pa_error) < 0) {
ATA_ERROR("audio read error, " << pa_strerror(pa_error) << ".");
return;
}
@ -244,9 +247,11 @@ enum audio::orchestra::error audio::orchestra::api::Pulse::stopStream() {
}
m_state = audio::orchestra::state_stopped;
m_mutex.lock();
if (m_private->s_play) {
if ( m_private != nullptr
&& m_private->handle != nullptr
&& m_mode == audio::orchestra::mode_output) {
int32_t pa_error;
if (pa_simple_drain(m_private->s_play, &pa_error) < 0) {
if (pa_simple_drain(m_private->handle, &pa_error) < 0) {
ATA_ERROR("error draining output device, " << pa_strerror(pa_error) << ".");
m_mutex.unlock();
return audio::orchestra::error_systemError;
@ -268,9 +273,11 @@ enum audio::orchestra::error audio::orchestra::api::Pulse::abortStream() {
}
m_state = audio::orchestra::state_stopped;
m_mutex.lock();
if (m_private && m_private->s_play) {
if ( m_private != nullptr
&& m_private->handle != nullptr
&& m_mode == audio::orchestra::mode_output) {
int32_t pa_error;
if (pa_simple_flush(m_private->s_play, &pa_error) < 0) {
if (pa_simple_flush(m_private->handle, &pa_error) < 0) {
ATA_ERROR("error flushing output device, " << pa_strerror(pa_error) << ".");
m_mutex.unlock();
return audio::orchestra::error_systemError;
@ -376,15 +383,15 @@ bool audio::orchestra::api::Pulse::probeDeviceOpen(uint32_t _device,
int32_t error;
switch (_mode) {
case audio::orchestra::mode_input:
m_private->s_rec = pa_simple_new(nullptr, "orchestra", PA_STREAM_RECORD, nullptr, "Record", &ss, nullptr, nullptr, &error);
if (!m_private->s_rec) {
m_private->handle = pa_simple_new(nullptr, "orchestra", PA_STREAM_RECORD, nullptr, "Record", &ss, nullptr, nullptr, &error);
if (m_private->handle == nullptr) {
ATA_ERROR("error connecting input to PulseAudio server.");
goto error;
}
break;
case audio::orchestra::mode_output:
m_private->s_play = pa_simple_new(nullptr, "orchestra", PA_STREAM_PLAYBACK, nullptr, "Playback", &ss, nullptr, nullptr, &error);
if (!m_private->s_play) {
m_private->handle = pa_simple_new(nullptr, "orchestra", PA_STREAM_PLAYBACK, nullptr, "Playback", &ss, nullptr, nullptr, &error);
if (m_private->handle == nullptr) {
ATA_ERROR("error connecting output to PulseAudio server.");
goto error;
}
@ -394,12 +401,10 @@ bool audio::orchestra::api::Pulse::probeDeviceOpen(uint32_t _device,
}
if (m_mode == audio::orchestra::mode_unknow) {
m_mode = _mode;
} else if (m_mode == _mode) {
} else {
goto error;
}else {
m_mode = audio::orchestra::mode_duplex;
}
if (!m_private->threadRunning) {
if (m_private->threadRunning == false) {
m_private->threadRunning = true;
m_private->thread = std11::make_shared<std11::thread>(&pulseaudio_callback, this);
if (m_private->thread == nullptr) {
@ -410,8 +415,8 @@ bool audio::orchestra::api::Pulse::probeDeviceOpen(uint32_t _device,
m_state = audio::orchestra::state_stopped;
return true;
error:
for (int32_t i=0; i<2; i++) {
m_userBuffer[i].clear();
for (int32_t iii=0; iii<2; ++iii) {
m_userBuffer[iii].clear();
}
if (m_deviceBuffer) {
free(m_deviceBuffer);

View File

@ -0,0 +1,153 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
* @fork from RTAudio
*/
#include <stdio.h>
#include <string.h>
#include <pulse/pulseaudio.h>
#include <audio/orchestra/api/PulseDeviceList.h>
#include <audio/orchestra/debug.h>
// This callback gets called when our context changes state. We really only
// care about when it's ready or if it has failed
static void callbackStateMachine(pa_context* _contex, void *_userdata) {
pa_context_state_t state;
int *pulseAudioReady = static_cast<int*>(_userdata);
state = pa_context_get_state(_contex);
switch (state) {
// There are just here for reference
case PA_CONTEXT_UNCONNECTED:
ATA_INFO("pulse state: PA_CONTEXT_UNCONNECTED");
break;
case PA_CONTEXT_CONNECTING:
ATA_INFO("pulse state: PA_CONTEXT_CONNECTING");
break;
case PA_CONTEXT_AUTHORIZING:
ATA_INFO("pulse state: PA_CONTEXT_AUTHORIZING");
break;
case PA_CONTEXT_SETTING_NAME:
ATA_INFO("pulse state: PA_CONTEXT_SETTING_NAME");
break;
default:
ATA_INFO("pulse state: default");
break;
case PA_CONTEXT_FAILED:
*pulseAudioReady = 2;
ATA_INFO("pulse state: PA_CONTEXT_FAILED");
break;
case PA_CONTEXT_TERMINATED:
*pulseAudioReady = 2;
ATA_INFO("pulse state: PA_CONTEXT_TERMINATED");
break;
case PA_CONTEXT_READY:
*pulseAudioReady = 1;
ATA_INFO("pulse state: PA_CONTEXT_READY");
break;
}
}
// Callback on getting data from pulseaudio:
static void callbackGetSinkList(pa_context* _contex, const pa_sink_info* _info, int _eol, void* _userdata) {
std::vector<audio::orchestra::api::pulse::Element>* list = static_cast<std::vector<audio::orchestra::api::pulse::Element>*>(_userdata);
// If eol is set to a positive number, you're at the end of the list
if (_eol > 0) {
return;
}
ATA_INFO("find output : " << _info->name);
list->push_back(audio::orchestra::api::pulse::Element(_info->index, false, _info->name, _info->description));
}
// allback to get data from pulseaudio:
static void callbackGetSourceList(pa_context* _contex, const pa_source_info* _info, int _eol, void* _userdata) {
std::vector<audio::orchestra::api::pulse::Element>* list = static_cast<std::vector<audio::orchestra::api::pulse::Element>*>(_userdata);
if (_eol > 0) {
return;
}
ATA_INFO("find input : " << _info->name);
list->push_back(audio::orchestra::api::pulse::Element(_info->index, true, _info->name, _info->description));
}
std::vector<audio::orchestra::api::pulse::Element> audio::orchestra::api::pulse::getDeviceList() {
// Define our pulse audio loop and connection variables
pa_mainloop* pulseAudioMainLoop;
pa_mainloop_api* pulseAudioMainLoopAPI;
pa_operation* pulseAudioOperation;
pa_context* pulseAudioContex;
pa_context_flags_t pulseAudioFlags;
std::vector<audio::orchestra::api::pulse::Element> out;
// We'll need these state variables to keep track of our requests
int state = 0;
int pulseAudioReady = 0;
// Create a mainloop API and connection to the default server
pulseAudioMainLoop = pa_mainloop_new();
pulseAudioMainLoopAPI = pa_mainloop_get_api(pulseAudioMainLoop);
pulseAudioContex = pa_context_new(pulseAudioMainLoopAPI, "test");
// This function connects to the pulse server
pa_context_connect(pulseAudioContex, NULL, pulseAudioFlags, NULL);
// If there's an error, the callback will set pulseAudioReady
pa_context_set_state_callback(pulseAudioContex, callbackStateMachine, &pulseAudioReady);
ATA_INFO("start main loop...");
while (true) {
ATA_INFO("loop");
// We can't do anything until PA is ready, so just iterate the mainloop
// and continue
if (pulseAudioReady == 0) {
ATA_INFO("Pulse not ready");
pa_mainloop_iterate(pulseAudioMainLoop, 1, nullptr);
continue;
}
// We couldn't get a connection to the server, so exit out
if (pulseAudioReady == 2) {
ATA_INFO("pulse not ready");
pa_context_disconnect(pulseAudioContex);
pa_context_unref(pulseAudioContex);
pa_mainloop_free(pulseAudioMainLoop);
ATA_ERROR("Pulse interface error: Can not connect to the pulseaudio iterface...");
return out;
}
// At this point, we're connected to the server and ready to make
// requests
switch (state) {
// State 0: we haven't done anything yet
case 0:
ATA_INFO("Request sink list");
pulseAudioOperation = pa_context_get_sink_info_list(pulseAudioContex,
callbackGetSinkList,
&out);
state++;
break;
case 1:
// Now we wait for our operation to complete. When it's
// complete our pa_output_devicelist is filled out, and we move
// along to the next state
if (pa_operation_get_state(pulseAudioOperation) == PA_OPERATION_DONE) {
pa_operation_unref(pulseAudioOperation);
ATA_INFO("Request sources list");
pulseAudioOperation = pa_context_get_source_info_list(pulseAudioContex,
callbackGetSourceList,
&out);
state++;
}
break;
case 2:
if (pa_operation_get_state(pulseAudioOperation) == PA_OPERATION_DONE) {
ATA_INFO("All is done");
// Now we're done, clean up and disconnect and return
pa_operation_unref(pulseAudioOperation);
pa_context_disconnect(pulseAudioContex);
pa_context_unref(pulseAudioContex);
pa_mainloop_free(pulseAudioMainLoop);
return out;
}
break;
default:
// We should never see this state
ATA_ERROR("Error in getting the devices list ...");
return out;
}
// Iterate the main loop ..
pa_mainloop_iterate(pulseAudioMainLoop, 1, nullptr);
}
return out;
}

View File

@ -0,0 +1,50 @@
/** @file
* @author Edouard DUPIN
* @copyright 2011, Edouard DUPIN, all right reserved
* @license APACHE v2.0 (see license file)
* @fork from RTAudio
*/
#if !defined(__AUDIO_ORCHESTRA_API_PULSE_DEVICE_H__) && defined(ORCHESTRA_BUILD_PULSE)
#define __AUDIO_ORCHESTRA_API_PULSE_DEVICE_H__
#include <etk/types.h>
namespace audio {
namespace orchestra {
namespace api {
namespace pulse {
class Element {
private:
size_t m_index;
bool m_input;
std::string m_name;
std::string m_description;
public:
Element(size_t _index, bool _input, const std::string& _name, const std::string& _desc) :
m_index(_index),
m_input(_input),
m_name(_name),
m_description(_desc) {
// nothing to do...
}
size_t getIndex() const {
return m_index;
}
bool isInput() const {
return m_input;
}
const std::string& getName() const {
return m_name;
}
const std::string& getDescription() const {
return m_description;
}
};
std::vector<audio::orchestra::api::pulse::Element> getDeviceList();
}
}
}
}
#endif

View File

@ -17,6 +17,7 @@ namespace audio {
}
#define ATA_BASE(info,data) TK_LOG_BASE(audio::orchestra::getLogId(),info,data)
#define ATA_PRINT(data) ATA_BASE(-1, data)
#define ATA_CRITICAL(data) ATA_BASE(1, data)
#define ATA_ERROR(data) ATA_BASE(2, data)
#define ATA_WARNING(data) ATA_BASE(3, data)

View File

@ -44,6 +44,7 @@ def create(target):
'audio/orchestra/api/Alsa.cpp',
'audio/orchestra/api/Jack.cpp',
'audio/orchestra/api/Pulse.cpp',
'audio/orchestra/api/PulseDeviceList.cpp',
'audio/orchestra/api/Oss.cpp'
])
myModule.add_optionnal_module_depend('alsa', ["c++", "-DORCHESTRA_BUILD_ALSA"])