[DEV] try to add a list of device for pulse
This commit is contained in:
parent
57c9cc1132
commit
4b5bbd9626
@ -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) {
|
||||
|
@ -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);
|
||||
|
153
audio/orchestra/api/PulseDeviceList.cpp
Normal file
153
audio/orchestra/api/PulseDeviceList.cpp
Normal 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;
|
||||
}
|
50
audio/orchestra/api/PulseDeviceList.h
Normal file
50
audio/orchestra/api/PulseDeviceList.h
Normal 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
|
@ -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)
|
||||
|
@ -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"])
|
||||
|
Loading…
x
Reference in New Issue
Block a user