2015-02-09 21:44:32 +01:00
|
|
|
/** @file
|
|
|
|
* @author Edouard DUPIN
|
|
|
|
* @copyright 2011, Edouard DUPIN, all right reserved
|
|
|
|
* @license APACHE v2.0 (see license file)
|
|
|
|
* @fork from RTAudio
|
2014-03-11 21:46:00 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
#if defined(ORCHESTRA_BUILD_PULSE)
|
2014-03-11 21:46:00 +01:00
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <limits.h>
|
2015-04-10 22:06:17 +02:00
|
|
|
#include <audio/orchestra/Interface.h>
|
|
|
|
#include <audio/orchestra/debug.h>
|
2014-03-11 21:46:00 +01:00
|
|
|
#include <pulse/error.h>
|
|
|
|
#include <pulse/simple.h>
|
|
|
|
#include <cstdio>
|
2015-04-10 22:06:17 +02:00
|
|
|
#include <etk/thread/tools.h>
|
2015-06-13 11:50:30 +02:00
|
|
|
#include <audio/orchestra/api/PulseDeviceList.h>
|
2014-03-11 21:46:00 +01:00
|
|
|
|
2015-01-27 23:06:19 +01:00
|
|
|
#undef __class__
|
|
|
|
#define __class__ "api::Pulse"
|
|
|
|
|
2015-06-23 21:09:57 +02:00
|
|
|
std::shared_ptr<audio::orchestra::Api> audio::orchestra::api::Pulse::create() {
|
|
|
|
return std::shared_ptr<audio::orchestra::Api>(new audio::orchestra::api::Pulse());
|
2014-03-11 22:37:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-11 21:46:00 +01:00
|
|
|
static const uint32_t SUPPORTED_SAMPLERATES[] = {
|
|
|
|
8000,
|
|
|
|
16000,
|
|
|
|
22050,
|
|
|
|
32000,
|
|
|
|
44100,
|
|
|
|
48000,
|
|
|
|
96000,
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
struct rtaudio_pa_format_mapping_t {
|
2015-02-08 15:09:39 +01:00
|
|
|
enum audio::format airtaudio_format;
|
2014-03-11 21:46:00 +01:00
|
|
|
pa_sample_format_t pa_format;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {
|
2015-02-05 23:31:22 +01:00
|
|
|
{audio::format_int16, PA_SAMPLE_S16LE},
|
|
|
|
{audio::format_int32, PA_SAMPLE_S32LE},
|
|
|
|
{audio::format_float, PA_SAMPLE_FLOAT32LE},
|
2015-02-08 15:09:39 +01:00
|
|
|
{audio::format_unknow, PA_SAMPLE_INVALID}};
|
2014-03-11 21:46:00 +01:00
|
|
|
|
2015-02-10 21:01:53 +01:00
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
namespace audio {
|
|
|
|
namespace orchestra {
|
|
|
|
namespace api {
|
|
|
|
class PulsePrivate {
|
|
|
|
public:
|
2015-06-13 11:50:30 +02:00
|
|
|
pa_simple* handle;
|
2015-04-10 22:06:17 +02:00
|
|
|
std11::shared_ptr<std11::thread> thread;
|
|
|
|
bool threadRunning;
|
|
|
|
std11::condition_variable runnable_cv;
|
|
|
|
bool runnable;
|
|
|
|
PulsePrivate() :
|
2015-06-13 11:50:30 +02:00
|
|
|
handle(0),
|
2015-04-10 22:06:17 +02:00
|
|
|
threadRunning(false),
|
|
|
|
runnable(false) {
|
|
|
|
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
2014-03-11 22:37:22 +01:00
|
|
|
}
|
2015-02-10 21:01:53 +01:00
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
audio::orchestra::api::Pulse::Pulse() :
|
|
|
|
m_private(new audio::orchestra::api::PulsePrivate()) {
|
2015-02-10 21:01:53 +01:00
|
|
|
|
|
|
|
}
|
2014-03-11 21:46:00 +01:00
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
audio::orchestra::api::Pulse::~Pulse() {
|
|
|
|
if (m_state != audio::orchestra::state_closed) {
|
2014-03-11 21:46:00 +01:00
|
|
|
closeStream();
|
2014-03-12 23:55:49 +01:00
|
|
|
}
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
uint32_t audio::orchestra::api::Pulse::getDeviceCount() {
|
2015-06-14 18:32:08 +02:00
|
|
|
#if 1
|
|
|
|
std::vector<audio::orchestra::DeviceInfo> list = audio::orchestra::api::pulse::getDeviceList();
|
2015-06-13 11:50:30 +02:00
|
|
|
return list.size();
|
|
|
|
#else
|
|
|
|
return 1;
|
|
|
|
#endif
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
audio::orchestra::DeviceInfo audio::orchestra::api::Pulse::getDeviceInfo(uint32_t _device) {
|
2015-06-14 18:32:08 +02:00
|
|
|
std::vector<audio::orchestra::DeviceInfo> list = audio::orchestra::api::pulse::getDeviceList();
|
|
|
|
if (_device >= list.size()) {
|
|
|
|
ATA_ERROR("Request device out of IDs:" << _device << " >= " << list.size());
|
|
|
|
return audio::orchestra::DeviceInfo();
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-06-14 18:32:08 +02:00
|
|
|
return list[_device];
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
|
2015-02-09 21:44:32 +01:00
|
|
|
static void pulseaudio_callback(void* _userData) {
|
2015-04-10 22:06:17 +02:00
|
|
|
audio::orchestra::api::Pulse* myClass = reinterpret_cast<audio::orchestra::api::Pulse*>(_userData);
|
2015-02-09 21:44:32 +01:00
|
|
|
myClass->callbackEvent();
|
|
|
|
}
|
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
void audio::orchestra::api::Pulse::callbackEvent() {
|
2015-03-20 21:07:58 +01:00
|
|
|
etk::thread::setName("Pulse IO-" + m_name);
|
2015-02-17 21:08:15 +01:00
|
|
|
while (m_private->threadRunning == true) {
|
2015-02-09 21:44:32 +01:00
|
|
|
callbackEventOneCycle();
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
enum audio::orchestra::error audio::orchestra::api::Pulse::closeStream() {
|
2015-02-17 21:08:15 +01:00
|
|
|
m_private->threadRunning = false;
|
2015-02-10 21:01:53 +01:00
|
|
|
m_mutex.lock();
|
2015-04-10 22:06:17 +02:00
|
|
|
if (m_state == audio::orchestra::state_stopped) {
|
2015-02-10 21:01:53 +01:00
|
|
|
m_private->runnable = true;
|
|
|
|
m_private->runnable_cv.notify_one();;
|
|
|
|
}
|
|
|
|
m_mutex.unlock();
|
|
|
|
m_private->thread->join();
|
2015-06-13 11:50:30 +02:00
|
|
|
if (m_mode == audio::orchestra::mode_output) {
|
|
|
|
pa_simple_flush(m_private->handle, nullptr);
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-06-13 11:50:30 +02:00
|
|
|
pa_simple_free(m_private->handle);
|
|
|
|
m_private->handle = nullptr;
|
2015-02-09 21:44:32 +01:00
|
|
|
m_userBuffer[0].clear();
|
|
|
|
m_userBuffer[1].clear();
|
2015-04-10 22:06:17 +02:00
|
|
|
m_state = audio::orchestra::state_closed;
|
|
|
|
m_mode = audio::orchestra::mode_unknow;
|
|
|
|
return audio::orchestra::error_none;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
void audio::orchestra::api::Pulse::callbackEventOneCycle() {
|
|
|
|
if (m_state == audio::orchestra::state_stopped) {
|
2015-02-24 22:20:11 +01:00
|
|
|
std11::unique_lock<std11::mutex> lck(m_mutex);
|
2015-02-10 21:01:53 +01:00
|
|
|
while (!m_private->runnable) {
|
|
|
|
m_private->runnable_cv.wait(lck);
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
if (m_state != audio::orchestra::state_running) {
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mutex.unlock();
|
2014-03-11 21:46:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
if (m_state == audio::orchestra::state_closed) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("the stream is closed ... this shouldn't happen!");
|
2014-03-11 21:46:00 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-04-13 21:49:48 +02:00
|
|
|
audio::Time streamTime = getStreamTime();
|
2015-04-10 22:06:17 +02:00
|
|
|
std::vector<enum audio::orchestra::status> status;
|
|
|
|
int32_t doStopStream = m_callback(&m_userBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_input)][0],
|
2015-02-17 21:08:15 +01:00
|
|
|
streamTime,
|
2015-04-10 22:06:17 +02:00
|
|
|
&m_userBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)][0],
|
2015-02-17 21:08:15 +01:00
|
|
|
streamTime,
|
|
|
|
m_bufferSize,
|
|
|
|
status);
|
2014-03-11 21:46:00 +01:00
|
|
|
if (doStopStream == 2) {
|
|
|
|
abortStream();
|
|
|
|
return;
|
|
|
|
}
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mutex.lock();
|
2015-04-10 22:06:17 +02:00
|
|
|
void *pulse_in = m_doConvertBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_input)] ? m_deviceBuffer : &m_userBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_input)][0];
|
|
|
|
void *pulse_out = m_doConvertBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)] ? m_deviceBuffer : &m_userBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)][0];
|
|
|
|
if (m_state != audio::orchestra::state_running) {
|
2014-03-11 21:46:00 +01:00
|
|
|
goto unlock;
|
|
|
|
}
|
|
|
|
int32_t pa_error;
|
|
|
|
size_t bytes;
|
2015-06-13 11:50:30 +02:00
|
|
|
if (m_mode == audio::orchestra::mode_output) {
|
2015-04-10 22:06:17 +02:00
|
|
|
if (m_doConvertBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)]) {
|
2015-02-09 21:44:32 +01:00
|
|
|
convertBuffer(m_deviceBuffer,
|
2015-04-10 22:06:17 +02:00
|
|
|
&m_userBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)][0],
|
|
|
|
m_convertInfo[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)]);
|
|
|
|
bytes = m_nDeviceChannels[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)] * m_bufferSize * audio::getFormatBytes(m_deviceFormat[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)]);
|
2014-03-11 21:46:00 +01:00
|
|
|
} else {
|
2015-04-10 22:06:17 +02:00
|
|
|
bytes = m_nUserChannels[audio::orchestra::modeToIdTable(audio::orchestra::mode_output)] * m_bufferSize * audio::getFormatBytes(m_userFormat);
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-06-13 11:50:30 +02:00
|
|
|
if (pa_simple_write(m_private->handle, pulse_out, bytes, &pa_error) < 0) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("audio write error, " << pa_strerror(pa_error) << ".");
|
2014-03-12 23:55:49 +01:00
|
|
|
return;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
}
|
2015-06-13 11:50:30 +02:00
|
|
|
if (m_mode == audio::orchestra::mode_input) {
|
2015-04-10 22:06:17 +02:00
|
|
|
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)]);
|
2014-03-11 21:46:00 +01:00
|
|
|
} else {
|
2015-04-10 22:06:17 +02:00
|
|
|
bytes = m_nUserChannels[audio::orchestra::modeToIdTable(audio::orchestra::mode_input)] * m_bufferSize * audio::getFormatBytes(m_userFormat);
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-06-13 11:50:30 +02:00
|
|
|
if (pa_simple_read(m_private->handle, pulse_in, bytes, &pa_error) < 0) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("audio read error, " << pa_strerror(pa_error) << ".");
|
2014-03-12 23:55:49 +01:00
|
|
|
return;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
if (m_doConvertBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_input)]) {
|
|
|
|
convertBuffer(&m_userBuffer[audio::orchestra::modeToIdTable(audio::orchestra::mode_input)][0],
|
2015-02-09 21:44:32 +01:00
|
|
|
m_deviceBuffer,
|
2015-04-10 22:06:17 +02:00
|
|
|
m_convertInfo[audio::orchestra::modeToIdTable(audio::orchestra::mode_input)]);
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
unlock:
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mutex.unlock();
|
2015-04-10 22:06:17 +02:00
|
|
|
audio::orchestra::Api::tickStreamTime();
|
2014-03-11 21:46:00 +01:00
|
|
|
if (doStopStream == 1) {
|
|
|
|
stopStream();
|
2014-03-12 23:55:49 +01:00
|
|
|
return;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2014-03-12 23:55:49 +01:00
|
|
|
return;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
enum audio::orchestra::error audio::orchestra::api::Pulse::startStream() {
|
2015-02-10 22:38:30 +01:00
|
|
|
// TODO : Check return ...
|
2015-04-10 22:06:17 +02:00
|
|
|
audio::orchestra::Api::startStream();
|
|
|
|
if (m_state == audio::orchestra::state_closed) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("the stream is not open!");
|
2015-04-10 22:06:17 +02:00
|
|
|
return audio::orchestra::error_invalidUse;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
if (m_state == audio::orchestra::state_running) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("the stream is already running!");
|
2015-04-10 22:06:17 +02:00
|
|
|
return audio::orchestra::error_warning;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mutex.lock();
|
2015-04-10 22:06:17 +02:00
|
|
|
m_state = audio::orchestra::state_running;
|
2015-02-10 21:01:53 +01:00
|
|
|
m_private->runnable = true;
|
|
|
|
m_private->runnable_cv.notify_one();
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mutex.unlock();
|
2015-04-10 22:06:17 +02:00
|
|
|
return audio::orchestra::error_none;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
enum audio::orchestra::error audio::orchestra::api::Pulse::stopStream() {
|
|
|
|
if (m_state == audio::orchestra::state_closed) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("the stream is not open!");
|
2015-04-10 22:06:17 +02:00
|
|
|
return audio::orchestra::error_invalidUse;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
if (m_state == audio::orchestra::state_stopped) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("the stream is already stopped!");
|
2015-04-10 22:06:17 +02:00
|
|
|
return audio::orchestra::error_warning;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
m_state = audio::orchestra::state_stopped;
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mutex.lock();
|
2015-06-13 11:50:30 +02:00
|
|
|
if ( m_private != nullptr
|
|
|
|
&& m_private->handle != nullptr
|
|
|
|
&& m_mode == audio::orchestra::mode_output) {
|
2014-03-11 21:46:00 +01:00
|
|
|
int32_t pa_error;
|
2015-06-13 11:50:30 +02:00
|
|
|
if (pa_simple_drain(m_private->handle, &pa_error) < 0) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("error draining output device, " << pa_strerror(pa_error) << ".");
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mutex.unlock();
|
2015-04-10 22:06:17 +02:00
|
|
|
return audio::orchestra::error_systemError;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
m_state = audio::orchestra::state_stopped;
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mutex.unlock();
|
2015-04-10 22:06:17 +02:00
|
|
|
return audio::orchestra::error_none;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
enum audio::orchestra::error audio::orchestra::api::Pulse::abortStream() {
|
|
|
|
if (m_state == audio::orchestra::state_closed) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("the stream is not open!");
|
2015-04-10 22:06:17 +02:00
|
|
|
return audio::orchestra::error_invalidUse;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
if (m_state == audio::orchestra::state_stopped) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("the stream is already stopped!");
|
2015-04-10 22:06:17 +02:00
|
|
|
return audio::orchestra::error_warning;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
m_state = audio::orchestra::state_stopped;
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mutex.lock();
|
2015-06-13 11:50:30 +02:00
|
|
|
if ( m_private != nullptr
|
|
|
|
&& m_private->handle != nullptr
|
|
|
|
&& m_mode == audio::orchestra::mode_output) {
|
2014-03-11 21:46:00 +01:00
|
|
|
int32_t pa_error;
|
2015-06-13 11:50:30 +02:00
|
|
|
if (pa_simple_flush(m_private->handle, &pa_error) < 0) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("error flushing output device, " << pa_strerror(pa_error) << ".");
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mutex.unlock();
|
2015-04-10 22:06:17 +02:00
|
|
|
return audio::orchestra::error_systemError;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
m_state = audio::orchestra::state_stopped;
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mutex.unlock();
|
2015-04-10 22:06:17 +02:00
|
|
|
return audio::orchestra::error_none;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
|
2015-04-10 22:06:17 +02:00
|
|
|
bool audio::orchestra::api::Pulse::probeDeviceOpen(uint32_t _device,
|
|
|
|
audio::orchestra::mode _mode,
|
2014-03-12 23:55:49 +01:00
|
|
|
uint32_t _channels,
|
|
|
|
uint32_t _firstChannel,
|
|
|
|
uint32_t _sampleRate,
|
2015-02-05 23:31:22 +01:00
|
|
|
audio::format _format,
|
2014-03-12 23:55:49 +01:00
|
|
|
uint32_t *_bufferSize,
|
2015-04-10 22:06:17 +02:00
|
|
|
const audio::orchestra::StreamOptions& _options) {
|
2014-03-11 21:46:00 +01:00
|
|
|
uint64_t bufferBytes = 0;
|
|
|
|
pa_sample_spec ss;
|
2014-03-12 23:55:49 +01:00
|
|
|
if (_device != 0) {
|
2014-03-11 21:46:00 +01:00
|
|
|
return false;
|
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
if (_mode != audio::orchestra::mode_input && _mode != audio::orchestra::mode_output) {
|
2014-03-11 21:46:00 +01:00
|
|
|
return false;
|
|
|
|
}
|
2014-03-12 23:55:49 +01:00
|
|
|
if (_channels != 1 && _channels != 2) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("unsupported number of channels.");
|
2014-03-11 21:46:00 +01:00
|
|
|
return false;
|
|
|
|
}
|
2014-03-12 23:55:49 +01:00
|
|
|
ss.channels = _channels;
|
|
|
|
if (_firstChannel != 0) {
|
2014-03-11 21:46:00 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool sr_found = false;
|
|
|
|
for (const uint32_t *sr = SUPPORTED_SAMPLERATES; *sr; ++sr) {
|
2014-03-12 23:55:49 +01:00
|
|
|
if (_sampleRate == *sr) {
|
2014-03-11 21:46:00 +01:00
|
|
|
sr_found = true;
|
2015-02-09 21:44:32 +01:00
|
|
|
m_sampleRate = _sampleRate;
|
2014-03-12 23:55:49 +01:00
|
|
|
ss.rate = _sampleRate;
|
2014-03-11 21:46:00 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!sr_found) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("unsupported sample rate.");
|
2014-03-11 21:46:00 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
bool sf_found = 0;
|
|
|
|
for (const rtaudio_pa_format_mapping_t *sf = supported_sampleformats;
|
|
|
|
sf->airtaudio_format && sf->pa_format != PA_SAMPLE_INVALID;
|
|
|
|
++sf) {
|
2014-03-12 23:55:49 +01:00
|
|
|
if (_format == sf->airtaudio_format) {
|
2014-03-11 21:46:00 +01:00
|
|
|
sf_found = true;
|
2015-02-09 21:44:32 +01:00
|
|
|
m_userFormat = sf->airtaudio_format;
|
2014-03-11 21:46:00 +01:00
|
|
|
ss.format = sf->pa_format;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!sf_found) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("unsupported sample format.");
|
2014-03-11 21:46:00 +01:00
|
|
|
return false;
|
|
|
|
}
|
2015-02-09 21:44:32 +01:00
|
|
|
m_deviceInterleaved[modeToIdTable(_mode)] = true;
|
|
|
|
m_nBuffers = 1;
|
|
|
|
m_doByteSwap[modeToIdTable(_mode)] = false;
|
|
|
|
m_doConvertBuffer[modeToIdTable(_mode)] = false;
|
|
|
|
m_deviceFormat[modeToIdTable(_mode)] = m_userFormat;
|
|
|
|
m_nUserChannels[modeToIdTable(_mode)] = _channels;
|
|
|
|
m_nDeviceChannels[modeToIdTable(_mode)] = _channels + _firstChannel;
|
|
|
|
m_channelOffset[modeToIdTable(_mode)] = 0;
|
2014-03-11 21:46:00 +01:00
|
|
|
// Allocate necessary internal buffers.
|
2015-02-09 21:44:32 +01:00
|
|
|
bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat);
|
|
|
|
m_userBuffer[modeToIdTable(_mode)].resize(bufferBytes, 0);
|
|
|
|
if (m_userBuffer[modeToIdTable(_mode)].size() == 0) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("error allocating user buffer memory.");
|
2014-03-11 21:46:00 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2015-02-09 21:44:32 +01:00
|
|
|
m_bufferSize = *_bufferSize;
|
|
|
|
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
2014-03-11 21:46:00 +01:00
|
|
|
bool makeBuffer = true;
|
2015-02-09 21:44:32 +01:00
|
|
|
bufferBytes = m_nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]);
|
2015-04-10 22:06:17 +02:00
|
|
|
if (_mode == audio::orchestra::mode_input) {
|
|
|
|
if (m_mode == audio::orchestra::mode_output && m_deviceBuffer) {
|
2015-02-09 21:44:32 +01:00
|
|
|
uint64_t bytesOut = m_nDeviceChannels[0] * audio::getFormatBytes(m_deviceFormat[0]);
|
2014-03-11 21:46:00 +01:00
|
|
|
if (bufferBytes <= bytesOut) makeBuffer = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (makeBuffer) {
|
2014-03-12 23:55:49 +01:00
|
|
|
bufferBytes *= *_bufferSize;
|
2015-02-09 21:44:32 +01:00
|
|
|
if (m_deviceBuffer) free(m_deviceBuffer);
|
|
|
|
m_deviceBuffer = (char *) calloc(bufferBytes, 1);
|
|
|
|
if (m_deviceBuffer == nullptr) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("error allocating device buffer memory.");
|
2014-03-11 21:46:00 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-02-09 21:44:32 +01:00
|
|
|
m_device[modeToIdTable(_mode)] = _device;
|
2014-03-11 21:46:00 +01:00
|
|
|
// Setup the buffer conversion information structure.
|
2015-02-09 21:44:32 +01:00
|
|
|
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
2014-03-12 23:55:49 +01:00
|
|
|
setConvertInfo(_mode, _firstChannel);
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
int32_t error;
|
2014-03-12 23:55:49 +01:00
|
|
|
switch (_mode) {
|
2015-04-10 22:06:17 +02:00
|
|
|
case audio::orchestra::mode_input:
|
2015-06-13 11:50:30 +02:00
|
|
|
m_private->handle = pa_simple_new(nullptr, "orchestra", PA_STREAM_RECORD, nullptr, "Record", &ss, nullptr, nullptr, &error);
|
|
|
|
if (m_private->handle == nullptr) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("error connecting input to PulseAudio server.");
|
2014-03-13 21:16:30 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
break;
|
2015-04-10 22:06:17 +02:00
|
|
|
case audio::orchestra::mode_output:
|
2015-06-13 11:50:30 +02:00
|
|
|
m_private->handle = pa_simple_new(nullptr, "orchestra", PA_STREAM_PLAYBACK, nullptr, "Playback", &ss, nullptr, nullptr, &error);
|
|
|
|
if (m_private->handle == nullptr) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("error connecting output to PulseAudio server.");
|
2014-03-13 21:16:30 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2014-03-11 21:46:00 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
if (m_mode == audio::orchestra::mode_unknow) {
|
2015-02-09 21:44:32 +01:00
|
|
|
m_mode = _mode;
|
2015-06-13 11:50:30 +02:00
|
|
|
} else {
|
2014-03-11 21:46:00 +01:00
|
|
|
goto error;
|
|
|
|
}
|
2015-06-13 11:50:30 +02:00
|
|
|
if (m_private->threadRunning == false) {
|
2015-02-17 21:08:15 +01:00
|
|
|
m_private->threadRunning = true;
|
2015-04-17 22:02:09 +02:00
|
|
|
m_private->thread = std11::make_shared<std11::thread>(&pulseaudio_callback, this);
|
2015-02-10 21:01:53 +01:00
|
|
|
if (m_private->thread == nullptr) {
|
2015-02-05 23:31:22 +01:00
|
|
|
ATA_ERROR("error creating thread.");
|
2014-03-11 21:46:00 +01:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
}
|
2015-04-10 22:06:17 +02:00
|
|
|
m_state = audio::orchestra::state_stopped;
|
2014-03-11 21:46:00 +01:00
|
|
|
return true;
|
|
|
|
error:
|
2015-06-13 11:50:30 +02:00
|
|
|
for (int32_t iii=0; iii<2; ++iii) {
|
|
|
|
m_userBuffer[iii].clear();
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2015-02-09 21:44:32 +01:00
|
|
|
if (m_deviceBuffer) {
|
|
|
|
free(m_deviceBuffer);
|
|
|
|
m_deviceBuffer = 0;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
2014-03-13 21:16:30 +01:00
|
|
|
return false;
|
2014-03-11 21:46:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|