[DEV] set the fork work corectly
This commit is contained in:
parent
f4471f25e8
commit
7d91d12152
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
//#include <etk/types.h>
|
||||
@ -17,67 +16,6 @@
|
||||
#undef __class__
|
||||
#define __class__ "api"
|
||||
|
||||
static const char* listType[] {
|
||||
"undefined",
|
||||
"alsa",
|
||||
"pulse",
|
||||
"oss",
|
||||
"jack",
|
||||
"coreOSX",
|
||||
"corIOS",
|
||||
"asio",
|
||||
"ds",
|
||||
"java",
|
||||
"dummy",
|
||||
"user1",
|
||||
"user2",
|
||||
"user3",
|
||||
"user4"
|
||||
};
|
||||
static int32_t listTypeSize = sizeof(listType)/sizeof(char*);
|
||||
|
||||
|
||||
std::ostream& airtaudio::operator <<(std::ostream& _os, const enum airtaudio::type& _obj) {
|
||||
_os << listType[_obj];
|
||||
return _os;
|
||||
}
|
||||
|
||||
std::ostream& airtaudio::operator <<(std::ostream& _os, const std::vector<enum airtaudio::type>& _obj) {
|
||||
_os << std::string("{");
|
||||
for (size_t iii=0; iii<_obj.size(); ++iii) {
|
||||
if (iii!=0) {
|
||||
_os << std::string(";");
|
||||
}
|
||||
_os << _obj[iii];
|
||||
}
|
||||
_os << std::string("}");
|
||||
return _os;
|
||||
}
|
||||
|
||||
std::string airtaudio::getTypeString(enum audio::format _value) {
|
||||
return listType[_value];
|
||||
}
|
||||
|
||||
enum airtaudio::type airtaudio::getTypeFromString(const std::string& _value) {
|
||||
for (int32_t iii=0; iii<listTypeSize; ++iii) {
|
||||
if (_value == listType[iii]) {
|
||||
return static_cast<enum airtaudio::type>(iii);
|
||||
}
|
||||
}
|
||||
return airtaudio::type_undefined;
|
||||
}
|
||||
|
||||
int32_t airtaudio::modeToIdTable(enum mode _mode) {
|
||||
switch (_mode) {
|
||||
case mode_unknow:
|
||||
case mode_duplex:
|
||||
case mode_output:
|
||||
return 0;
|
||||
case mode_input:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Static variable definitions.
|
||||
const std::vector<uint32_t>& airtaudio::genericSampleRate() {
|
||||
@ -104,12 +42,14 @@ const std::vector<uint32_t>& airtaudio::genericSampleRate() {
|
||||
};
|
||||
|
||||
|
||||
airtaudio::Api::Api() {
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_stream.mode = airtaudio::mode_unknow;
|
||||
m_stream.apiHandle = 0;
|
||||
m_stream.userBuffer[0] = 0;
|
||||
m_stream.userBuffer[1] = 0;
|
||||
airtaudio::Api::Api() :
|
||||
m_apiHandle(nullptr),
|
||||
m_deviceBuffer(nullptr) {
|
||||
m_device[0] = 11111;
|
||||
m_device[1] = 11111;
|
||||
m_state = airtaudio::state_closed;
|
||||
m_mode = airtaudio::mode_unknow;
|
||||
m_apiHandle = 0;
|
||||
}
|
||||
|
||||
airtaudio::Api::~Api() {
|
||||
@ -123,7 +63,7 @@ enum airtaudio::error airtaudio::Api::openStream(airtaudio::StreamParameters *oP
|
||||
uint32_t *bufferFrames,
|
||||
airtaudio::AirTAudioCallback callback,
|
||||
airtaudio::StreamOptions *options) {
|
||||
if (m_stream.state != airtaudio::state_closed) {
|
||||
if (m_state != airtaudio::state_closed) {
|
||||
ATA_ERROR("a stream is already open!");
|
||||
return airtaudio::error_invalidUse;
|
||||
}
|
||||
@ -193,11 +133,11 @@ enum airtaudio::error airtaudio::Api::openStream(airtaudio::StreamParameters *oP
|
||||
return airtaudio::error_systemError;
|
||||
}
|
||||
}
|
||||
m_stream.callbackInfo.callback = callback;
|
||||
m_callbackInfo.callback = callback;
|
||||
if (options != nullptr) {
|
||||
options->numberOfBuffers = m_stream.nBuffers;
|
||||
options->numberOfBuffers = m_nBuffers;
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_state = airtaudio::state_stopped;
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
|
||||
@ -232,9 +172,9 @@ void airtaudio::Api::tickStreamTime() {
|
||||
// Subclasses that do not provide their own implementation of
|
||||
// getStreamTime should call this function once per buffer I/O to
|
||||
// provide basic stream time support.
|
||||
m_stream.streamTime += (m_stream.bufferSize * 1.0 / m_stream.sampleRate);
|
||||
m_streamTime += (m_bufferSize * 1.0 / m_sampleRate);
|
||||
#if defined(HAVE_GETTIMEOFDAY)
|
||||
gettimeofday(&m_stream.lastTickTimestamp, nullptr);
|
||||
gettimeofday(&m_lastTickTimestamp, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -243,13 +183,13 @@ long airtaudio::Api::getStreamLatency() {
|
||||
return 0;
|
||||
}
|
||||
long totalLatency = 0;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
totalLatency = m_stream.latency[0];
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
totalLatency = m_latency[0];
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
totalLatency += m_stream.latency[1];
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
totalLatency += m_latency[1];
|
||||
}
|
||||
return totalLatency;
|
||||
}
|
||||
@ -263,16 +203,16 @@ double airtaudio::Api::getStreamTime() {
|
||||
// adding in the elapsed time since the last tick.
|
||||
struct timeval then;
|
||||
struct timeval now;
|
||||
if (m_stream.state != airtaudio::state_running || m_stream.streamTime == 0.0) {
|
||||
return m_stream.streamTime;
|
||||
if (m_state != airtaudio::state_running || m_streamTime == 0.0) {
|
||||
return m_streamTime;
|
||||
}
|
||||
gettimeofday(&now, nullptr);
|
||||
then = m_stream.lastTickTimestamp;
|
||||
return m_stream.streamTime
|
||||
then = m_lastTickTimestamp;
|
||||
return m_streamTime
|
||||
+ ((now.tv_sec + 0.000001 * now.tv_usec)
|
||||
- (then.tv_sec + 0.000001 * then.tv_usec));
|
||||
#else
|
||||
return m_stream.streamTime;
|
||||
return m_streamTime;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -280,11 +220,11 @@ uint32_t airtaudio::Api::getStreamSampleRate() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return 0;
|
||||
}
|
||||
return m_stream.sampleRate;
|
||||
return m_sampleRate;
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::Api::verifyStream() {
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("a stream is not open!");
|
||||
return airtaudio::error_invalidUse;
|
||||
}
|
||||
@ -292,98 +232,98 @@ enum airtaudio::error airtaudio::Api::verifyStream() {
|
||||
}
|
||||
|
||||
void airtaudio::Api::clearStreamInfo() {
|
||||
m_stream.mode = airtaudio::mode_unknow;
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_stream.sampleRate = 0;
|
||||
m_stream.bufferSize = 0;
|
||||
m_stream.nBuffers = 0;
|
||||
m_stream.userFormat = audio::format_unknow;
|
||||
m_stream.streamTime = 0.0;
|
||||
m_stream.apiHandle = 0;
|
||||
m_stream.deviceBuffer = 0;
|
||||
m_stream.callbackInfo.callback = 0;
|
||||
m_stream.callbackInfo.isRunning = false;
|
||||
m_mode = airtaudio::mode_unknow;
|
||||
m_state = airtaudio::state_closed;
|
||||
m_sampleRate = 0;
|
||||
m_bufferSize = 0;
|
||||
m_nBuffers = 0;
|
||||
m_userFormat = audio::format_unknow;
|
||||
m_streamTime = 0.0;
|
||||
m_apiHandle = nullptr;
|
||||
m_deviceBuffer = nullptr;
|
||||
m_callbackInfo.callback = 0;
|
||||
m_callbackInfo.isRunning = false;
|
||||
for (int32_t iii=0; iii<2; ++iii) {
|
||||
m_stream.device[iii] = 11111;
|
||||
m_stream.doConvertBuffer[iii] = false;
|
||||
m_stream.deviceInterleaved[iii] = true;
|
||||
m_stream.doByteSwap[iii] = false;
|
||||
m_stream.nUserChannels[iii] = 0;
|
||||
m_stream.nDeviceChannels[iii] = 0;
|
||||
m_stream.channelOffset[iii] = 0;
|
||||
m_stream.deviceFormat[iii] = audio::format_unknow;
|
||||
m_stream.latency[iii] = 0;
|
||||
m_stream.userBuffer[iii] = 0;
|
||||
m_stream.convertInfo[iii].channels = 0;
|
||||
m_stream.convertInfo[iii].inJump = 0;
|
||||
m_stream.convertInfo[iii].outJump = 0;
|
||||
m_stream.convertInfo[iii].inFormat = audio::format_unknow;
|
||||
m_stream.convertInfo[iii].outFormat = audio::format_unknow;
|
||||
m_stream.convertInfo[iii].inOffset.clear();
|
||||
m_stream.convertInfo[iii].outOffset.clear();
|
||||
m_device[iii] = 11111;
|
||||
m_doConvertBuffer[iii] = false;
|
||||
m_deviceInterleaved[iii] = true;
|
||||
m_doByteSwap[iii] = false;
|
||||
m_nUserChannels[iii] = 0;
|
||||
m_nDeviceChannels[iii] = 0;
|
||||
m_channelOffset[iii] = 0;
|
||||
m_deviceFormat[iii] = audio::format_unknow;
|
||||
m_latency[iii] = 0;
|
||||
m_userBuffer[iii].clear();
|
||||
m_convertInfo[iii].channels = 0;
|
||||
m_convertInfo[iii].inJump = 0;
|
||||
m_convertInfo[iii].outJump = 0;
|
||||
m_convertInfo[iii].inFormat = audio::format_unknow;
|
||||
m_convertInfo[iii].outFormat = audio::format_unknow;
|
||||
m_convertInfo[iii].inOffset.clear();
|
||||
m_convertInfo[iii].outOffset.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void airtaudio::Api::setConvertInfo(airtaudio::mode _mode, uint32_t _firstChannel) {
|
||||
int32_t idTable = airtaudio::modeToIdTable(_mode);
|
||||
if (_mode == airtaudio::mode_input) { // convert device to user buffer
|
||||
m_stream.convertInfo[idTable].inJump = m_stream.nDeviceChannels[1];
|
||||
m_stream.convertInfo[idTable].outJump = m_stream.nUserChannels[1];
|
||||
m_stream.convertInfo[idTable].inFormat = m_stream.deviceFormat[1];
|
||||
m_stream.convertInfo[idTable].outFormat = m_stream.userFormat;
|
||||
m_convertInfo[idTable].inJump = m_nDeviceChannels[1];
|
||||
m_convertInfo[idTable].outJump = m_nUserChannels[1];
|
||||
m_convertInfo[idTable].inFormat = m_deviceFormat[1];
|
||||
m_convertInfo[idTable].outFormat = m_userFormat;
|
||||
} else { // convert user to device buffer
|
||||
m_stream.convertInfo[idTable].inJump = m_stream.nUserChannels[0];
|
||||
m_stream.convertInfo[idTable].outJump = m_stream.nDeviceChannels[0];
|
||||
m_stream.convertInfo[idTable].inFormat = m_stream.userFormat;
|
||||
m_stream.convertInfo[idTable].outFormat = m_stream.deviceFormat[0];
|
||||
m_convertInfo[idTable].inJump = m_nUserChannels[0];
|
||||
m_convertInfo[idTable].outJump = m_nDeviceChannels[0];
|
||||
m_convertInfo[idTable].inFormat = m_userFormat;
|
||||
m_convertInfo[idTable].outFormat = m_deviceFormat[0];
|
||||
}
|
||||
if (m_stream.convertInfo[idTable].inJump < m_stream.convertInfo[idTable].outJump) {
|
||||
m_stream.convertInfo[idTable].channels = m_stream.convertInfo[idTable].inJump;
|
||||
if (m_convertInfo[idTable].inJump < m_convertInfo[idTable].outJump) {
|
||||
m_convertInfo[idTable].channels = m_convertInfo[idTable].inJump;
|
||||
} else {
|
||||
m_stream.convertInfo[idTable].channels = m_stream.convertInfo[idTable].outJump;
|
||||
m_convertInfo[idTable].channels = m_convertInfo[idTable].outJump;
|
||||
}
|
||||
// Set up the interleave/deinterleave offsets.
|
||||
if (m_stream.deviceInterleaved[idTable] == false) {
|
||||
if (m_deviceInterleaved[idTable] == false) {
|
||||
if (_mode == airtaudio::mode_input) {
|
||||
for (int32_t kkk=0; kkk<m_stream.convertInfo[idTable].channels; ++kkk) {
|
||||
m_stream.convertInfo[idTable].inOffset.push_back(kkk * m_stream.bufferSize);
|
||||
m_stream.convertInfo[idTable].outOffset.push_back(kkk);
|
||||
m_stream.convertInfo[idTable].inJump = 1;
|
||||
for (int32_t kkk=0; kkk<m_convertInfo[idTable].channels; ++kkk) {
|
||||
m_convertInfo[idTable].inOffset.push_back(kkk * m_bufferSize);
|
||||
m_convertInfo[idTable].outOffset.push_back(kkk);
|
||||
m_convertInfo[idTable].inJump = 1;
|
||||
}
|
||||
} else {
|
||||
for (int32_t kkk=0; kkk<m_stream.convertInfo[idTable].channels; ++kkk) {
|
||||
m_stream.convertInfo[idTable].inOffset.push_back(kkk);
|
||||
m_stream.convertInfo[idTable].outOffset.push_back(kkk * m_stream.bufferSize);
|
||||
m_stream.convertInfo[idTable].outJump = 1;
|
||||
for (int32_t kkk=0; kkk<m_convertInfo[idTable].channels; ++kkk) {
|
||||
m_convertInfo[idTable].inOffset.push_back(kkk);
|
||||
m_convertInfo[idTable].outOffset.push_back(kkk * m_bufferSize);
|
||||
m_convertInfo[idTable].outJump = 1;
|
||||
}
|
||||
}
|
||||
} else { // no (de)interleaving
|
||||
for (int32_t kkk=0; kkk<m_stream.convertInfo[idTable].channels; ++kkk) {
|
||||
m_stream.convertInfo[idTable].inOffset.push_back(kkk);
|
||||
m_stream.convertInfo[idTable].outOffset.push_back(kkk);
|
||||
for (int32_t kkk=0; kkk<m_convertInfo[idTable].channels; ++kkk) {
|
||||
m_convertInfo[idTable].inOffset.push_back(kkk);
|
||||
m_convertInfo[idTable].outOffset.push_back(kkk);
|
||||
}
|
||||
}
|
||||
|
||||
// Add channel offset.
|
||||
if (_firstChannel > 0) {
|
||||
if (m_stream.deviceInterleaved[idTable]) {
|
||||
if (m_deviceInterleaved[idTable]) {
|
||||
if (_mode == airtaudio::mode_output) {
|
||||
for (int32_t kkk=0; kkk<m_stream.convertInfo[idTable].channels; ++kkk) {
|
||||
m_stream.convertInfo[idTable].outOffset[kkk] += _firstChannel;
|
||||
for (int32_t kkk=0; kkk<m_convertInfo[idTable].channels; ++kkk) {
|
||||
m_convertInfo[idTable].outOffset[kkk] += _firstChannel;
|
||||
}
|
||||
} else {
|
||||
for (int32_t kkk=0; kkk<m_stream.convertInfo[idTable].channels; ++kkk) {
|
||||
m_stream.convertInfo[idTable].inOffset[kkk] += _firstChannel;
|
||||
for (int32_t kkk=0; kkk<m_convertInfo[idTable].channels; ++kkk) {
|
||||
m_convertInfo[idTable].inOffset[kkk] += _firstChannel;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (_mode == airtaudio::mode_output) {
|
||||
for (int32_t kkk=0; kkk<m_stream.convertInfo[idTable].channels; ++kkk) {
|
||||
m_stream.convertInfo[idTable].outOffset[kkk] += (_firstChannel * m_stream.bufferSize);
|
||||
for (int32_t kkk=0; kkk<m_convertInfo[idTable].channels; ++kkk) {
|
||||
m_convertInfo[idTable].outOffset[kkk] += (_firstChannel * m_bufferSize);
|
||||
}
|
||||
} else {
|
||||
for (int32_t kkk=0; kkk<m_stream.convertInfo[idTable].channels; ++kkk) {
|
||||
m_stream.convertInfo[idTable].inOffset[kkk] += (_firstChannel * m_stream.bufferSize);
|
||||
for (int32_t kkk=0; kkk<m_convertInfo[idTable].channels; ++kkk) {
|
||||
m_convertInfo[idTable].inOffset[kkk] += (_firstChannel * m_bufferSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -396,14 +336,65 @@ void airtaudio::Api::convertBuffer(char *_outBuffer, char *_inBuffer, airtaudio:
|
||||
// the lower three bytes of a 32-bit integer.
|
||||
|
||||
// Clear our device buffer when in/out duplex device channels are different
|
||||
if ( _outBuffer == m_stream.deviceBuffer
|
||||
&& m_stream.mode == airtaudio::mode_duplex
|
||||
&& m_stream.nDeviceChannels[0] < m_stream.nDeviceChannels[1]) {
|
||||
memset(_outBuffer, 0, m_stream.bufferSize * _info.outJump * audio::getFormatBytes(_info.outFormat));
|
||||
if ( _outBuffer == m_deviceBuffer
|
||||
&& m_mode == airtaudio::mode_duplex
|
||||
&& m_nDeviceChannels[0] < m_nDeviceChannels[1]) {
|
||||
memset(_outBuffer, 0, m_bufferSize * _info.outJump * audio::getFormatBytes(_info.outFormat));
|
||||
}
|
||||
int32_t jjj;
|
||||
if (_info.outFormat != _info.outFormat) {
|
||||
ATA_CRITICAL("not manage anymore the format changing ...");
|
||||
int j;
|
||||
switch (audio::getFormatBytes(_info.outFormat)) {
|
||||
case 1:
|
||||
{
|
||||
uint8_t *out = reinterpret_cast<uint8_t*>(_outBuffer);
|
||||
uint8_t *in = reinterpret_cast<uint8_t*>(_inBuffer);
|
||||
for (size_t iii=0; iii<m_bufferSize; ++iii) {
|
||||
for (size_t jjj=0; jjj<_info.channels; jjj++) {
|
||||
out[_info.outOffset[jjj]] = in[_info.inOffset[jjj]];
|
||||
}
|
||||
in += _info.inJump;
|
||||
out += _info.outJump;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
{
|
||||
uint16_t *out = reinterpret_cast<uint16_t*>(_outBuffer);
|
||||
uint16_t *in = reinterpret_cast<uint16_t*>(_inBuffer);
|
||||
for (size_t iii=0; iii<m_bufferSize; ++iii) {
|
||||
for (size_t jjj=0; jjj<_info.channels; jjj++) {
|
||||
out[_info.outOffset[jjj]] = in[_info.inOffset[jjj]];
|
||||
}
|
||||
in += _info.inJump;
|
||||
out += _info.outJump;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
{
|
||||
uint32_t *out = reinterpret_cast<uint32_t*>(_outBuffer);
|
||||
uint32_t *in = reinterpret_cast<uint32_t*>(_inBuffer);
|
||||
for (size_t iii=0; iii<m_bufferSize; ++iii) {
|
||||
for (size_t jjj=0; jjj<_info.channels; jjj++) {
|
||||
out[_info.outOffset[jjj]] = in[_info.inOffset[jjj]];
|
||||
}
|
||||
in += _info.inJump;
|
||||
out += _info.outJump;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
{
|
||||
uint64_t *out = reinterpret_cast<uint64_t*>(_outBuffer);
|
||||
uint64_t *in = reinterpret_cast<uint64_t*>(_inBuffer);
|
||||
for (size_t iii=0; iii<m_bufferSize; ++iii) {
|
||||
for (size_t jjj=0; jjj<_info.channels; jjj++) {
|
||||
out[_info.outOffset[jjj]] = in[_info.inOffset[jjj]];
|
||||
}
|
||||
in += _info.inJump;
|
||||
out += _info.outJump;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
157
airtaudio/Api.h
157
airtaudio/Api.h
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_API_H__
|
||||
@ -11,47 +10,13 @@
|
||||
|
||||
#include <sstream>
|
||||
#include <airtaudio/debug.h>
|
||||
#include <airtaudio/type.h>
|
||||
#include <airtaudio/state.h>
|
||||
#include <airtaudio/mode.h>
|
||||
|
||||
namespace airtaudio {
|
||||
const std::vector<uint32_t>& genericSampleRate();
|
||||
/**
|
||||
* @brief Audio API specifier arguments.
|
||||
*/
|
||||
enum type {
|
||||
type_undefined, //!< Error API.
|
||||
type_alsa, //!< LINUX The Advanced Linux Sound Architecture.
|
||||
type_pulse, //!< LINUX The Linux PulseAudio.
|
||||
type_oss, //!< LINUX The Linux Open Sound System.
|
||||
type_jack, //!< UNIX The Jack Low-Latency Audio Server.
|
||||
type_coreOSX, //!< Macintosh OSX Core Audio.
|
||||
type_coreIOS, //!< Macintosh iOS Core Audio.
|
||||
type_asio, //!< WINDOWS The Steinberg Audio Stream I/O.
|
||||
type_ds, //!< WINDOWS The Microsoft Direct Sound.
|
||||
type_java, //!< ANDROID Interface.
|
||||
type_dummy, //!< Empty wrapper (non-functional).
|
||||
type_user1, //!< User interface 1.
|
||||
type_user2, //!< User interface 2.
|
||||
type_user3, //!< User interface 3.
|
||||
type_user4, //!< User interface 4.
|
||||
};
|
||||
std::ostream& operator <<(std::ostream& _os, const enum airtaudio::type& _obj);
|
||||
std::ostream& operator <<(std::ostream& _os, const std::vector<enum airtaudio::type>& _obj);
|
||||
std::string getTypeString(enum audio::format _value);
|
||||
enum airtaudio::type getTypeFromString(const std::string& _value);
|
||||
|
||||
enum state {
|
||||
state_closed,
|
||||
state_stopped,
|
||||
state_stopping,
|
||||
state_running
|
||||
};
|
||||
enum mode {
|
||||
mode_unknow,
|
||||
mode_output,
|
||||
mode_input,
|
||||
mode_duplex
|
||||
};
|
||||
int32_t modeToIdTable(enum mode _mode);
|
||||
// A protected structure used for buffer conversion.
|
||||
class ConvertInfo {
|
||||
public:
|
||||
@ -64,45 +29,6 @@ namespace airtaudio {
|
||||
std::vector<int> outOffset;
|
||||
};
|
||||
|
||||
namespace api {
|
||||
// A protected structure for audio streams.
|
||||
class Stream {
|
||||
public:
|
||||
uint32_t device[2]; // Playback and record, respectively.
|
||||
void *apiHandle; // void pointer for API specific stream handle information
|
||||
enum airtaudio::mode mode; // airtaudio::mode_output, airtaudio::mode_input, or airtaudio::mode_duplex.
|
||||
enum airtaudio::state state; // STOPPED, RUNNING, or CLOSED
|
||||
char *userBuffer[2]; // Playback and record, respectively.
|
||||
char *deviceBuffer;
|
||||
bool doConvertBuffer[2]; // Playback and record, respectively.
|
||||
bool deviceInterleaved[2]; // Playback and record, respectively.
|
||||
bool doByteSwap[2]; // Playback and record, respectively.
|
||||
uint32_t sampleRate;
|
||||
uint32_t bufferSize;
|
||||
uint32_t nBuffers;
|
||||
uint32_t nUserChannels[2]; // Playback and record, respectively.
|
||||
uint32_t nDeviceChannels[2]; // Playback and record channels, respectively.
|
||||
uint32_t channelOffset[2]; // Playback and record, respectively.
|
||||
uint64_t latency[2]; // Playback and record, respectively.
|
||||
enum audio::format userFormat;
|
||||
enum audio::format deviceFormat[2]; // Playback and record, respectively.
|
||||
std::mutex mutex;
|
||||
airtaudio::CallbackInfo callbackInfo;
|
||||
airtaudio::ConvertInfo convertInfo[2];
|
||||
double streamTime; // Number of elapsed seconds since the stream started.
|
||||
|
||||
#if defined(HAVE_GETTIMEOFDAY)
|
||||
struct timeval lastTickTimestamp;
|
||||
#endif
|
||||
|
||||
Stream() :
|
||||
apiHandle(nullptr),
|
||||
deviceBuffer(nullptr) {
|
||||
device[0] = 11111;
|
||||
device[1] = 11111;
|
||||
}
|
||||
};
|
||||
};
|
||||
class Api {
|
||||
public:
|
||||
Api();
|
||||
@ -127,21 +53,45 @@ namespace airtaudio {
|
||||
uint32_t getStreamSampleRate();
|
||||
virtual double getStreamTime();
|
||||
bool isStreamOpen() const {
|
||||
return m_stream.state != airtaudio::state_closed;
|
||||
return m_state != airtaudio::state_closed;
|
||||
}
|
||||
bool isStreamRunning() const {
|
||||
return m_stream.state == airtaudio::state_running;
|
||||
return m_state == airtaudio::state_running;
|
||||
}
|
||||
|
||||
protected:
|
||||
airtaudio::api::Stream m_stream;
|
||||
mutable std::mutex m_mutex;
|
||||
uint32_t m_device[2]; // Playback and record, respectively.
|
||||
// TODO : Remove this use derivative property of the c++ class ...
|
||||
void *m_apiHandle; // void pointer for API specific stream handle information
|
||||
enum airtaudio::mode m_mode; // airtaudio::mode_output, airtaudio::mode_input, or airtaudio::mode_duplex.
|
||||
enum airtaudio::state m_state; // STOPPED, RUNNING, or CLOSED
|
||||
std::vector<char> m_userBuffer[2]; // Playback and record, respectively.
|
||||
char *m_deviceBuffer;
|
||||
bool m_doConvertBuffer[2]; // Playback and record, respectively.
|
||||
bool m_deviceInterleaved[2]; // Playback and record, respectively.
|
||||
bool m_doByteSwap[2]; // Playback and record, respectively.
|
||||
uint32_t m_sampleRate;
|
||||
uint32_t m_bufferSize;
|
||||
uint32_t m_nBuffers;
|
||||
uint32_t m_nUserChannels[2]; // Playback and record, respectively.
|
||||
uint32_t m_nDeviceChannels[2]; // Playback and record channels, respectively.
|
||||
uint32_t m_channelOffset[2]; // Playback and record, respectively.
|
||||
uint64_t m_latency[2]; // Playback and record, respectively.
|
||||
enum audio::format m_userFormat; // TODO : Remove this ==> use can only open in the Harware format ...
|
||||
enum audio::format m_deviceFormat[2]; // Playback and record, respectively.
|
||||
// TODO : Remove this ...
|
||||
airtaudio::CallbackInfo m_callbackInfo;
|
||||
airtaudio::ConvertInfo m_convertInfo[2];
|
||||
// TODO : use : std::chrono::system_clock::time_point ...
|
||||
double m_streamTime; // Number of elapsed seconds since the stream started.
|
||||
|
||||
/*!
|
||||
Protected, api-specific method that attempts to open a device
|
||||
with the given parameters. This function MUST be implemented by
|
||||
all subclasses. If an error is encountered during the probe, a
|
||||
"warning" message is reported and false is returned. A
|
||||
successful probe is indicated by a return value of true.
|
||||
/**
|
||||
* @brief api-specific method that attempts to open a device
|
||||
* with the given parameters. This function MUST be implemented by
|
||||
* all subclasses. If an error is encountered during the probe, a
|
||||
* "warning" message is reported and false is returned. A
|
||||
* successful probe is indicated by a return value of true.
|
||||
*/
|
||||
virtual bool probeDeviceOpen(uint32_t _device,
|
||||
enum airtaudio::mode _mode,
|
||||
@ -151,16 +101,16 @@ namespace airtaudio {
|
||||
enum audio::format _format,
|
||||
uint32_t *_bufferSize,
|
||||
airtaudio::StreamOptions *_options);
|
||||
|
||||
//! A protected function used to increment the stream time.
|
||||
/**
|
||||
* @brief Increment the stream time.
|
||||
*/
|
||||
void tickStreamTime();
|
||||
|
||||
//! Protected common method to clear an RtApiStream structure.
|
||||
/**
|
||||
* @brief Clear an RtApiStream structure.
|
||||
*/
|
||||
void clearStreamInfo();
|
||||
|
||||
/*!
|
||||
Protected common method that throws an RtError (type =
|
||||
INVALID_USE) if a stream is not open.
|
||||
/**
|
||||
* @brief Check the current stream status
|
||||
*/
|
||||
enum airtaudio::error verifyStream();
|
||||
/**
|
||||
@ -171,12 +121,15 @@ namespace airtaudio {
|
||||
char *_inBuffer,
|
||||
airtaudio::ConvertInfo& _info);
|
||||
|
||||
//! Protected common method used to perform byte-swapping on buffers.
|
||||
/**
|
||||
* @brief Perform byte-swapping on buffers.
|
||||
*/
|
||||
void byteSwapBuffer(char *_buffer,
|
||||
uint32_t _samples,
|
||||
enum audio::format _format);
|
||||
|
||||
//! Protected common method that sets up the parameters for buffer conversion.
|
||||
/**
|
||||
* @brief Sets up the parameters for buffer conversion.
|
||||
*/
|
||||
void setConvertInfo(enum airtaudio::mode _mode,
|
||||
uint32_t _firstChannel);
|
||||
};
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_CALLBACK_INFO_H__
|
||||
@ -19,7 +18,6 @@ namespace airtaudio {
|
||||
// handling functions.
|
||||
class CallbackInfo {
|
||||
public:
|
||||
void* object; // Used as a "this" pointer.
|
||||
std::thread* thread;
|
||||
airtaudio::AirTAudioCallback callback;
|
||||
void* apiInfo; // void pointer for API specific callback information
|
||||
@ -29,7 +27,6 @@ namespace airtaudio {
|
||||
|
||||
// Default constructor.
|
||||
CallbackInfo() :
|
||||
object(nullptr),
|
||||
callback(nullptr),
|
||||
apiInfo(nullptr),
|
||||
isRunning(false),
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_DEVICE_INFO_H__
|
||||
|
9
airtaudio/Flags.cpp
Normal file
9
airtaudio/Flags.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#include <airtaudio/Flags.h>
|
||||
#include <airtaudio/debug.h>
|
24
airtaudio/Flags.h
Normal file
24
airtaudio/Flags.h
Normal file
@ -0,0 +1,24 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_FLAGS_H__
|
||||
#define __AIRTAUDIO_FLAGS_H__
|
||||
|
||||
#include <etk/types.h>
|
||||
|
||||
namespace airtaudio {
|
||||
class Flags {
|
||||
public:
|
||||
bool m_minimizeLatency; // Simple example ==> TODO ...
|
||||
Flags() :
|
||||
m_minimizeLatency(false) {
|
||||
// nothing to do ...
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
//#include <etk/types.h>
|
||||
|
@ -1,18 +1,16 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_RTAUDIO_H__
|
||||
#define __AIRTAUDIO_RTAUDIO_H__
|
||||
#ifndef __AIRTAUDIO_INTERFACE_H__
|
||||
#define __AIRTAUDIO_INTERFACE_H__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <airtaudio/base.h>
|
||||
#include <airtaudio/int24_t.h>
|
||||
#include <airtaudio/CallbackInfo.h>
|
||||
#include <airtaudio/Api.h>
|
||||
#include <airtaudio/api/Alsa.h>
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_STREAM_OPTION_H__
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_STREAM_PARAMETER_H__
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
|
||||
@ -42,14 +41,14 @@ struct AlsaHandle {
|
||||
}
|
||||
};
|
||||
|
||||
static void alsaCallbackHandler(void * _ptr);
|
||||
|
||||
|
||||
airtaudio::api::Alsa::Alsa() {
|
||||
// Nothing to do here.
|
||||
}
|
||||
|
||||
airtaudio::api::Alsa::~Alsa() {
|
||||
if (m_stream.state != airtaudio::state_closed) {
|
||||
if (m_state != airtaudio::state_closed) {
|
||||
closeStream();
|
||||
}
|
||||
}
|
||||
@ -154,9 +153,9 @@ airtaudio::DeviceInfo airtaudio::api::Alsa::getDeviceInfo(uint32_t _device) {
|
||||
foundDevice:
|
||||
// If a stream is already open, we cannot probe the stream devices.
|
||||
// Thus, use the saved results.
|
||||
if ( m_stream.state != airtaudio::state_closed
|
||||
&& ( m_stream.device[0] == _device
|
||||
|| m_stream.device[1] == _device)) {
|
||||
if ( m_state != airtaudio::state_closed
|
||||
&& ( m_device[0] == _device
|
||||
|| m_device[1] == _device)) {
|
||||
snd_ctl_close(chandle);
|
||||
if (_device >= m_devices.size()) {
|
||||
ATA_ERROR("device ID was not present before stream was opened.");
|
||||
@ -428,7 +427,7 @@ foundDevice:
|
||||
// stream and save the results for use by getDeviceInfo().
|
||||
if ( _mode == airtaudio::mode_output
|
||||
|| ( _mode == airtaudio::mode_input
|
||||
&& m_stream.mode != airtaudio::mode_output)) {
|
||||
&& m_mode != airtaudio::mode_output)) {
|
||||
// only do once
|
||||
this->saveDeviceInfo();
|
||||
}
|
||||
@ -462,9 +461,9 @@ foundDevice:
|
||||
result = snd_pcm_hw_params_set_access(phandle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
|
||||
if (result < 0) {
|
||||
result = snd_pcm_hw_params_set_access(phandle, hw_params, SND_PCM_ACCESS_RW_NONINTERLEAVED);
|
||||
m_stream.deviceInterleaved[modeToIdTable(_mode)] = false;
|
||||
m_deviceInterleaved[modeToIdTable(_mode)] = false;
|
||||
} else {
|
||||
m_stream.deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
m_deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if (result < 0) {
|
||||
snd_pcm_close(phandle);
|
||||
@ -472,7 +471,7 @@ foundDevice:
|
||||
return false;
|
||||
}
|
||||
// Determine how to set the device format.
|
||||
m_stream.userFormat = _format;
|
||||
m_userFormat = _format;
|
||||
snd_pcm_format_t deviceFormat = SND_PCM_FORMAT_UNKNOWN;
|
||||
if (_format == audio::format_int8) {
|
||||
deviceFormat = SND_PCM_FORMAT_S8;
|
||||
@ -488,7 +487,7 @@ foundDevice:
|
||||
deviceFormat = SND_PCM_FORMAT_FLOAT64;
|
||||
}
|
||||
if (snd_pcm_hw_params_test_format(phandle, hw_params, deviceFormat) == 0) {
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = _format;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = _format;
|
||||
} else {
|
||||
// If we get here, no supported format was found.
|
||||
snd_pcm_close(phandle);
|
||||
@ -503,11 +502,11 @@ foundDevice:
|
||||
return false;
|
||||
}
|
||||
// Determine whether byte-swaping is necessary.
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = false;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = false;
|
||||
if (deviceFormat != SND_PCM_FORMAT_S8) {
|
||||
result = snd_pcm_format_cpu_endian(deviceFormat);
|
||||
if (result == 0) {
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
} else if (result < 0) {
|
||||
snd_pcm_close(phandle);
|
||||
ATA_ERROR("error getting pcm device (" << name << ") endian-ness, " << snd_strerror(result) << ".");
|
||||
@ -523,7 +522,7 @@ foundDevice:
|
||||
}
|
||||
// Determine the number of channels for this device. We support a possible
|
||||
// minimum device channel number > than the value requested by the user.
|
||||
m_stream.nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
uint32_t value;
|
||||
result = snd_pcm_hw_params_get_channels_max(hw_params, &value);
|
||||
uint32_t deviceChannels = value;
|
||||
@ -543,7 +542,7 @@ foundDevice:
|
||||
if (deviceChannels < _channels + _firstChannel) {
|
||||
deviceChannels = _channels + _firstChannel;
|
||||
}
|
||||
m_stream.nDeviceChannels[modeToIdTable(_mode)] = deviceChannels;
|
||||
m_nDeviceChannels[modeToIdTable(_mode)] = deviceChannels;
|
||||
// Set the device channels.
|
||||
result = snd_pcm_hw_params_set_channels(phandle, hw_params, deviceChannels);
|
||||
if (result < 0) {
|
||||
@ -584,14 +583,14 @@ foundDevice:
|
||||
}
|
||||
// If attempting to setup a duplex stream, the bufferSize parameter
|
||||
// MUST be the same in both directions!
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
&& _mode == airtaudio::mode_input
|
||||
&& *_bufferSize != m_stream.bufferSize) {
|
||||
&& *_bufferSize != m_bufferSize) {
|
||||
snd_pcm_close(phandle);
|
||||
ATA_ERROR("system error setting buffer size for duplex stream on device (" << name << ").");
|
||||
return false;
|
||||
}
|
||||
m_stream.bufferSize = *_bufferSize;
|
||||
m_bufferSize = *_bufferSize;
|
||||
// Install the hardware configuration
|
||||
result = snd_pcm_hw_params(phandle, hw_params);
|
||||
if (result < 0) {
|
||||
@ -621,46 +620,46 @@ foundDevice:
|
||||
return false;
|
||||
}
|
||||
// Set flags for buffer conversion
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_stream.userFormat != m_stream.deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_userFormat != m_deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if (m_stream.nUserChannels[modeToIdTable(_mode)] < m_stream.nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if (m_nUserChannels[modeToIdTable(_mode)] < m_nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if ( m_stream.deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_stream.nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if ( m_deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
// Allocate the ApiHandle if necessary and then save.
|
||||
AlsaHandle *apiInfo = nullptr;
|
||||
if (m_stream.apiHandle == nullptr) {
|
||||
if (m_apiHandle == nullptr) {
|
||||
apiInfo = (AlsaHandle *) new AlsaHandle;
|
||||
if (apiInfo == nullptr) {
|
||||
ATA_ERROR("error allocating AlsaHandle memory.");
|
||||
goto error;
|
||||
}
|
||||
m_stream.apiHandle = (void *) apiInfo;
|
||||
m_apiHandle = (void *) apiInfo;
|
||||
} else {
|
||||
apiInfo = (AlsaHandle *) m_stream.apiHandle;
|
||||
apiInfo = (AlsaHandle *) m_apiHandle;
|
||||
}
|
||||
apiInfo->handles[modeToIdTable(_mode)] = phandle;
|
||||
phandle = 0;
|
||||
// Allocate necessary internal buffers.
|
||||
uint64_t bufferBytes;
|
||||
bufferBytes = m_stream.nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_stream.userFormat);
|
||||
m_stream.userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat);
|
||||
m_userBuffer[modeToIdTable(_mode)].resize(bufferBytes, 0);
|
||||
if (m_userBuffer[modeToIdTable(_mode)].size() == 0) {
|
||||
ATA_ERROR("error allocating user buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
bool makeBuffer = true;
|
||||
bufferBytes = m_stream.nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_stream.deviceFormat[modeToIdTable(_mode)]);
|
||||
bufferBytes = m_nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]);
|
||||
if (_mode == airtaudio::mode_input) {
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
&& m_stream.deviceBuffer) {
|
||||
uint64_t bytesOut = m_stream.nDeviceChannels[0] * audio::getFormatBytes(m_stream.deviceFormat[0]);
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
&& m_deviceBuffer) {
|
||||
uint64_t bytesOut = m_nDeviceChannels[0] * audio::getFormatBytes(m_deviceFormat[0]);
|
||||
if (bufferBytes <= bytesOut) {
|
||||
makeBuffer = false;
|
||||
}
|
||||
@ -668,30 +667,30 @@ foundDevice:
|
||||
}
|
||||
if (makeBuffer) {
|
||||
bufferBytes *= *_bufferSize;
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = nullptr;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = nullptr;
|
||||
}
|
||||
m_stream.deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.deviceBuffer == nullptr) {
|
||||
m_deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_deviceBuffer == nullptr) {
|
||||
ATA_ERROR("error allocating device buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_stream.sampleRate = _sampleRate;
|
||||
m_stream.nBuffers = periods;
|
||||
m_stream.device[modeToIdTable(_mode)] = _device;
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_sampleRate = _sampleRate;
|
||||
m_nBuffers = periods;
|
||||
m_device[modeToIdTable(_mode)] = _device;
|
||||
m_state = airtaudio::state_stopped;
|
||||
// Setup the buffer conversion information structure.
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
setConvertInfo(_mode, _firstChannel);
|
||||
}
|
||||
// Setup thread if necessary.
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
&& _mode == airtaudio::mode_input) {
|
||||
// We had already set up an output stream.
|
||||
m_stream.mode = airtaudio::mode_duplex;
|
||||
m_mode = airtaudio::mode_duplex;
|
||||
// Link the streams if possible.
|
||||
apiInfo->synchronized = false;
|
||||
if (snd_pcm_link(apiInfo->handles[0], apiInfo->handles[1]) == 0) {
|
||||
@ -701,13 +700,12 @@ foundDevice:
|
||||
// TODO : airtaudio::error_warning;
|
||||
}
|
||||
} else {
|
||||
m_stream.mode = _mode;
|
||||
m_mode = _mode;
|
||||
// Setup callback thread.
|
||||
m_stream.callbackInfo.object = (void *) this;
|
||||
m_stream.callbackInfo.isRunning = true;
|
||||
m_stream.callbackInfo.thread = new std::thread(alsaCallbackHandler, &m_stream.callbackInfo);
|
||||
if (m_stream.callbackInfo.thread == nullptr) {
|
||||
m_stream.callbackInfo.isRunning = false;
|
||||
m_callbackInfo.isRunning = true;
|
||||
m_callbackInfo.thread = new std::thread(&airtaudio::api::Alsa::alsaCallbackEvent, this);
|
||||
if (m_callbackInfo.thread == nullptr) {
|
||||
m_callbackInfo.isRunning = false;
|
||||
ATA_ERROR("creating callback thread!");
|
||||
goto error;
|
||||
}
|
||||
@ -723,49 +721,46 @@ error:
|
||||
}
|
||||
delete apiInfo;
|
||||
apiInfo = nullptr;
|
||||
m_stream.apiHandle = 0;
|
||||
m_apiHandle = 0;
|
||||
}
|
||||
if (phandle) {
|
||||
snd_pcm_close(phandle);
|
||||
}
|
||||
for (int32_t iii=0; iii<2; ++iii) {
|
||||
if (m_stream.userBuffer[iii]) {
|
||||
free(m_stream.userBuffer[iii]);
|
||||
m_stream.userBuffer[iii] = 0;
|
||||
m_userBuffer[iii].clear();
|
||||
}
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = 0;
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = 0;
|
||||
}
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_state = airtaudio::state_closed;
|
||||
return false;
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::api::Alsa::closeStream() {
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("no open stream to close!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
AlsaHandle *apiInfo = (AlsaHandle *) m_stream.apiHandle;
|
||||
m_stream.callbackInfo.isRunning = false;
|
||||
m_stream.mutex.lock();
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle;
|
||||
m_callbackInfo.isRunning = false;
|
||||
m_mutex.lock();
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
apiInfo->runnable = true;
|
||||
apiInfo->runnable_cv.notify_one();
|
||||
}
|
||||
m_stream.mutex.unlock();
|
||||
if (m_stream.callbackInfo.thread != nullptr) {
|
||||
m_stream.callbackInfo.thread->join();
|
||||
m_mutex.unlock();
|
||||
if (m_callbackInfo.thread != nullptr) {
|
||||
m_callbackInfo.thread->join();
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if (m_state == airtaudio::state_running) {
|
||||
m_state = airtaudio::state_stopped;
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
snd_pcm_drop(apiInfo->handles[0]);
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
snd_pcm_drop(apiInfo->handles[1]);
|
||||
}
|
||||
}
|
||||
@ -778,20 +773,17 @@ enum airtaudio::error airtaudio::api::Alsa::closeStream() {
|
||||
}
|
||||
delete apiInfo;
|
||||
apiInfo = nullptr;
|
||||
m_stream.apiHandle = 0;
|
||||
m_apiHandle = 0;
|
||||
}
|
||||
for (int32_t iii=0; iii<2; ++iii) {
|
||||
if (m_stream.userBuffer[iii] != nullptr) {
|
||||
free(m_stream.userBuffer[iii]);
|
||||
m_stream.userBuffer[iii] = 0;
|
||||
m_userBuffer[iii].clear();
|
||||
}
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = 0;
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = 0;
|
||||
}
|
||||
m_stream.mode = airtaudio::mode_unknow;
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_mode = airtaudio::mode_unknow;
|
||||
m_state = airtaudio::state_closed;
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
|
||||
@ -800,17 +792,17 @@ enum airtaudio::error airtaudio::api::Alsa::startStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
if (m_state == airtaudio::state_running) {
|
||||
ATA_ERROR("the stream is already running!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
std::unique_lock<std::mutex> lck(m_stream.mutex);
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
int32_t result = 0;
|
||||
snd_pcm_state_t state;
|
||||
AlsaHandle *apiInfo = (AlsaHandle *) m_stream.apiHandle;
|
||||
AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle;
|
||||
snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
if (handle[0] == nullptr) {
|
||||
ATA_ERROR("send nullptr to alsa ...");
|
||||
if (handle[1] != nullptr) {
|
||||
@ -826,8 +818,8 @@ enum airtaudio::error airtaudio::api::Alsa::startStream() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex)
|
||||
if ( ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex)
|
||||
&& !apiInfo->synchronized) {
|
||||
if (handle[1] == nullptr) {
|
||||
ATA_ERROR("send nullptr to alsa ...");
|
||||
@ -844,7 +836,7 @@ enum airtaudio::error airtaudio::api::Alsa::startStream() {
|
||||
}
|
||||
}
|
||||
}
|
||||
m_stream.state = airtaudio::state_running;
|
||||
m_state = airtaudio::state_running;
|
||||
unlock:
|
||||
apiInfo->runnable = true;
|
||||
apiInfo->runnable_cv.notify_one();
|
||||
@ -858,17 +850,17 @@ enum airtaudio::error airtaudio::api::Alsa::stopStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
std::unique_lock<std::mutex> lck(m_stream.mutex);
|
||||
m_state = airtaudio::state_stopped;
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
int32_t result = 0;
|
||||
AlsaHandle *apiInfo = (AlsaHandle *) m_stream.apiHandle;
|
||||
AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle;
|
||||
snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
if (apiInfo->synchronized) {
|
||||
result = snd_pcm_drop(handle[0]);
|
||||
} else {
|
||||
@ -879,8 +871,8 @@ enum airtaudio::error airtaudio::api::Alsa::stopStream() {
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
if ( ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex)
|
||||
if ( ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex)
|
||||
&& !apiInfo->synchronized) {
|
||||
result = snd_pcm_drop(handle[1]);
|
||||
if (result < 0) {
|
||||
@ -899,25 +891,25 @@ enum airtaudio::error airtaudio::api::Alsa::abortStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
std::unique_lock<std::mutex> lck(m_stream.mutex);
|
||||
m_state = airtaudio::state_stopped;
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
int32_t result = 0;
|
||||
AlsaHandle *apiInfo = (AlsaHandle *) m_stream.apiHandle;
|
||||
AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle;
|
||||
snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
result = snd_pcm_drop(handle[0]);
|
||||
if (result < 0) {
|
||||
ATA_ERROR("error aborting output pcm device, " << snd_strerror(result) << ".");
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
if ( ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex)
|
||||
if ( ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex)
|
||||
&& !apiInfo->synchronized) {
|
||||
result = snd_pcm_drop(handle[1]);
|
||||
if (result < 0) {
|
||||
@ -932,47 +924,59 @@ unlock:
|
||||
return airtaudio::error_systemError;
|
||||
}
|
||||
|
||||
|
||||
void airtaudio::api::Alsa::alsaCallbackEvent(void *_userData) {
|
||||
airtaudio::api::Alsa* myClass = reinterpret_cast<airtaudio::api::Alsa*>(_userData);
|
||||
myClass->callbackEvent();
|
||||
}
|
||||
|
||||
void airtaudio::api::Alsa::callbackEvent() {
|
||||
AlsaHandle *apiInfo = (AlsaHandle *) m_stream.apiHandle;
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
std::unique_lock<std::mutex> lck(m_stream.mutex);
|
||||
while (m_callbackInfo.isRunning == true) {
|
||||
callbackEventOneCycle();
|
||||
}
|
||||
}
|
||||
|
||||
void airtaudio::api::Alsa::callbackEventOneCycle() {
|
||||
AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle;
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
// TODO : Set this back ....
|
||||
/*
|
||||
while (!apiInfo->runnable) {
|
||||
apiInfo->runnable_cv.wait(lck);
|
||||
}
|
||||
*/
|
||||
if (m_stream.state != airtaudio::state_running) {
|
||||
if (m_state != airtaudio::state_running) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_CRITICAL("the stream is closed ... this shouldn't happen!");
|
||||
return; // TODO : notify appl: airtaudio::error_warning;
|
||||
}
|
||||
int32_t doStopStream = 0;
|
||||
double streamTime = getStreamTime();
|
||||
enum airtaudio::status status = airtaudio::status_ok;
|
||||
if (m_stream.mode != airtaudio::mode_input && apiInfo->xrun[0] == true) {
|
||||
if (m_mode != airtaudio::mode_input && apiInfo->xrun[0] == true) {
|
||||
status = airtaudio::status_underflow;
|
||||
apiInfo->xrun[0] = false;
|
||||
}
|
||||
if (m_stream.mode != airtaudio::mode_output && apiInfo->xrun[1] == true) {
|
||||
if (m_mode != airtaudio::mode_output && apiInfo->xrun[1] == true) {
|
||||
status = airtaudio::status_overflow;
|
||||
apiInfo->xrun[1] = false;
|
||||
}
|
||||
doStopStream = m_stream.callbackInfo.callback(m_stream.userBuffer[0],
|
||||
m_stream.userBuffer[1],
|
||||
m_stream.bufferSize,
|
||||
doStopStream = m_callbackInfo.callback(&m_userBuffer[0][0],
|
||||
&m_userBuffer[1][0],
|
||||
m_bufferSize,
|
||||
streamTime,
|
||||
status);
|
||||
if (doStopStream == 2) {
|
||||
abortStream();
|
||||
return;
|
||||
}
|
||||
std::unique_lock<std::mutex> lck(m_stream.mutex);
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
// The state might change while waiting on a mutex.
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
goto unlock;
|
||||
}
|
||||
int32_t result;
|
||||
@ -982,29 +986,29 @@ void airtaudio::api::Alsa::callbackEvent() {
|
||||
snd_pcm_sframes_t frames;
|
||||
audio::format format;
|
||||
handle = (snd_pcm_t **) apiInfo->handles;
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
// Setup parameters.
|
||||
if (m_stream.doConvertBuffer[1]) {
|
||||
buffer = m_stream.deviceBuffer;
|
||||
channels = m_stream.nDeviceChannels[1];
|
||||
format = m_stream.deviceFormat[1];
|
||||
if (m_doConvertBuffer[1]) {
|
||||
buffer = m_deviceBuffer;
|
||||
channels = m_nDeviceChannels[1];
|
||||
format = m_deviceFormat[1];
|
||||
} else {
|
||||
buffer = m_stream.userBuffer[1];
|
||||
channels = m_stream.nUserChannels[1];
|
||||
format = m_stream.userFormat;
|
||||
buffer = &m_userBuffer[1][0];
|
||||
channels = m_nUserChannels[1];
|
||||
format = m_userFormat;
|
||||
}
|
||||
// Read samples from device in interleaved/non-interleaved format.
|
||||
if (m_stream.deviceInterleaved[1]) {
|
||||
result = snd_pcm_readi(handle[1], buffer, m_stream.bufferSize);
|
||||
if (m_deviceInterleaved[1]) {
|
||||
result = snd_pcm_readi(handle[1], buffer, m_bufferSize);
|
||||
} else {
|
||||
void *bufs[channels];
|
||||
size_t offset = m_stream.bufferSize * audio::getFormatBytes(format);
|
||||
size_t offset = m_bufferSize * audio::getFormatBytes(format);
|
||||
for (int32_t i=0; i<channels; i++)
|
||||
bufs[i] = (void *) (buffer + (i * offset));
|
||||
result = snd_pcm_readn(handle[1], bufs, m_stream.bufferSize);
|
||||
result = snd_pcm_readn(handle[1], bufs, m_bufferSize);
|
||||
}
|
||||
if (result < (int) m_stream.bufferSize) {
|
||||
if (result < (int) m_bufferSize) {
|
||||
// Either an error or overrun occured.
|
||||
if (result == -EPIPE) {
|
||||
snd_pcm_state_t state = snd_pcm_state(handle[1]);
|
||||
@ -1024,50 +1028,50 @@ void airtaudio::api::Alsa::callbackEvent() {
|
||||
goto tryOutput;
|
||||
}
|
||||
// Do byte swapping if necessary.
|
||||
if (m_stream.doByteSwap[1]) {
|
||||
byteSwapBuffer(buffer, m_stream.bufferSize * channels, format);
|
||||
if (m_doByteSwap[1]) {
|
||||
byteSwapBuffer(buffer, m_bufferSize * channels, format);
|
||||
}
|
||||
// Do buffer conversion if necessary.
|
||||
if (m_stream.doConvertBuffer[1]) {
|
||||
convertBuffer(m_stream.userBuffer[1], m_stream.deviceBuffer, m_stream.convertInfo[1]);
|
||||
if (m_doConvertBuffer[1]) {
|
||||
convertBuffer(&m_userBuffer[1][0], m_deviceBuffer, m_convertInfo[1]);
|
||||
}
|
||||
// Check stream latency
|
||||
result = snd_pcm_delay(handle[1], &frames);
|
||||
if (result == 0 && frames > 0) {
|
||||
m_stream.latency[1] = frames;
|
||||
m_latency[1] = frames;
|
||||
}
|
||||
}
|
||||
|
||||
tryOutput:
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
// Setup parameters and do buffer conversion if necessary.
|
||||
if (m_stream.doConvertBuffer[0]) {
|
||||
buffer = m_stream.deviceBuffer;
|
||||
convertBuffer(buffer, m_stream.userBuffer[0], m_stream.convertInfo[0]);
|
||||
channels = m_stream.nDeviceChannels[0];
|
||||
format = m_stream.deviceFormat[0];
|
||||
if (m_doConvertBuffer[0]) {
|
||||
buffer = m_deviceBuffer;
|
||||
convertBuffer(buffer, &m_userBuffer[0][0], m_convertInfo[0]);
|
||||
channels = m_nDeviceChannels[0];
|
||||
format = m_deviceFormat[0];
|
||||
} else {
|
||||
buffer = m_stream.userBuffer[0];
|
||||
channels = m_stream.nUserChannels[0];
|
||||
format = m_stream.userFormat;
|
||||
buffer = &m_userBuffer[0][0];
|
||||
channels = m_nUserChannels[0];
|
||||
format = m_userFormat;
|
||||
}
|
||||
// Do byte swapping if necessary.
|
||||
if (m_stream.doByteSwap[0]) {
|
||||
byteSwapBuffer(buffer, m_stream.bufferSize * channels, format);
|
||||
if (m_doByteSwap[0]) {
|
||||
byteSwapBuffer(buffer, m_bufferSize * channels, format);
|
||||
}
|
||||
// Write samples to device in interleaved/non-interleaved format.
|
||||
if (m_stream.deviceInterleaved[0]) {
|
||||
result = snd_pcm_writei(handle[0], buffer, m_stream.bufferSize);
|
||||
if (m_deviceInterleaved[0]) {
|
||||
result = snd_pcm_writei(handle[0], buffer, m_bufferSize);
|
||||
} else {
|
||||
void *bufs[channels];
|
||||
size_t offset = m_stream.bufferSize * audio::getFormatBytes(format);
|
||||
size_t offset = m_bufferSize * audio::getFormatBytes(format);
|
||||
for (int32_t i=0; i<channels; i++) {
|
||||
bufs[i] = (void *) (buffer + (i * offset));
|
||||
}
|
||||
result = snd_pcm_writen(handle[0], bufs, m_stream.bufferSize);
|
||||
result = snd_pcm_writen(handle[0], bufs, m_bufferSize);
|
||||
}
|
||||
if (result < (int) m_stream.bufferSize) {
|
||||
if (result < (int) m_bufferSize) {
|
||||
// Either an error or underrun occured.
|
||||
if (result == -EPIPE) {
|
||||
snd_pcm_state_t state = snd_pcm_state(handle[0]);
|
||||
@ -1089,7 +1093,7 @@ tryOutput:
|
||||
// Check stream latency
|
||||
result = snd_pcm_delay(handle[0], &frames);
|
||||
if (result == 0 && frames > 0) {
|
||||
m_stream.latency[0] = frames;
|
||||
m_latency[0] = frames;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1100,14 +1104,5 @@ unlock:
|
||||
}
|
||||
}
|
||||
|
||||
static void alsaCallbackHandler(void *_ptr) {
|
||||
airtaudio::CallbackInfo *info = (airtaudio::CallbackInfo*)_ptr;
|
||||
airtaudio::api::Alsa *object = (airtaudio::api::Alsa *) info->object;
|
||||
bool *isRunning = &info->isRunning;
|
||||
while (*isRunning == true) {
|
||||
// TODO : pthread_testcancel();
|
||||
object->callbackEvent();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#if !defined(__AIRTAUDIO_API_ALSA_H__) && defined(__LINUX_ALSA__)
|
||||
@ -31,6 +30,9 @@ namespace airtaudio {
|
||||
// which is not a member of RtAudio. External use of this function
|
||||
// will most likely produce highly undesireable results!
|
||||
void callbackEvent();
|
||||
void callbackEventOneCycle();
|
||||
private:
|
||||
static void alsaCallbackEvent(void* _userData);
|
||||
private:
|
||||
std::vector<airtaudio::DeviceInfo> m_devices;
|
||||
void saveDeviceInfo();
|
||||
|
@ -1,7 +1,8 @@
|
||||
/**
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifdef __ANDROID_JAVA__
|
||||
@ -109,15 +110,15 @@ void airtaudio::api::Android::callBackEvent(void* _data,
|
||||
int32_t doStopStream = 0;
|
||||
double streamTime = getStreamTime();
|
||||
enum airtaudio::status status = airtaudio::status_ok;
|
||||
if (m_stream.doConvertBuffer[airtaudio::mode_output] == true) {
|
||||
doStopStream = m_stream.callbackInfo.callback(m_stream.userBuffer[airtaudio::mode_output],
|
||||
if (m_doConvertBuffer[airtaudio::mode_output] == true) {
|
||||
doStopStream = m_callbackInfo.callback(m_userBuffer[airtaudio::mode_output],
|
||||
nullptr,
|
||||
_frameRate,
|
||||
streamTime,
|
||||
status);
|
||||
convertBuffer((char*)_data, (char*)m_stream.userBuffer[airtaudio::mode_output], m_stream.convertInfo[airtaudio::mode_output]);
|
||||
convertBuffer((char*)_data, (char*)m_userBuffer[airtaudio::mode_output], m_convertInfo[airtaudio::mode_output]);
|
||||
} else {
|
||||
doStopStream = m_stream.callbackInfo.callback(_data,
|
||||
doStopStream = m_callbackInfo.callback(_data,
|
||||
nullptr,
|
||||
_frameRate,
|
||||
streamTime,
|
||||
@ -154,8 +155,8 @@ bool airtaudio::api::Android::probeDeviceOpen(uint32_t _device,
|
||||
ATA_ERROR("Can not start a device input or duplex for Android ...");
|
||||
return false;
|
||||
}
|
||||
m_stream.userFormat = _format;
|
||||
m_stream.nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_userFormat = _format;
|
||||
m_nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
ewol::Context& tmpContext = ewol::getContext();
|
||||
bool ret = false;
|
||||
if (_format == SINT8) {
|
||||
@ -163,38 +164,38 @@ bool airtaudio::api::Android::probeDeviceOpen(uint32_t _device,
|
||||
} else {
|
||||
ret = tmpContext.audioOpenDevice(_device, _sampleRate, _channels, 1, androidCallBackEvent, this);
|
||||
}
|
||||
m_stream.bufferSize = 256;
|
||||
m_stream.sampleRate = _sampleRate;
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = false; // for endienness ...
|
||||
m_bufferSize = 256;
|
||||
m_sampleRate = _sampleRate;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = false; // for endienness ...
|
||||
|
||||
// TODO : For now, we write it in hard ==> to bu update later ...
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = SINT16;
|
||||
m_stream.nDeviceChannels[modeToIdTable(_mode)] = 2;
|
||||
m_stream.deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = SINT16;
|
||||
m_nDeviceChannels[modeToIdTable(_mode)] = 2;
|
||||
m_deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_stream.userFormat != m_stream.deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_userFormat != m_deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if (m_stream.nUserChannels[modeToIdTable(_mode)] < m_stream.nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if (m_nUserChannels[modeToIdTable(_mode)] < m_nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if ( m_stream.deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_stream.nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if ( m_deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)] == true) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)] == true) {
|
||||
// Allocate necessary internal buffers.
|
||||
uint64_t bufferBytes = m_stream.nUserChannels[modeToIdTable(_mode)] * m_stream.bufferSize * audio::getFormatBytes(m_stream.userFormat);
|
||||
m_stream.userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
uint64_t bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * m_bufferSize * audio::getFormatBytes(m_userFormat);
|
||||
m_userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
ATA_ERROR("airtaudio::api::Android::probeDeviceOpen: error allocating user buffer memory.");
|
||||
}
|
||||
setConvertInfo(_mode, _firstChannel);
|
||||
}
|
||||
ATA_INFO("device format : " << m_stream.deviceFormat[modeToIdTable(_mode)] << " user format : " << m_stream.userFormat);
|
||||
ATA_INFO("device channels : " << m_stream.nDeviceChannels[modeToIdTable(_mode)] << " user channels : " << m_stream.nUserChannels[modeToIdTable(_mode)]);
|
||||
ATA_INFO("do convert buffer : " << m_stream.doConvertBuffer[modeToIdTable(_mode)]);
|
||||
ATA_INFO("device format : " << m_deviceFormat[modeToIdTable(_mode)] << " user format : " << m_userFormat);
|
||||
ATA_INFO("device channels : " << m_nDeviceChannels[modeToIdTable(_mode)] << " user channels : " << m_nUserChannels[modeToIdTable(_mode)]);
|
||||
ATA_INFO("do convert buffer : " << m_doConvertBuffer[modeToIdTable(_mode)]);
|
||||
if (ret == false) {
|
||||
ATA_ERROR("Can not open device.");
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
/**
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#if !defined(__AIRTAUDIO_API_ANDROID_H__) && defined(__ANDROID_JAVA__)
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
|
||||
@ -83,7 +82,7 @@ airtaudio::api::Asio::Asio() {
|
||||
}
|
||||
|
||||
airtaudio::api::Asio::~Asio() {
|
||||
if (m_stream.state != airtaudio::state_closed) {
|
||||
if (m_state != airtaudio::state_closed) {
|
||||
closeStream();
|
||||
}
|
||||
if (m_coInitialized) {
|
||||
@ -109,7 +108,7 @@ rtaudio::DeviceInfo airtaudio::api::Asio::getDeviceInfo(uint32_t _device) {
|
||||
return info;
|
||||
}
|
||||
// If a stream is already open, we cannot probe other devices. Thus, use the saved results.
|
||||
if (m_stream.state != airtaudio::state_closed) {
|
||||
if (m_state != airtaudio::state_closed) {
|
||||
if (_device >= m_devices.size()) {
|
||||
ATA_ERROR("device ID was not present before stream was opened.");
|
||||
return info;
|
||||
@ -222,8 +221,8 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
|
||||
airtaudio::StreamOptions *_options) {
|
||||
// For ASIO, a duplex stream MUST use the same driver.
|
||||
if ( _mode == airtaudio::mode_input
|
||||
&& m_stream.mode == airtaudio::mode_output
|
||||
&& m_stream.device[0] != _device) {
|
||||
&& m_mode == airtaudio::mode_output
|
||||
&& m_device[0] != _device) {
|
||||
ATA_ERROR("an ASIO duplex stream must use the same device for input and output!");
|
||||
return false;
|
||||
}
|
||||
@ -235,7 +234,7 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
|
||||
}
|
||||
// Only load the driver once for duplex stream.
|
||||
if ( _mode != airtaudio::mode_input
|
||||
|| m_stream.mode != airtaudio::mode_output) {
|
||||
|| m_mode != airtaudio::mode_output) {
|
||||
// The getDeviceInfo() function will not work when a stream is open
|
||||
// because ASIO does not allow multiple devices to run at the same
|
||||
// time. Thus, we'll probe the system before opening a stream and
|
||||
@ -267,9 +266,9 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
|
||||
ATA_ERROR("driver (" << driverName << ") does not support requested channel count (" << _channels << ") + offset (" << _firstChannel << ").");
|
||||
return false;
|
||||
}
|
||||
m_stream.nDeviceChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_stream.nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_stream.channelOffset[modeToIdTable(_mode)] = _firstChannel;
|
||||
m_nDeviceChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_channelOffset[modeToIdTable(_mode)] = _firstChannel;
|
||||
// Verify the sample rate is supported.
|
||||
result = ASIOCanSampleRate((ASIOSampleRate) _sampleRate);
|
||||
if (result != ASE_OK) {
|
||||
@ -309,41 +308,41 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
|
||||
return false;
|
||||
}
|
||||
// Assuming WINDOWS host is always little-endian.
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = false;
|
||||
m_stream.userFormat = _format;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = 0;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = false;
|
||||
m_userFormat = _format;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = 0;
|
||||
if ( channelInfo.type == ASIOSTInt16MSB
|
||||
|| channelInfo.type == ASIOSTInt16LSB) {
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
if (channelInfo.type == ASIOSTInt16MSB) {
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
} else if ( channelInfo.type == ASIOSTInt32MSB
|
||||
|| channelInfo.type == ASIOSTInt32LSB) {
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT32;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT32;
|
||||
if (channelInfo.type == ASIOSTInt32MSB) {
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
} else if ( channelInfo.type == ASIOSTFloat32MSB
|
||||
|| channelInfo.type == ASIOSTFloat32LSB) {
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_FLOAT32;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_FLOAT32;
|
||||
if (channelInfo.type == ASIOSTFloat32MSB) {
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
} else if ( channelInfo.type == ASIOSTFloat64MSB
|
||||
|| channelInfo.type == ASIOSTFloat64LSB) {
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_FLOAT64;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_FLOAT64;
|
||||
if (channelInfo.type == ASIOSTFloat64MSB) {
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
} else if ( channelInfo.type == ASIOSTInt24MSB
|
||||
|| channelInfo.type == ASIOSTInt24LSB) {
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT24;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT24;
|
||||
if (channelInfo.type == ASIOSTInt24MSB) {
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceFormat[modeToIdTable(_mode)] == 0) {
|
||||
if (m_deviceFormat[modeToIdTable(_mode)] == 0) {
|
||||
drivers.removeCurrentDriver();
|
||||
ATA_ERROR("driver (" << driverName << ") data format not supported by RtAudio.");
|
||||
return false;
|
||||
@ -394,18 +393,18 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
|
||||
*_bufferSize = (*_bufferSize + granularity-1) / granularity * granularity;
|
||||
}
|
||||
if ( _mode == airtaudio::mode_input
|
||||
&& m_stream.mode == airtaudio::mode_output
|
||||
&& m_stream.bufferSize != *_bufferSize) {
|
||||
&& m_mode == airtaudio::mode_output
|
||||
&& m_bufferSize != *_bufferSize) {
|
||||
drivers.removeCurrentDriver();
|
||||
ATA_ERROR("input/output buffersize discrepancy!");
|
||||
return false;
|
||||
}
|
||||
m_stream.bufferSize = *_bufferSize;
|
||||
m_stream.nBuffers = 2;
|
||||
m_bufferSize = *_bufferSize;
|
||||
m_nBuffers = 2;
|
||||
// ASIO always uses non-interleaved buffers.
|
||||
m_stream.deviceInterleaved[modeToIdTable(_mode)] = false;
|
||||
m_deviceInterleaved[modeToIdTable(_mode)] = false;
|
||||
// Allocate, if necessary, our AsioHandle structure for the stream.
|
||||
AsioHandle *handle = (AsioHandle *) m_stream.apiHandle;
|
||||
AsioHandle *handle = (AsioHandle *) m_apiHandle;
|
||||
if (handle == nullptr) {
|
||||
handle = new AsioHandle;
|
||||
if (handle == nullptr) {
|
||||
@ -419,14 +418,14 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
|
||||
TRUE, // manual-reset
|
||||
FALSE, // non-signaled initially
|
||||
nullptr); // unnamed
|
||||
m_stream.apiHandle = (void *) handle;
|
||||
m_apiHandle = (void *) handle;
|
||||
}
|
||||
// Create the ASIO internal buffers. Since RtAudio sets up input
|
||||
// and output separately, we'll have to dispose of previously
|
||||
// created output buffers for a duplex stream.
|
||||
long inputLatency, outputLatency;
|
||||
if ( _mode == airtaudio::mode_input
|
||||
&& m_stream.mode == airtaudio::mode_output) {
|
||||
&& m_mode == airtaudio::mode_output) {
|
||||
ASIODisposeBuffers();
|
||||
if (handle->bufferInfos == nullptr) {
|
||||
free(handle->bufferInfos);
|
||||
@ -435,7 +434,7 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
|
||||
}
|
||||
// Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
|
||||
bool buffersAllocated = false;
|
||||
uint32_t i, nChannels = m_stream.nDeviceChannels[0] + m_stream.nDeviceChannels[1];
|
||||
uint32_t i, nChannels = m_nDeviceChannels[0] + m_nDeviceChannels[1];
|
||||
handle->bufferInfos = (ASIOBufferInfo *) malloc(nChannels * sizeof(ASIOBufferInfo));
|
||||
if (handle->bufferInfos == nullptr) {
|
||||
ATA_ERROR("error allocating bufferInfo memory for driver (" << driverName << ").");
|
||||
@ -443,14 +442,14 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
|
||||
}
|
||||
ASIOBufferInfo *infos;
|
||||
infos = handle->bufferInfos;
|
||||
for (i=0; i<m_stream.nDeviceChannels[0]; i++, infos++) {
|
||||
for (i=0; i<m_nDeviceChannels[0]; i++, infos++) {
|
||||
infos->isInput = ASIOFalse;
|
||||
infos->channelNum = i + m_stream.channelOffset[0];
|
||||
infos->channelNum = i + m_channelOffset[0];
|
||||
infos->buffers[0] = infos->buffers[1] = 0;
|
||||
}
|
||||
for (i=0; i<m_stream.nDeviceChannels[1]; i++, infos++) {
|
||||
for (i=0; i<m_nDeviceChannels[1]; i++, infos++) {
|
||||
infos->isInput = ASIOTrue;
|
||||
infos->channelNum = i + m_stream.channelOffset[1];
|
||||
infos->channelNum = i + m_channelOffset[1];
|
||||
infos->buffers[0] = infos->buffers[1] = 0;
|
||||
}
|
||||
// Set up the ASIO callback structure and create the ASIO data buffers.
|
||||
@ -458,35 +457,35 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
|
||||
asioCallbacks.sampleRateDidChange = &sampleRateChanged;
|
||||
asioCallbacks.asioMessage = &asioMessages;
|
||||
asioCallbacks.bufferSwitchTimeInfo = nullptr;
|
||||
result = ASIOCreateBuffers(handle->bufferInfos, nChannels, m_stream.bufferSize, &asioCallbacks);
|
||||
result = ASIOCreateBuffers(handle->bufferInfos, nChannels, m_bufferSize, &asioCallbacks);
|
||||
if (result != ASE_OK) {
|
||||
ATA_ERROR("driver (" << driverName << ") error (" << getAsioErrorString(result) << ") creating buffers.");
|
||||
goto error;
|
||||
}
|
||||
buffersAllocated = true;
|
||||
// Set flags for buffer conversion.
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_stream.userFormat != m_stream.deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_userFormat != m_deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if ( m_stream.deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_stream.nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if ( m_deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
// Allocate necessary internal buffers
|
||||
uint64_t bufferBytes;
|
||||
bufferBytes = m_stream.nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_stream.userFormat);
|
||||
m_stream.userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat);
|
||||
m_userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
ATA_ERROR("error allocating user buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
bool makeBuffer = true;
|
||||
bufferBytes = m_stream.nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_stream.deviceFormat[modeToIdTable(_mode)]);
|
||||
bufferBytes = m_nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]);
|
||||
if (_mode == airtaudio::mode_input) {
|
||||
if (m_stream.mode == airtaudio::mode_output && m_stream.deviceBuffer) {
|
||||
uint64_t bytesOut = m_stream.nDeviceChannels[0] * audio::getFormatBytes(m_stream.deviceFormat[0]);
|
||||
if (m_mode == airtaudio::mode_output && m_deviceBuffer) {
|
||||
uint64_t bytesOut = m_nDeviceChannels[0] * audio::getFormatBytes(m_deviceFormat[0]);
|
||||
if (bufferBytes <= bytesOut) {
|
||||
makeBuffer = false;
|
||||
}
|
||||
@ -494,41 +493,41 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
|
||||
}
|
||||
if (makeBuffer) {
|
||||
bufferBytes *= *_bufferSize;
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = nullptr;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = nullptr;
|
||||
}
|
||||
m_stream.deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.deviceBuffer == nullptr) {
|
||||
m_deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_deviceBuffer == nullptr) {
|
||||
ATA_ERROR("error allocating device buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_stream.sampleRate = _sampleRate;
|
||||
m_stream.device[modeToIdTable(_mode)] = _device;
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
asioCallbackInfo = &m_stream.callbackInfo;
|
||||
m_stream.callbackInfo.object = (void*)this;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
m_sampleRate = _sampleRate;
|
||||
m_device[modeToIdTable(_mode)] = _device;
|
||||
m_state = airtaudio::state_stopped;
|
||||
asioCallbackInfo = &m_callbackInfo;
|
||||
m_callbackInfo.object = (void*)this;
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
&& _mode == airtaudio::mode_input) {
|
||||
// We had already set up an output stream.
|
||||
m_stream.mode = airtaudio::mode_duplex;
|
||||
m_mode = airtaudio::mode_duplex;
|
||||
} else {
|
||||
m_stream.mode = _mode;
|
||||
m_mode = _mode;
|
||||
}
|
||||
// Determine device latencies
|
||||
result = ASIOGetLatencies(&inputLatency, &outputLatency);
|
||||
if (result != ASE_OK) {
|
||||
ATA_ERROR("driver (" << driverName << ") error (" << getAsioErrorString(result) << ") getting latency.");
|
||||
} else {
|
||||
m_stream.latency[0] = outputLatency;
|
||||
m_stream.latency[1] = inputLatency;
|
||||
m_latency[0] = outputLatency;
|
||||
m_latency[1] = inputLatency;
|
||||
}
|
||||
// Setup the buffer conversion information structure. We don't use
|
||||
// buffers to do channel offsets, so we override that parameter
|
||||
// here.
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
setConvertInfo(_mode, 0);
|
||||
}
|
||||
return true;
|
||||
@ -545,53 +544,53 @@ error:
|
||||
}
|
||||
delete handle;
|
||||
handle = nullptr;
|
||||
m_stream.apiHandle = 0;
|
||||
m_apiHandle = 0;
|
||||
}
|
||||
for (int32_t i=0; i<2; i++) {
|
||||
if (m_stream.userBuffer[i]) {
|
||||
free(m_stream.userBuffer[i]);
|
||||
m_stream.userBuffer[i] = 0;
|
||||
if (m_userBuffer[i]) {
|
||||
free(m_userBuffer[i]);
|
||||
m_userBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = 0;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::api::Asio::closeStream() {
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("no open stream to close!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
if (m_state == airtaudio::state_running) {
|
||||
m_state = airtaudio::state_stopped;
|
||||
ASIOStop();
|
||||
}
|
||||
ASIODisposeBuffers();
|
||||
drivers.removeCurrentDriver();
|
||||
AsioHandle *handle = (AsioHandle *) m_stream.apiHandle;
|
||||
AsioHandle *handle = (AsioHandle *) m_apiHandle;
|
||||
if (handle) {
|
||||
CloseHandle(handle->condition);
|
||||
if (handle->bufferInfos) {
|
||||
free(handle->bufferInfos);
|
||||
}
|
||||
delete handle;
|
||||
m_stream.apiHandle = 0;
|
||||
m_apiHandle = 0;
|
||||
}
|
||||
for (int32_t i=0; i<2; i++) {
|
||||
if (m_stream.userBuffer[i]) {
|
||||
free(m_stream.userBuffer[i]);
|
||||
m_stream.userBuffer[i] = 0;
|
||||
if (m_userBuffer[i]) {
|
||||
free(m_userBuffer[i]);
|
||||
m_userBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = 0;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = 0;
|
||||
}
|
||||
m_stream.mode = airtaudio::mode_unknow;
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_mode = airtaudio::mode_unknow;
|
||||
m_state = airtaudio::state_closed;
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
|
||||
@ -601,11 +600,11 @@ enum airtaudio::error airtaudio::api::Asio::startStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
if (m_state == airtaudio::state_running) {
|
||||
ATA_ERROR("the stream is already running!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
AsioHandle *handle = (AsioHandle *) m_stream.apiHandle;
|
||||
AsioHandle *handle = (AsioHandle *) m_apiHandle;
|
||||
ASIOError result = ASIOStart();
|
||||
if (result != ASE_OK) {
|
||||
ATA_ERROR("error (" << getAsioErrorString(result) << ") starting device.");
|
||||
@ -614,7 +613,7 @@ enum airtaudio::error airtaudio::api::Asio::startStream() {
|
||||
handle->drainCounter = 0;
|
||||
handle->internalDrain = false;
|
||||
ResetEvent(handle->condition);
|
||||
m_stream.state = airtaudio::state_running;
|
||||
m_state = airtaudio::state_running;
|
||||
asioXRun = false;
|
||||
unlock:
|
||||
stopThreadCalled = false;
|
||||
@ -628,18 +627,18 @@ enum airtaudio::error airtaudio::api::Asio::stopStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
AsioHandle *handle = (AsioHandle *) m_stream.apiHandle;
|
||||
if (m_stream.mode == airtaudio::mode_output || m_stream.mode == airtaudio::mode_duplex) {
|
||||
AsioHandle *handle = (AsioHandle *) m_apiHandle;
|
||||
if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) {
|
||||
if (handle->drainCounter == 0) {
|
||||
handle->drainCounter = 2;
|
||||
WaitForSingleObject(handle->condition, INFINITE); // block until signaled
|
||||
}
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_state = airtaudio::state_stopped;
|
||||
ASIOError result = ASIOStop();
|
||||
if (result != ASE_OK) {
|
||||
ATA_ERROR("error (" << getAsioErrorString(result) << ") stopping device.");
|
||||
@ -654,7 +653,7 @@ enum airtaudio::error airtaudio::api::Asio::abortStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
error(airtaudio::error_warning);
|
||||
return;
|
||||
@ -664,7 +663,7 @@ enum airtaudio::error airtaudio::api::Asio::abortStream() {
|
||||
// noted where the device buffers need to be zeroed to avoid
|
||||
// continuing sound, even when the device buffers are completely
|
||||
// disposed. So now, calling abort is the same as calling stop.
|
||||
// AsioHandle *handle = (AsioHandle *) m_stream.apiHandle;
|
||||
// AsioHandle *handle = (AsioHandle *) m_apiHandle;
|
||||
// handle->drainCounter = 2;
|
||||
return stopStream();
|
||||
}
|
||||
@ -683,25 +682,25 @@ static unsigned __stdcall asioStopStream(void *_ptr) {
|
||||
}
|
||||
|
||||
bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
|
||||
if ( m_stream.state == airtaudio::state_stopped
|
||||
|| m_stream.state == airtaudio::state_stopping) {
|
||||
if ( m_state == airtaudio::state_stopped
|
||||
|| m_state == airtaudio::state_stopping) {
|
||||
return true;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("the stream is closed ... this shouldn't happen!");
|
||||
return false;
|
||||
}
|
||||
CallbackInfo *info = (CallbackInfo *) &m_stream.callbackInfo;
|
||||
AsioHandle *handle = (AsioHandle *) m_stream.apiHandle;
|
||||
CallbackInfo *info = (CallbackInfo *) &m_callbackInfo;
|
||||
AsioHandle *handle = (AsioHandle *) m_apiHandle;
|
||||
// Check if we were draining the stream and signal if finished.
|
||||
if (handle->drainCounter > 3) {
|
||||
m_stream.state = airtaudio::state_stopping;
|
||||
m_state = airtaudio::state_stopping;
|
||||
if (handle->internalDrain == false) {
|
||||
SetEvent(handle->condition);
|
||||
} else { // spawn a thread to stop the stream
|
||||
unsigned threadId;
|
||||
m_stream.callbackInfo.thread = _beginthreadex(nullptr, 0, &asioStopStream,
|
||||
&m_stream.callbackInfo, 0, &threadId);
|
||||
m_callbackInfo.thread = _beginthreadex(nullptr, 0, &asioStopStream,
|
||||
&m_callbackInfo, 0, &threadId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -710,27 +709,27 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
|
||||
if (handle->drainCounter == 0) {
|
||||
double streamTime = getStreamTime();
|
||||
rtaudio::streamStatus status = 0;
|
||||
if (m_stream.mode != airtaudio::mode_input && asioXRun == true) {
|
||||
if (m_mode != airtaudio::mode_input && asioXRun == true) {
|
||||
status |= RTAUDIO_airtaudio::status_underflow;
|
||||
asioXRun = false;
|
||||
}
|
||||
if (m_stream.mode != airtaudio::mode_output && asioXRun == true) {
|
||||
if (m_mode != airtaudio::mode_output && asioXRun == true) {
|
||||
status |= RTAUDIO_airtaudio::mode_input_OVERFLOW;
|
||||
asioXRun = false;
|
||||
}
|
||||
int32_t cbReturnValue = info->callback(m_stream.userBuffer[0],
|
||||
m_stream.userBuffer[1],
|
||||
m_stream.bufferSize,
|
||||
int32_t cbReturnValue = info->callback(m_userBuffer[0],
|
||||
m_userBuffer[1],
|
||||
m_bufferSize,
|
||||
streamTime,
|
||||
status);
|
||||
if (cbReturnValue == 2) {
|
||||
m_stream.state = airtaudio::state_stopping;
|
||||
m_state = airtaudio::state_stopping;
|
||||
handle->drainCounter = 2;
|
||||
unsigned threadId;
|
||||
m_stream.callbackInfo.thread = _beginthreadex(nullptr,
|
||||
m_callbackInfo.thread = _beginthreadex(nullptr,
|
||||
0,
|
||||
&asioStopStream,
|
||||
&m_stream.callbackInfo,
|
||||
&m_callbackInfo,
|
||||
0,
|
||||
&threadId);
|
||||
return true;
|
||||
@ -740,40 +739,40 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
|
||||
}
|
||||
}
|
||||
uint32_t nChannels, bufferBytes, i, j;
|
||||
nChannels = m_stream.nDeviceChannels[0] + m_stream.nDeviceChannels[1];
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
bufferBytes = m_stream.bufferSize * audio::getFormatBytes(m_stream.deviceFormat[0]);
|
||||
nChannels = m_nDeviceChannels[0] + m_nDeviceChannels[1];
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
bufferBytes = m_bufferSize * audio::getFormatBytes(m_deviceFormat[0]);
|
||||
if (handle->drainCounter > 1) { // write zeros to the output stream
|
||||
for (i=0, j=0; i<nChannels; i++) {
|
||||
if (handle->bufferInfos[i].isInput != ASIOTrue) {
|
||||
memset(handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes);
|
||||
}
|
||||
}
|
||||
} else if (m_stream.doConvertBuffer[0]) {
|
||||
convertBuffer(m_stream.deviceBuffer, m_stream.userBuffer[0], m_stream.convertInfo[0]);
|
||||
if (m_stream.doByteSwap[0]) {
|
||||
byteSwapBuffer(m_stream.deviceBuffer,
|
||||
m_stream.bufferSize * m_stream.nDeviceChannels[0],
|
||||
m_stream.deviceFormat[0]);
|
||||
} else if (m_doConvertBuffer[0]) {
|
||||
convertBuffer(m_deviceBuffer, m_userBuffer[0], m_convertInfo[0]);
|
||||
if (m_doByteSwap[0]) {
|
||||
byteSwapBuffer(m_deviceBuffer,
|
||||
m_bufferSize * m_nDeviceChannels[0],
|
||||
m_deviceFormat[0]);
|
||||
}
|
||||
for (i=0, j=0; i<nChannels; i++) {
|
||||
if (handle->bufferInfos[i].isInput != ASIOTrue) {
|
||||
memcpy(handle->bufferInfos[i].buffers[bufferIndex],
|
||||
&m_stream.deviceBuffer[j++*bufferBytes],
|
||||
&m_deviceBuffer[j++*bufferBytes],
|
||||
bufferBytes);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_stream.doByteSwap[0]) {
|
||||
byteSwapBuffer(m_stream.userBuffer[0],
|
||||
m_stream.bufferSize * m_stream.nUserChannels[0],
|
||||
m_stream.userFormat);
|
||||
if (m_doByteSwap[0]) {
|
||||
byteSwapBuffer(m_userBuffer[0],
|
||||
m_bufferSize * m_nUserChannels[0],
|
||||
m_userFormat);
|
||||
}
|
||||
for (i=0, j=0; i<nChannels; i++) {
|
||||
if (handle->bufferInfos[i].isInput != ASIOTrue) {
|
||||
memcpy(handle->bufferInfos[i].buffers[bufferIndex],
|
||||
&m_stream.userBuffer[0][bufferBytes*j++],
|
||||
&m_userBuffer[0][bufferBytes*j++],
|
||||
bufferBytes);
|
||||
}
|
||||
}
|
||||
@ -783,38 +782,38 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
bufferBytes = m_stream.bufferSize * audio::getFormatBytes(m_stream.deviceFormat[1]);
|
||||
if (m_stream.doConvertBuffer[1]) {
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
bufferBytes = m_bufferSize * audio::getFormatBytes(m_deviceFormat[1]);
|
||||
if (m_doConvertBuffer[1]) {
|
||||
// Always interleave ASIO input data.
|
||||
for (i=0, j=0; i<nChannels; i++) {
|
||||
if (handle->bufferInfos[i].isInput == ASIOTrue) {
|
||||
memcpy(&m_stream.deviceBuffer[j++*bufferBytes],
|
||||
memcpy(&m_deviceBuffer[j++*bufferBytes],
|
||||
handle->bufferInfos[i].buffers[bufferIndex],
|
||||
bufferBytes);
|
||||
}
|
||||
}
|
||||
if (m_stream.doByteSwap[1]) {
|
||||
byteSwapBuffer(m_stream.deviceBuffer,
|
||||
m_stream.bufferSize * m_stream.nDeviceChannels[1],
|
||||
m_stream.deviceFormat[1]);
|
||||
if (m_doByteSwap[1]) {
|
||||
byteSwapBuffer(m_deviceBuffer,
|
||||
m_bufferSize * m_nDeviceChannels[1],
|
||||
m_deviceFormat[1]);
|
||||
}
|
||||
convertBuffer(m_stream.userBuffer[1],
|
||||
m_stream.deviceBuffer,
|
||||
m_stream.convertInfo[1]);
|
||||
convertBuffer(m_userBuffer[1],
|
||||
m_deviceBuffer,
|
||||
m_convertInfo[1]);
|
||||
} else {
|
||||
for (i=0, j=0; i<nChannels; i++) {
|
||||
if (handle->bufferInfos[i].isInput == ASIOTrue) {
|
||||
memcpy(&m_stream.userBuffer[1][bufferBytes*j++],
|
||||
memcpy(&m_userBuffer[1][bufferBytes*j++],
|
||||
handle->bufferInfos[i].buffers[bufferIndex],
|
||||
bufferBytes);
|
||||
}
|
||||
}
|
||||
if (m_stream.doByteSwap[1]) {
|
||||
byteSwapBuffer(m_stream.userBuffer[1],
|
||||
m_stream.bufferSize * m_stream.nUserChannels[1],
|
||||
m_stream.userFormat);
|
||||
if (m_doByteSwap[1]) {
|
||||
byteSwapBuffer(m_userBuffer[1],
|
||||
m_bufferSize * m_nUserChannels[1],
|
||||
m_userFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#if !defined(__AIRTAUDIO_API_ASIO_H__) && defined(__WINDOWS_ASIO__)
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
|
||||
@ -95,7 +94,7 @@ airtaudio::api::Core::~Core() {
|
||||
// The subclass destructor gets called before the base class
|
||||
// destructor, so close an existing stream before deallocating
|
||||
// apiDeviceId memory.
|
||||
if (m_stream.state != airtaudio::state_closed) {
|
||||
if (m_state != airtaudio::state_closed) {
|
||||
closeStream();
|
||||
}
|
||||
}
|
||||
@ -385,16 +384,15 @@ airtaudio::DeviceInfo airtaudio::api::Core::getDeviceInfo(uint32_t _device) {
|
||||
return info;
|
||||
}
|
||||
|
||||
static OSStatus callbackHandler(AudioDeviceID _inDevice,
|
||||
OSStatus airtaudio::api::Core::callbackEvent(AudioDeviceID _inDevice,
|
||||
const AudioTimeStamp* _inNow,
|
||||
const AudioBufferList* _inInputData,
|
||||
const AudioTimeStamp* _inInputTime,
|
||||
AudioBufferList* _outOutputData,
|
||||
const AudioTimeStamp* _inOutputTime,
|
||||
void* _infoPointer) {
|
||||
airtaudio::CallbackInfo* info = (airtaudio::CallbackInfo*)_infoPointer;
|
||||
airtaudio::api::Core* object = (airtaudio::api::Core*)info->object;
|
||||
if (object->callbackEvent(_inDevice, _inInputData, _outOutputData) == false) {
|
||||
void* _userData) {
|
||||
airtaudio::api::Core* myClass = reinterpret_cast<airtaudio::api::Core*>(_userData);
|
||||
if (myClass->callbackEvent(_inDevice, _inInputData, _outOutputData) == false) {
|
||||
return kAudioHardwareUnspecifiedError;
|
||||
} else {
|
||||
return kAudioHardwareNoError;
|
||||
@ -598,14 +596,14 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device,
|
||||
// If attempting to setup a duplex stream, the bufferSize parameter
|
||||
// MUST be the same in both directions!
|
||||
*_bufferSize = theSize;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
&& _mode == airtaudio::mode_input
|
||||
&& *_bufferSize != m_stream.bufferSize) {
|
||||
&& *_bufferSize != m_bufferSize) {
|
||||
ATA_ERROR("system error setting buffer size for duplex stream on device (" << _device << ").");
|
||||
return false;
|
||||
}
|
||||
m_stream.bufferSize = *_bufferSize;
|
||||
m_stream.nBuffers = 1;
|
||||
m_bufferSize = *_bufferSize;
|
||||
m_nBuffers = 1;
|
||||
// Try to set "hog" mode ... it's not clear to me this is working.
|
||||
if ( _options != nullptr
|
||||
&& _options->flags & HOG_DEVICE) {
|
||||
@ -763,7 +761,7 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device,
|
||||
if (AudioObjectHasProperty(id, &property) == true) {
|
||||
result = AudioObjectGetPropertyData(id, &property, 0, nullptr, &dataSize, &latency);
|
||||
if (result == kAudioHardwareNoError) {
|
||||
m_stream.latency[ _mode ] = latency;
|
||||
m_latency[ _mode ] = latency;
|
||||
} else {
|
||||
ATA_ERROR("system error (" << getErrorCode(result) << ") getting device latency for device (" << _device << ").");
|
||||
return false;
|
||||
@ -772,75 +770,75 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device,
|
||||
// Byte-swapping: According to AudioHardware.h, the stream data will
|
||||
// always be presented in native-endian format, so we should never
|
||||
// need to byte swap.
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = false;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = false;
|
||||
// From the CoreAudio documentation, PCM data must be supplied as
|
||||
// 32-bit floats.
|
||||
m_stream.userFormat = _format;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = FLOAT32;
|
||||
m_userFormat = _format;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = FLOAT32;
|
||||
if (streamCount == 1) {
|
||||
m_stream.nDeviceChannels[modeToIdTable(_mode)] = description.mChannelsPerFrame;
|
||||
m_nDeviceChannels[modeToIdTable(_mode)] = description.mChannelsPerFrame;
|
||||
} else {
|
||||
// multiple streams
|
||||
m_stream.nDeviceChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_nDeviceChannels[modeToIdTable(_mode)] = _channels;
|
||||
}
|
||||
m_stream.nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_stream.channelOffset[modeToIdTable(_mode)] = channelOffset; // offset within a CoreAudio stream
|
||||
m_stream.deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
m_nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_channelOffset[modeToIdTable(_mode)] = channelOffset; // offset within a CoreAudio stream
|
||||
m_deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
if (monoMode == true) {
|
||||
m_stream.deviceInterleaved[modeToIdTable(_mode)] = false;
|
||||
m_deviceInterleaved[modeToIdTable(_mode)] = false;
|
||||
}
|
||||
// Set flags for buffer conversion.
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_stream.userFormat != m_stream.deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_userFormat != m_deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if (m_stream.nUserChannels[modeToIdTable(_mode)] < m_stream.nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if (m_nUserChannels[modeToIdTable(_mode)] < m_nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if (streamCount == 1) {
|
||||
if ( m_stream.nUserChannels[modeToIdTable(_mode)] > 1
|
||||
&& m_stream.deviceInterleaved[modeToIdTable(_mode)] == false) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if ( m_nUserChannels[modeToIdTable(_mode)] > 1
|
||||
&& m_deviceInterleaved[modeToIdTable(_mode)] == false) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
} else if (monoMode) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
// Allocate our CoreHandle structure for the stream.
|
||||
CoreHandle *handle = 0;
|
||||
if (m_stream.apiHandle == 0) {
|
||||
if (m_apiHandle == 0) {
|
||||
handle = new CoreHandle;
|
||||
if (handle == nullptr) {
|
||||
ATA_ERROR("error allocating CoreHandle memory.");
|
||||
return false;
|
||||
}
|
||||
m_stream.apiHandle = (void *) handle;
|
||||
m_apiHandle = (void *) handle;
|
||||
} else {
|
||||
handle = (CoreHandle *) m_stream.apiHandle;
|
||||
handle = (CoreHandle *) m_apiHandle;
|
||||
}
|
||||
handle->iStream[modeToIdTable(_mode)] = firstStream;
|
||||
handle->nStreams[modeToIdTable(_mode)] = streamCount;
|
||||
handle->id[modeToIdTable(_mode)] = id;
|
||||
// Allocate necessary internal buffers.
|
||||
uint64_t bufferBytes;
|
||||
bufferBytes = m_stream.nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_stream.userFormat);
|
||||
// m_stream.userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
m_stream.userBuffer[modeToIdTable(_mode)] = (char *) malloc(bufferBytes * sizeof(char));
|
||||
memset(m_stream.userBuffer[modeToIdTable(_mode)], 0, bufferBytes * sizeof(char));
|
||||
if (m_stream.userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat);
|
||||
// m_userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
m_userBuffer[modeToIdTable(_mode)] = (char *) malloc(bufferBytes * sizeof(char));
|
||||
memset(m_userBuffer[modeToIdTable(_mode)], 0, bufferBytes * sizeof(char));
|
||||
if (m_userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
ATA_ERROR("error allocating user buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
// If possible, we will make use of the CoreAudio stream buffers as
|
||||
// "device buffers". However, we can't do this if using multiple
|
||||
// streams.
|
||||
if ( m_stream.doConvertBuffer[modeToIdTable(_mode)]
|
||||
if ( m_doConvertBuffer[modeToIdTable(_mode)]
|
||||
&& handle->nStreams[modeToIdTable(_mode)] > 1) {
|
||||
bool makeBuffer = true;
|
||||
bufferBytes = m_stream.nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_stream.deviceFormat[modeToIdTable(_mode)]);
|
||||
bufferBytes = m_nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]);
|
||||
if (_mode == airtaudio::mode_input) {
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
&& m_stream.deviceBuffer) {
|
||||
uint64_t bytesOut = m_stream.nDeviceChannels[0] * audio::getFormatBytes(m_stream.deviceFormat[0]);
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
&& m_deviceBuffer) {
|
||||
uint64_t bytesOut = m_nDeviceChannels[0] * audio::getFormatBytes(m_deviceFormat[0]);
|
||||
if (bufferBytes <= bytesOut) {
|
||||
makeBuffer = false;
|
||||
}
|
||||
@ -848,23 +846,23 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device,
|
||||
}
|
||||
if (makeBuffer) {
|
||||
bufferBytes *= *_bufferSize;
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = nullptr;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = nullptr;
|
||||
}
|
||||
m_stream.deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.deviceBuffer == nullptr) {
|
||||
m_deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_deviceBuffer == nullptr) {
|
||||
ATA_ERROR("error allocating device buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_stream.sampleRate = _sampleRate;
|
||||
m_stream.device[modeToIdTable(_mode)] = _device;
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_stream.callbackInfo.object = (void *) this;
|
||||
m_sampleRate = _sampleRate;
|
||||
m_device[modeToIdTable(_mode)] = _device;
|
||||
m_state = airtaudio::state_stopped;
|
||||
m_callbackInfo.object = (void *) this;
|
||||
// Setup the buffer conversion information structure.
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (streamCount > 1) {
|
||||
setConvertInfo(_mode, 0);
|
||||
} else {
|
||||
@ -872,26 +870,26 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device,
|
||||
}
|
||||
}
|
||||
if ( _mode == airtaudio::mode_input
|
||||
&& m_stream.mode == airtaudio::mode_output
|
||||
&& m_stream.device[0] == _device) {
|
||||
&& m_mode == airtaudio::mode_output
|
||||
&& m_device[0] == _device) {
|
||||
// Only one callback procedure per device.
|
||||
m_stream.mode = airtaudio::mode_duplex;
|
||||
m_mode = airtaudio::mode_duplex;
|
||||
} else {
|
||||
#if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
|
||||
result = AudioDeviceCreateIOProcID(id, callbackHandler, (void *) &m_stream.callbackInfo, &handle->procId[modeToIdTable(_mode)]);
|
||||
result = AudioDeviceCreateIOProcID(id, &airtaudio::api::Core::callbackEvent, (void *) &m_callbackInfo, &handle->procId[modeToIdTable(_mode)]);
|
||||
#else
|
||||
// deprecated in favor of AudioDeviceCreateIOProcID()
|
||||
result = AudioDeviceAddIOProc(id, callbackHandler, (void *) &m_stream.callbackInfo);
|
||||
result = AudioDeviceAddIOProc(id, &airtaudio::api::Core::callbackEvent, (void *) &m_callbackInfo);
|
||||
#endif
|
||||
if (result != noErr) {
|
||||
ATA_ERROR("system error setting callback for device (" << _device << ").");
|
||||
goto error;
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
&& _mode == airtaudio::mode_input) {
|
||||
m_stream.mode = airtaudio::mode_duplex;
|
||||
m_mode = airtaudio::mode_duplex;
|
||||
} else {
|
||||
m_stream.mode = _mode;
|
||||
m_mode = _mode;
|
||||
}
|
||||
}
|
||||
// Setup the device property listener for over/underload.
|
||||
@ -901,67 +899,67 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device,
|
||||
error:
|
||||
if (handle) {
|
||||
delete handle;
|
||||
m_stream.apiHandle = 0;
|
||||
m_apiHandle = 0;
|
||||
}
|
||||
for (int32_t i=0; i<2; i++) {
|
||||
if (m_stream.userBuffer[i]) {
|
||||
free(m_stream.userBuffer[i]);
|
||||
m_stream.userBuffer[i] = 0;
|
||||
if (m_userBuffer[i]) {
|
||||
free(m_userBuffer[i]);
|
||||
m_userBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = 0;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = 0;
|
||||
}
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_state = airtaudio::state_closed;
|
||||
return false;
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::api::Core::closeStream() {
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("no open stream to close!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
CoreHandle *handle = (CoreHandle *) m_stream.apiHandle;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
AudioDeviceStop(handle->id[0], callbackHandler);
|
||||
CoreHandle *handle = (CoreHandle *) m_apiHandle;
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
if (m_state == airtaudio::state_running) {
|
||||
AudioDeviceStop(handle->id[0], &airtaudio::api::Core::callbackEvent);
|
||||
}
|
||||
#if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
|
||||
AudioDeviceDestroyIOProcID(handle->id[0], handle->procId[0]);
|
||||
#else
|
||||
// deprecated in favor of AudioDeviceDestroyIOProcID()
|
||||
AudioDeviceRemoveIOProc(handle->id[0], callbackHandler);
|
||||
AudioDeviceRemoveIOProc(handle->id[0], &airtaudio::api::Core::callbackEvent);
|
||||
#endif
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| ( m_stream.mode == airtaudio::mode_duplex
|
||||
&& m_stream.device[0] != m_stream.device[1])) {
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
AudioDeviceStop(handle->id[1], callbackHandler);
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| ( m_mode == airtaudio::mode_duplex
|
||||
&& m_device[0] != m_device[1])) {
|
||||
if (m_state == airtaudio::state_running) {
|
||||
AudioDeviceStop(handle->id[1], &airtaudio::api::Core::callbackEvent);
|
||||
}
|
||||
#if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
|
||||
AudioDeviceDestroyIOProcID(handle->id[1], handle->procId[1]);
|
||||
#else
|
||||
// deprecated in favor of AudioDeviceDestroyIOProcID()
|
||||
AudioDeviceRemoveIOProc(handle->id[1], callbackHandler);
|
||||
AudioDeviceRemoveIOProc(handle->id[1], &airtaudio::api::Core::callbackEvent);
|
||||
#endif
|
||||
}
|
||||
for (int32_t i=0; i<2; i++) {
|
||||
if (m_stream.userBuffer[i]) {
|
||||
free(m_stream.userBuffer[i]);
|
||||
m_stream.userBuffer[i] = nullptr;
|
||||
if (m_userBuffer[i]) {
|
||||
free(m_userBuffer[i]);
|
||||
m_userBuffer[i] = nullptr;
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = nullptr;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = nullptr;
|
||||
}
|
||||
delete handle;
|
||||
m_stream.apiHandle = 0;
|
||||
m_stream.mode = airtaudio::mode_unknow;
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_apiHandle = 0;
|
||||
m_mode = airtaudio::mode_unknow;
|
||||
m_state = airtaudio::state_closed;
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
|
||||
@ -969,32 +967,32 @@ enum airtaudio::error airtaudio::api::Core::startStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
if (m_state == airtaudio::state_running) {
|
||||
ATA_ERROR("the stream is already running!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
OSStatus result = noErr;
|
||||
CoreHandle *handle = (CoreHandle *) m_stream.apiHandle;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
result = AudioDeviceStart(handle->id[0], callbackHandler);
|
||||
CoreHandle *handle = (CoreHandle *) m_apiHandle;
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
result = AudioDeviceStart(handle->id[0], &airtaudio::api::Core::callbackEvent);
|
||||
if (result != noErr) {
|
||||
ATA_ERROR("system error (" << getErrorCode(result) << ") starting callback procedure on device (" << m_stream.device[0] << ").");
|
||||
ATA_ERROR("system error (" << getErrorCode(result) << ") starting callback procedure on device (" << m_device[0] << ").");
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| ( m_stream.mode == airtaudio::mode_duplex
|
||||
&& m_stream.device[0] != m_stream.device[1])) {
|
||||
result = AudioDeviceStart(handle->id[1], callbackHandler);
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| ( m_mode == airtaudio::mode_duplex
|
||||
&& m_device[0] != m_device[1])) {
|
||||
result = AudioDeviceStart(handle->id[1], &airtaudio::api::Core::callbackEvent);
|
||||
if (result != noErr) {
|
||||
ATA_ERROR("system error starting input callback procedure on device (" << m_stream.device[1] << ").");
|
||||
ATA_ERROR("system error starting input callback procedure on device (" << m_device[1] << ").");
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
handle->drainCounter = 0;
|
||||
handle->internalDrain = false;
|
||||
m_stream.state = airtaudio::state_running;
|
||||
m_state = airtaudio::state_running;
|
||||
unlock:
|
||||
if (result == noErr) {
|
||||
return airtaudio::error_none;
|
||||
@ -1006,35 +1004,35 @@ enum airtaudio::error airtaudio::api::Core::stopStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
OSStatus result = noErr;
|
||||
CoreHandle *handle = (CoreHandle *) m_stream.apiHandle;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
CoreHandle *handle = (CoreHandle *) m_apiHandle;
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
if (handle->drainCounter == 0) {
|
||||
std::unique_lock<std::mutex> lck(m_stream.mutex);
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
handle->drainCounter = 2;
|
||||
handle->condition.wait(lck);
|
||||
}
|
||||
result = AudioDeviceStop(handle->id[0], callbackHandler);
|
||||
result = AudioDeviceStop(handle->id[0], &airtaudio::api::Core::callbackEvent);
|
||||
if (result != noErr) {
|
||||
ATA_ERROR("system error (" << getErrorCode(result) << ") stopping callback procedure on device (" << m_stream.device[0] << ").");
|
||||
ATA_ERROR("system error (" << getErrorCode(result) << ") stopping callback procedure on device (" << m_device[0] << ").");
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| ( m_stream.mode == airtaudio::mode_duplex
|
||||
&& m_stream.device[0] != m_stream.device[1])) {
|
||||
result = AudioDeviceStop(handle->id[1], callbackHandler);
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| ( m_mode == airtaudio::mode_duplex
|
||||
&& m_device[0] != m_device[1])) {
|
||||
result = AudioDeviceStop(handle->id[1], &airtaudio::api::Core::callbackEvent);
|
||||
if (result != noErr) {
|
||||
ATA_ERROR("system error (" << getErrorCode(result) << ") stopping input callback procedure on device (" << m_stream.device[1] << ").");
|
||||
ATA_ERROR("system error (" << getErrorCode(result) << ") stopping input callback procedure on device (" << m_device[1] << ").");
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_state = airtaudio::state_stopped;
|
||||
unlock:
|
||||
if (result == noErr) {
|
||||
return airtaudio::error_none;
|
||||
@ -1046,11 +1044,11 @@ enum airtaudio::error airtaudio::api::Core::abortStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
CoreHandle* handle = (CoreHandle*)m_stream.apiHandle;
|
||||
CoreHandle* handle = (CoreHandle*)m_apiHandle;
|
||||
handle->drainCounter = 2;
|
||||
return stopStream();
|
||||
}
|
||||
@ -1060,30 +1058,29 @@ enum airtaudio::error airtaudio::api::Core::abortStream() {
|
||||
// aborted. It is better to handle it this way because the
|
||||
// callbackEvent() function probably should return before the AudioDeviceStop()
|
||||
// function is called.
|
||||
static void coreStopStream(void *_ptr) {
|
||||
airtaudio::CallbackInfo* info = (airtaudio::CallbackInfo*)_ptr;
|
||||
airtaudio::api::Core* object = (airtaudio::api::Core*)info->object;
|
||||
object->stopStream();
|
||||
void airtaudio::api::Core::coreStopStream(void *_userData) {
|
||||
airtaudio::api::Core* myClass = reinterpret_cast<airtaudio::api::Core*>(_userData);
|
||||
myClass->stopStream();
|
||||
}
|
||||
|
||||
bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
const AudioBufferList *_inBufferList,
|
||||
const AudioBufferList *_outBufferList) {
|
||||
if ( m_stream.state == airtaudio::state_stopped
|
||||
|| m_stream.state == airtaudio::state_stopping) {
|
||||
if ( m_state == airtaudio::state_stopped
|
||||
|| m_state == airtaudio::state_stopping) {
|
||||
return true;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("the stream is closed ... this shouldn't happen!");
|
||||
return false;
|
||||
}
|
||||
CallbackInfo *info = (CallbackInfo *) &m_stream.callbackInfo;
|
||||
CoreHandle *handle = (CoreHandle *) m_stream.apiHandle;
|
||||
CallbackInfo *info = (CallbackInfo *) &m_callbackInfo;
|
||||
CoreHandle *handle = (CoreHandle *) m_apiHandle;
|
||||
// Check if we were draining the stream and signal is finished.
|
||||
if (handle->drainCounter > 3) {
|
||||
m_stream.state = airtaudio::state_stopping;
|
||||
m_state = airtaudio::state_stopping;
|
||||
if (handle->internalDrain == true) {
|
||||
new std::thread(coreStopStream, info);
|
||||
new std::thread(&airtaudio::api::Core::coreStopStream, this);
|
||||
} else {
|
||||
// external call to stopStream()
|
||||
handle->condition.notify_one();
|
||||
@ -1094,26 +1091,26 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
// Invoke user callback to get fresh output data UNLESS we are
|
||||
// draining stream or duplex mode AND the input/output devices are
|
||||
// different AND this function is called for the input device.
|
||||
if (handle->drainCounter == 0 && (m_stream.mode != airtaudio::mode_duplex || _deviceId == outputDevice)) {
|
||||
if (handle->drainCounter == 0 && (m_mode != airtaudio::mode_duplex || _deviceId == outputDevice)) {
|
||||
double streamTime = getStreamTime();
|
||||
enum airtaudio::status status = airtaudio::status_ok;
|
||||
if ( m_stream.mode != airtaudio::mode_input
|
||||
if ( m_mode != airtaudio::mode_input
|
||||
&& handle->xrun[0] == true) {
|
||||
status |= airtaudio::status_underflow;
|
||||
handle->xrun[0] = false;
|
||||
}
|
||||
if ( m_stream.mode != airtaudio::mode_output
|
||||
if ( m_mode != airtaudio::mode_output
|
||||
&& handle->xrun[1] == true) {
|
||||
status |= airtaudio::mode_input_OVERFLOW;
|
||||
handle->xrun[1] = false;
|
||||
}
|
||||
int32_t cbReturnValue = info->callback(m_stream.userBuffer[0],
|
||||
m_stream.userBuffer[1],
|
||||
m_stream.bufferSize,
|
||||
int32_t cbReturnValue = info->callback(m_userBuffer[0],
|
||||
m_userBuffer[1],
|
||||
m_bufferSize,
|
||||
streamTime,
|
||||
status);
|
||||
if (cbReturnValue == 2) {
|
||||
m_stream.state = airtaudio::state_stopping;
|
||||
m_state = airtaudio::state_stopping;
|
||||
handle->drainCounter = 2;
|
||||
abortStream();
|
||||
return true;
|
||||
@ -1122,8 +1119,8 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
handle->internalDrain = true;
|
||||
}
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| ( m_stream.mode == airtaudio::mode_duplex
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| ( m_mode == airtaudio::mode_duplex
|
||||
&& _deviceId == outputDevice)) {
|
||||
if (handle->drainCounter > 1) {
|
||||
// write zeros to the output stream
|
||||
@ -1140,29 +1137,29 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
}
|
||||
}
|
||||
} else if (handle->nStreams[0] == 1) {
|
||||
if (m_stream.doConvertBuffer[0]) {
|
||||
if (m_doConvertBuffer[0]) {
|
||||
// convert directly to CoreAudio stream buffer
|
||||
convertBuffer((char*)_outBufferList->mBuffers[handle->iStream[0]].mData,
|
||||
m_stream.userBuffer[0],
|
||||
m_stream.convertInfo[0]);
|
||||
m_userBuffer[0],
|
||||
m_convertInfo[0]);
|
||||
} else {
|
||||
// copy from user buffer
|
||||
memcpy(_outBufferList->mBuffers[handle->iStream[0]].mData,
|
||||
m_stream.userBuffer[0],
|
||||
m_userBuffer[0],
|
||||
_outBufferList->mBuffers[handle->iStream[0]].mDataByteSize);
|
||||
}
|
||||
} else {
|
||||
// fill multiple streams
|
||||
float *inBuffer = (float *) m_stream.userBuffer[0];
|
||||
if (m_stream.doConvertBuffer[0]) {
|
||||
convertBuffer(m_stream.deviceBuffer, m_stream.userBuffer[0], m_stream.convertInfo[0]);
|
||||
inBuffer = (float *) m_stream.deviceBuffer;
|
||||
float *inBuffer = (float *) m_userBuffer[0];
|
||||
if (m_doConvertBuffer[0]) {
|
||||
convertBuffer(m_deviceBuffer, m_userBuffer[0], m_convertInfo[0]);
|
||||
inBuffer = (float *) m_deviceBuffer;
|
||||
}
|
||||
if (m_stream.deviceInterleaved[0] == false) { // mono mode
|
||||
if (m_deviceInterleaved[0] == false) { // mono mode
|
||||
uint32_t bufferBytes = _outBufferList->mBuffers[handle->iStream[0]].mDataByteSize;
|
||||
for (uint32_t i=0; i<m_stream.nUserChannels[0]; i++) {
|
||||
for (uint32_t i=0; i<m_nUserChannels[0]; i++) {
|
||||
memcpy(_outBufferList->mBuffers[handle->iStream[0]+i].mData,
|
||||
(void *)&inBuffer[i*m_stream.bufferSize],
|
||||
(void *)&inBuffer[i*m_bufferSize],
|
||||
bufferBytes);
|
||||
}
|
||||
} else {
|
||||
@ -1170,15 +1167,15 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
uint32_t streamChannels, channelsLeft, inJump, outJump, inOffset;
|
||||
float *out, *in;
|
||||
bool inInterleaved = true;
|
||||
uint32_t inChannels = m_stream.nUserChannels[0];
|
||||
if (m_stream.doConvertBuffer[0]) {
|
||||
uint32_t inChannels = m_nUserChannels[0];
|
||||
if (m_doConvertBuffer[0]) {
|
||||
inInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
|
||||
inChannels = m_stream.nDeviceChannels[0];
|
||||
inChannels = m_nDeviceChannels[0];
|
||||
}
|
||||
if (inInterleaved) {
|
||||
inOffset = 1;
|
||||
} else {
|
||||
inOffset = m_stream.bufferSize;
|
||||
inOffset = m_bufferSize;
|
||||
}
|
||||
channelsLeft = inChannels;
|
||||
for (uint32_t i=0; i<handle->nStreams[0]; i++) {
|
||||
@ -1187,9 +1184,9 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
streamChannels = _outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels;
|
||||
outJump = 0;
|
||||
// Account for possible channel offset in first stream
|
||||
if (i == 0 && m_stream.channelOffset[0] > 0) {
|
||||
streamChannels -= m_stream.channelOffset[0];
|
||||
outJump = m_stream.channelOffset[0];
|
||||
if (i == 0 && m_channelOffset[0] > 0) {
|
||||
streamChannels -= m_channelOffset[0];
|
||||
outJump = m_channelOffset[0];
|
||||
out += outJump;
|
||||
}
|
||||
// Account for possible unfilled channels at end of the last stream
|
||||
@ -1205,7 +1202,7 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
inJump = 1;
|
||||
in += (inChannels - channelsLeft) * inOffset;
|
||||
}
|
||||
for (uint32_t i=0; i<m_stream.bufferSize; i++) {
|
||||
for (uint32_t i=0; i<m_bufferSize; i++) {
|
||||
for (uint32_t j=0; j<streamChannels; j++) {
|
||||
*out++ = in[j*inOffset];
|
||||
}
|
||||
@ -1223,30 +1220,30 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
}
|
||||
AudioDeviceID inputDevice;
|
||||
inputDevice = handle->id[1];
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| ( m_stream.mode == airtaudio::mode_duplex
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| ( m_mode == airtaudio::mode_duplex
|
||||
&& _deviceId == inputDevice)) {
|
||||
if (handle->nStreams[1] == 1) {
|
||||
if (m_stream.doConvertBuffer[1]) {
|
||||
if (m_doConvertBuffer[1]) {
|
||||
// convert directly from CoreAudio stream buffer
|
||||
convertBuffer(m_stream.userBuffer[1],
|
||||
convertBuffer(m_userBuffer[1],
|
||||
(char *) _inBufferList->mBuffers[handle->iStream[1]].mData,
|
||||
m_stream.convertInfo[1]);
|
||||
m_convertInfo[1]);
|
||||
} else { // copy to user buffer
|
||||
memcpy(m_stream.userBuffer[1],
|
||||
memcpy(m_userBuffer[1],
|
||||
_inBufferList->mBuffers[handle->iStream[1]].mData,
|
||||
_inBufferList->mBuffers[handle->iStream[1]].mDataByteSize);
|
||||
}
|
||||
} else { // read from multiple streams
|
||||
float *outBuffer = (float *) m_stream.userBuffer[1];
|
||||
if (m_stream.doConvertBuffer[1]) {
|
||||
outBuffer = (float *) m_stream.deviceBuffer;
|
||||
float *outBuffer = (float *) m_userBuffer[1];
|
||||
if (m_doConvertBuffer[1]) {
|
||||
outBuffer = (float *) m_deviceBuffer;
|
||||
}
|
||||
if (m_stream.deviceInterleaved[1] == false) {
|
||||
if (m_deviceInterleaved[1] == false) {
|
||||
// mono mode
|
||||
uint32_t bufferBytes = _inBufferList->mBuffers[handle->iStream[1]].mDataByteSize;
|
||||
for (uint32_t i=0; i<m_stream.nUserChannels[1]; i++) {
|
||||
memcpy((void *)&outBuffer[i*m_stream.bufferSize],
|
||||
for (uint32_t i=0; i<m_nUserChannels[1]; i++) {
|
||||
memcpy((void *)&outBuffer[i*m_bufferSize],
|
||||
_inBufferList->mBuffers[handle->iStream[1]+i].mData,
|
||||
bufferBytes);
|
||||
}
|
||||
@ -1255,15 +1252,15 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
uint32_t streamChannels, channelsLeft, inJump, outJump, outOffset;
|
||||
float *out, *in;
|
||||
bool outInterleaved = true;
|
||||
uint32_t outChannels = m_stream.nUserChannels[1];
|
||||
if (m_stream.doConvertBuffer[1]) {
|
||||
uint32_t outChannels = m_nUserChannels[1];
|
||||
if (m_doConvertBuffer[1]) {
|
||||
outInterleaved = true; // device buffer will always be interleaved for nStreams > 1 and not mono mode
|
||||
outChannels = m_stream.nDeviceChannels[1];
|
||||
outChannels = m_nDeviceChannels[1];
|
||||
}
|
||||
if (outInterleaved) {
|
||||
outOffset = 1;
|
||||
} else {
|
||||
outOffset = m_stream.bufferSize;
|
||||
outOffset = m_bufferSize;
|
||||
}
|
||||
channelsLeft = outChannels;
|
||||
for (uint32_t i=0; i<handle->nStreams[1]; i++) {
|
||||
@ -1272,9 +1269,9 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
streamChannels = _inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels;
|
||||
inJump = 0;
|
||||
// Account for possible channel offset in first stream
|
||||
if (i == 0 && m_stream.channelOffset[1] > 0) {
|
||||
streamChannels -= m_stream.channelOffset[1];
|
||||
inJump = m_stream.channelOffset[1];
|
||||
if (i == 0 && m_channelOffset[1] > 0) {
|
||||
streamChannels -= m_channelOffset[1];
|
||||
inJump = m_channelOffset[1];
|
||||
in += inJump;
|
||||
}
|
||||
// Account for possible unread channels at end of the last stream
|
||||
@ -1290,7 +1287,7 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
outJump = 1;
|
||||
out += (outChannels - channelsLeft) * outOffset;
|
||||
}
|
||||
for (uint32_t i=0; i<m_stream.bufferSize; i++) {
|
||||
for (uint32_t i=0; i<m_bufferSize; i++) {
|
||||
for (uint32_t j=0; j<streamChannels; j++) {
|
||||
out[j*outOffset] = *in++;
|
||||
}
|
||||
@ -1300,16 +1297,16 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
|
||||
channelsLeft -= streamChannels;
|
||||
}
|
||||
}
|
||||
if (m_stream.doConvertBuffer[1]) { // convert from our internal "device" buffer
|
||||
convertBuffer(m_stream.userBuffer[1],
|
||||
m_stream.deviceBuffer,
|
||||
m_stream.convertInfo[1]);
|
||||
if (m_doConvertBuffer[1]) { // convert from our internal "device" buffer
|
||||
convertBuffer(m_userBuffer[1],
|
||||
m_deviceBuffer,
|
||||
m_convertInfo[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unlock:
|
||||
//m_stream.mutex.unlock();
|
||||
//m_mutex.unlock();
|
||||
airtaudio::Api::tickStreamTime();
|
||||
return true;
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#if !defined(__AIRTAUDIO_API_CORE_H__) && defined(__MACOSX_CORE__)
|
||||
@ -38,7 +37,14 @@ namespace airtaudio {
|
||||
bool callbackEvent(AudioDeviceID _deviceId,
|
||||
const AudioBufferList *_inBufferList,
|
||||
const AudioBufferList *_outBufferList);
|
||||
|
||||
static OSStatus callbackEvent(AudioDeviceID _inDevice,
|
||||
const AudioTimeStamp* _inNow,
|
||||
const AudioBufferList* _inInputData,
|
||||
const AudioTimeStamp* _inInputTime,
|
||||
AudioBufferList* _outOutputData,
|
||||
const AudioTimeStamp* _inOutputTime,
|
||||
void* _infoPointer);
|
||||
void coreStopStream(void *_userData);
|
||||
private:
|
||||
bool probeDeviceOpen(uint32_t _device,
|
||||
airtaudio::mode _mode,
|
||||
|
@ -1,7 +1,8 @@
|
||||
/**
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#if !defined(__AIRTAUDIO_API_CORE_IOS_H__) && defined(__IOS_CORE__)
|
||||
@ -46,6 +47,12 @@ namespace airtaudio {
|
||||
int32_t _frameRate);
|
||||
private:
|
||||
CoreIosPrivate* m_private;
|
||||
static OSStatus playbackCallback(void *_userData,
|
||||
AudioUnitRenderActionFlags* _ioActionFlags,
|
||||
const AudioTimeStamp* _inTimeStamp,
|
||||
uint32_t _inBusNumber,
|
||||
uint32_t _inNumberFrames,
|
||||
AudioBufferList* _ioData);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -1,7 +1,8 @@
|
||||
/**
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifdef __IOS_CORE__
|
||||
@ -128,15 +129,15 @@ void airtaudio::api::CoreIos::callBackEvent(void* _data,
|
||||
int32_t doStopStream = 0;
|
||||
double streamTime = getStreamTime();
|
||||
enum airtaudio::status status = airtaudio::status_ok;
|
||||
if (m_stream.doConvertBuffer[airtaudio::mode_output] == true) {
|
||||
doStopStream = m_stream.callbackInfo.callback(m_stream.userBuffer[airtaudio::mode_output],
|
||||
if (m_doConvertBuffer[airtaudio::mode_output] == true) {
|
||||
doStopStream = m_callbackInfo.callback(m_userBuffer[airtaudio::mode_output],
|
||||
nullptr,
|
||||
_frameRate,
|
||||
streamTime,
|
||||
status);
|
||||
convertBuffer((char*)_data, (char*)m_stream.userBuffer[airtaudio::mode_output], m_stream.convertInfo[airtaudio::mode_output]);
|
||||
convertBuffer((char*)_data, (char*)m_userBuffer[airtaudio::mode_output], m_convertInfo[airtaudio::mode_output]);
|
||||
} else {
|
||||
doStopStream = m_stream.callbackInfo.callback(_data,
|
||||
doStopStream = m_callbackInfo.callback(_data,
|
||||
nullptr,
|
||||
_frameRate,
|
||||
streamTime,
|
||||
@ -149,22 +150,22 @@ void airtaudio::api::CoreIos::callBackEvent(void* _data,
|
||||
airtaudio::Api::tickStreamTime();
|
||||
}
|
||||
|
||||
static OSStatus playbackCallback(void *_userData,
|
||||
AudioUnitRenderActionFlags *ioActionFlags,
|
||||
const AudioTimeStamp *inTimeStamp,
|
||||
uint32_t inBusNumber,
|
||||
uint32_t inNumberFrames,
|
||||
AudioBufferList *ioData) {
|
||||
OSStatus airtaudio::api::CoreIos::playbackCallback(void *_userData,
|
||||
AudioUnitRenderActionFlags* _ioActionFlags,
|
||||
const AudioTimeStamp* _inTimeStamp,
|
||||
uint32_t _inBusNumber,
|
||||
uint32_t _inNumberFrames,
|
||||
AudioBufferList* _ioData) {
|
||||
if (_userData == nullptr) {
|
||||
ATA_ERROR("callback event ... nullptr pointer");
|
||||
return -1;
|
||||
}
|
||||
airtaudio::api::CoreIos* myClass = static_cast<airtaudio::api::CoreIos*>(_userData);
|
||||
// get all requested buffer :
|
||||
for (int32_t iii=0; iii < ioData->mNumberBuffers; iii++) {
|
||||
AudioBuffer buffer = ioData->mBuffers[iii];
|
||||
for (int32_t iii=0; iii < _ioData->mNumberBuffers; iii++) {
|
||||
AudioBuffer buffer = _ioData->mBuffers[iii];
|
||||
int32_t numberFrame = buffer.mDataByteSize/2/*stereo*/ /sizeof(int16_t);
|
||||
ATA_VERBOSE("request data size: " << numberFrame << " busNumber=" << inBusNumber);
|
||||
ATA_VERBOSE("request data size: " << numberFrame << " busNumber=" << _inBusNumber);
|
||||
myClass->callBackEvent(buffer.mData, numberFrame);
|
||||
}
|
||||
return noErr;
|
||||
@ -187,40 +188,40 @@ bool airtaudio::api::CoreIos::probeDeviceOpen(uint32_t _device,
|
||||
bool ret = true;
|
||||
|
||||
// configure Airtaudio internal configuration:
|
||||
m_stream.userFormat = _format;
|
||||
m_stream.nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_stream.bufferSize = 8192;
|
||||
m_stream.sampleRate = _sampleRate;
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = false; // for endienness ...
|
||||
m_userFormat = _format;
|
||||
m_nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_bufferSize = 8192;
|
||||
m_sampleRate = _sampleRate;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = false; // for endienness ...
|
||||
|
||||
// TODO : For now, we write it in hard ==> to be update later ...
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = SINT16;
|
||||
m_stream.nDeviceChannels[modeToIdTable(_mode)] = 2;
|
||||
m_stream.deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = SINT16;
|
||||
m_nDeviceChannels[modeToIdTable(_mode)] = 2;
|
||||
m_deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_stream.userFormat != m_stream.deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_userFormat != m_deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if (m_stream.nUserChannels[modeToIdTable(_mode)] < m_stream.nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if (m_nUserChannels[modeToIdTable(_mode)] < m_nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if ( m_stream.deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_stream.nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if ( m_deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)] == true) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)] == true) {
|
||||
// Allocate necessary internal buffers.
|
||||
uint64_t bufferBytes = m_stream.nUserChannels[modeToIdTable(_mode)] * m_stream.bufferSize * audio::getFormatBytes(m_stream.userFormat);
|
||||
m_stream.userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
uint64_t bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * m_bufferSize * audio::getFormatBytes(m_userFormat);
|
||||
m_userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
ATA_ERROR("error allocating user buffer memory.");
|
||||
}
|
||||
setConvertInfo(_mode, _firstChannel);
|
||||
}
|
||||
ATA_INFO("device format : " << m_stream.deviceFormat[modeToIdTable(_mode)] << " user format : " << m_stream.userFormat);
|
||||
ATA_INFO("device channels : " << m_stream.nDeviceChannels[modeToIdTable(_mode)] << " user channels : " << m_stream.nUserChannels[modeToIdTable(_mode)]);
|
||||
ATA_INFO("do convert buffer : " << m_stream.doConvertBuffer[modeToIdTable(_mode)]);
|
||||
ATA_INFO("device format : " << m_deviceFormat[modeToIdTable(_mode)] << " user format : " << m_userFormat);
|
||||
ATA_INFO("device channels : " << m_nDeviceChannels[modeToIdTable(_mode)] << " user channels : " << m_nUserChannels[modeToIdTable(_mode)]);
|
||||
ATA_INFO("do convert buffer : " << m_doConvertBuffer[modeToIdTable(_mode)]);
|
||||
if (ret == false) {
|
||||
ATA_ERROR("Can not open device.");
|
||||
}
|
||||
@ -282,7 +283,7 @@ bool airtaudio::api::CoreIos::probeDeviceOpen(uint32_t _device,
|
||||
|
||||
// Set output callback
|
||||
AURenderCallbackStruct callbackStruct;
|
||||
callbackStruct.inputProc = playbackCallback;
|
||||
callbackStruct.inputProc = &airtaudio::api::CoreIos::playbackCallback;
|
||||
callbackStruct.inputProcRefCon = this;
|
||||
status = AudioUnitSetProperty(m_private->audioUnit,
|
||||
kAudioUnitProperty_SetRenderCallback,
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
// Windows DirectSound API
|
||||
@ -126,7 +125,7 @@ airtaudio::api::Ds::~Ds() {
|
||||
if (m_coInitialized) {
|
||||
CoUninitialize(); // balanced call.
|
||||
}
|
||||
if (m_stream.state != airtaudio::state_closed) {
|
||||
if (m_state != airtaudio::state_closed) {
|
||||
closeStream();
|
||||
}
|
||||
}
|
||||
@ -497,12 +496,12 @@ bool airtaudio::api::Ds::probeDeviceOpen(uint32_t _device,
|
||||
&& !( _format == RTAUDIO_SINT8
|
||||
&& outCaps.dwFlags & DSCAPS_PRIMARY8BIT)) {
|
||||
waveFormat.wBitsPerSample = 16;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
} else {
|
||||
waveFormat.wBitsPerSample = 8;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT8;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT8;
|
||||
}
|
||||
m_stream.userFormat = _format;
|
||||
m_userFormat = _format;
|
||||
// Update wave format structure and buffer information.
|
||||
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
|
||||
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
|
||||
@ -628,23 +627,23 @@ bool airtaudio::api::Ds::probeDeviceOpen(uint32_t _device,
|
||||
deviceFormats = WAVE_FORMAT_1S08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_96S08;
|
||||
if (format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats) {
|
||||
waveFormat.wBitsPerSample = 8;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT8;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT8;
|
||||
} else { // assume 16-bit is supported
|
||||
waveFormat.wBitsPerSample = 16;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
}
|
||||
} else { // channel == 1
|
||||
deviceFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_2M08 | WAVE_FORMAT_4M08 | WAVE_FORMAT_96M08;
|
||||
if (format == RTAUDIO_SINT8 && inCaps.dwFormats & deviceFormats) {
|
||||
waveFormat.wBitsPerSample = 8;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT8;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT8;
|
||||
}
|
||||
else { // assume 16-bit is supported
|
||||
waveFormat.wBitsPerSample = 16;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
}
|
||||
}
|
||||
m_stream.userFormat = _format;
|
||||
m_userFormat = _format;
|
||||
// Update wave format structure and buffer information.
|
||||
waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8;
|
||||
waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
|
||||
@ -709,36 +708,36 @@ bool airtaudio::api::Ds::probeDeviceOpen(uint32_t _device,
|
||||
}
|
||||
// Set various stream parameters
|
||||
DsHandle *handle = 0;
|
||||
m_stream.nDeviceChannels[modeToIdTable(_mode)] = _channels + _firstChannel;
|
||||
m_stream.nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_stream.bufferSize = *_bufferSize;
|
||||
m_stream.channelOffset[modeToIdTable(_mode)] = _firstChannel;
|
||||
m_stream.deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
m_nDeviceChannels[modeToIdTable(_mode)] = _channels + _firstChannel;
|
||||
m_nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_bufferSize = *_bufferSize;
|
||||
m_channelOffset[modeToIdTable(_mode)] = _firstChannel;
|
||||
m_deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
// Set flag for buffer conversion
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_stream.nUserChannels[modeToIdTable(_mode)] != m_stream.nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_nUserChannels[modeToIdTable(_mode)] != m_nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if (m_stream.userFormat != m_stream.deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if (m_userFormat != m_deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if ( m_stream.deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_stream.nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if ( m_deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
// Allocate necessary internal buffers
|
||||
long bufferBytes = m_stream.nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_stream.userFormat);
|
||||
m_stream.userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
long bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat);
|
||||
m_userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
ATA_ERROR("error allocating user buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
bool makeBuffer = true;
|
||||
bufferBytes = m_stream.nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_stream.deviceFormat[modeToIdTable(_mode)]);
|
||||
bufferBytes = m_nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]);
|
||||
if (_mode == airtaudio::mode_input) {
|
||||
if (m_stream.mode == airtaudio::mode_output && m_stream.deviceBuffer) {
|
||||
uint64_t bytesOut = m_stream.nDeviceChannels[0] * audio::getFormatBytes(m_stream.deviceFormat[0]);
|
||||
if (m_mode == airtaudio::mode_output && m_deviceBuffer) {
|
||||
uint64_t bytesOut = m_nDeviceChannels[0] * audio::getFormatBytes(m_deviceFormat[0]);
|
||||
if (bufferBytes <= (long) bytesOut) {
|
||||
makeBuffer = false;
|
||||
}
|
||||
@ -746,18 +745,18 @@ bool airtaudio::api::Ds::probeDeviceOpen(uint32_t _device,
|
||||
}
|
||||
if (makeBuffer) {
|
||||
bufferBytes *= *_bufferSize;
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
}
|
||||
m_stream.deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.deviceBuffer == nullptr) {
|
||||
m_deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_deviceBuffer == nullptr) {
|
||||
ATA_ERROR("error allocating device buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Allocate our DsHandle structures for the stream.
|
||||
if (m_stream.apiHandle == 0) {
|
||||
if (m_apiHandle == 0) {
|
||||
handle = new DsHandle;
|
||||
if (handle == nullptr) {
|
||||
ATA_ERROR("error allocating AsioHandle memory.");
|
||||
@ -768,46 +767,46 @@ bool airtaudio::api::Ds::probeDeviceOpen(uint32_t _device,
|
||||
TRUE, // manual-reset
|
||||
FALSE, // non-signaled initially
|
||||
nullptr); // unnamed
|
||||
m_stream.apiHandle = (void *) handle;
|
||||
m_apiHandle = (void *) handle;
|
||||
} else {
|
||||
handle = (DsHandle *) m_stream.apiHandle;
|
||||
handle = (DsHandle *) m_apiHandle;
|
||||
}
|
||||
handle->id[modeToIdTable(_mode)] = ohandle;
|
||||
handle->buffer[modeToIdTable(_mode)] = bhandle;
|
||||
handle->dsBufferSize[modeToIdTable(_mode)] = dsBufferSize;
|
||||
handle->dsPointerLeadTime[modeToIdTable(_mode)] = dsPointerLeadTime;
|
||||
m_stream.device[modeToIdTable(_mode)] = _device;
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
m_device[modeToIdTable(_mode)] = _device;
|
||||
m_state = airtaudio::state_stopped;
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
&& _mode == airtaudio::mode_input) {
|
||||
// We had already set up an output stream.
|
||||
m_stream.mode = airtaudio::mode_duplex;
|
||||
m_mode = airtaudio::mode_duplex;
|
||||
} else {
|
||||
m_stream.mode = _mode;
|
||||
m_mode = _mode;
|
||||
}
|
||||
m_stream.nBuffers = nBuffers;
|
||||
m_stream.sampleRate = _sampleRate;
|
||||
m_nBuffers = nBuffers;
|
||||
m_sampleRate = _sampleRate;
|
||||
// Setup the buffer conversion information structure.
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
setConvertInfo(_mode, _firstChannel);
|
||||
}
|
||||
// Setup the callback thread.
|
||||
if (m_stream.callbackInfo.isRunning == false) {
|
||||
if (m_callbackInfo.isRunning == false) {
|
||||
unsigned threadId;
|
||||
m_stream.callbackInfo.isRunning = true;
|
||||
m_stream.callbackInfo.object = (void *) this;
|
||||
m_stream.callbackInfo.thread = _beginthreadex(nullptr,
|
||||
m_callbackInfo.isRunning = true;
|
||||
m_callbackInfo.object = (void *) this;
|
||||
m_callbackInfo.thread = _beginthreadex(nullptr,
|
||||
0,
|
||||
&callbackHandler,
|
||||
&m_stream.callbackInfo,
|
||||
&m_callbackInfo,
|
||||
0,
|
||||
&threadId);
|
||||
if (m_stream.callbackInfo.thread == 0) {
|
||||
if (m_callbackInfo.thread == 0) {
|
||||
ATA_ERROR("error creating callback thread!");
|
||||
goto error;
|
||||
}
|
||||
// Boost DS thread priority
|
||||
SetThreadPriority((HANDLE)m_stream.callbackInfo.thread, THREAD_PRIORITY_HIGHEST);
|
||||
SetThreadPriority((HANDLE)m_callbackInfo.thread, THREAD_PRIORITY_HIGHEST);
|
||||
}
|
||||
return true;
|
||||
error:
|
||||
@ -830,32 +829,32 @@ error:
|
||||
}
|
||||
CloseHandle(handle->condition);
|
||||
delete handle;
|
||||
m_stream.apiHandle = 0;
|
||||
m_apiHandle = 0;
|
||||
}
|
||||
for (int32_t i=0; i<2; i++) {
|
||||
if (m_stream.userBuffer[i]) {
|
||||
free(m_stream.userBuffer[i]);
|
||||
m_stream.userBuffer[i] = 0;
|
||||
if (m_userBuffer[i]) {
|
||||
free(m_userBuffer[i]);
|
||||
m_userBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = 0;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = 0;
|
||||
}
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_state = airtaudio::state_closed;
|
||||
return false;
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::api::Ds::closeStream() {
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("no open stream to close!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
// Stop the callback thread.
|
||||
m_stream.callbackInfo.isRunning = false;
|
||||
WaitForSingleObject((HANDLE) m_stream.callbackInfo.thread, INFINITE);
|
||||
CloseHandle((HANDLE) m_stream.callbackInfo.thread);
|
||||
DsHandle *handle = (DsHandle *) m_stream.apiHandle;
|
||||
m_callbackInfo.isRunning = false;
|
||||
WaitForSingleObject((HANDLE) m_callbackInfo.thread, INFINITE);
|
||||
CloseHandle((HANDLE) m_callbackInfo.thread);
|
||||
DsHandle *handle = (DsHandle *) m_apiHandle;
|
||||
if (handle) {
|
||||
if (handle->buffer[0]) { // the object pointer can be nullptr and valid
|
||||
LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
|
||||
@ -877,44 +876,44 @@ enum airtaudio::error airtaudio::api::Ds::closeStream() {
|
||||
}
|
||||
CloseHandle(handle->condition);
|
||||
delete handle;
|
||||
m_stream.apiHandle = 0;
|
||||
m_apiHandle = 0;
|
||||
}
|
||||
for (int32_t i=0; i<2; i++) {
|
||||
if (m_stream.userBuffer[i]) {
|
||||
free(m_stream.userBuffer[i]);
|
||||
m_stream.userBuffer[i] = 0;
|
||||
if (m_userBuffer[i]) {
|
||||
free(m_userBuffer[i]);
|
||||
m_userBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = 0;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = 0;
|
||||
}
|
||||
m_stream.mode = airtaudio::mode_unknow;
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_mode = airtaudio::mode_unknow;
|
||||
m_state = airtaudio::state_closed;
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::api::Ds::startStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
if (m_state == airtaudio::state_running) {
|
||||
ATA_ERROR("the stream is already running!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
DsHandle *handle = (DsHandle *) m_stream.apiHandle;
|
||||
DsHandle *handle = (DsHandle *) m_apiHandle;
|
||||
// Increase scheduler frequency on lesser windows (a side-effect of
|
||||
// increasing timer accuracy). On greater windows (Win2K or later),
|
||||
// this is already in effect.
|
||||
timeBeginPeriod(1);
|
||||
m_buffersRolling = false;
|
||||
m_duplexPrerollBytes = 0;
|
||||
if (m_stream.mode == airtaudio::mode_duplex) {
|
||||
if (m_mode == airtaudio::mode_duplex) {
|
||||
// 0.5 seconds of silence in airtaudio::mode_duplex mode while the devices spin up and synchronize.
|
||||
m_duplexPrerollBytes = (int) (0.5 * m_stream.sampleRate * audio::getFormatBytes(m_stream.deviceFormat[1]) * m_stream.nDeviceChannels[1]);
|
||||
m_duplexPrerollBytes = (int) (0.5 * m_sampleRate * audio::getFormatBytes(m_deviceFormat[1]) * m_nDeviceChannels[1]);
|
||||
}
|
||||
HRESULT result = 0;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
|
||||
result = buffer->Play(0, 0, DSBPLAY_LOOPING);
|
||||
if (FAILED(result)) {
|
||||
@ -922,8 +921,8 @@ enum airtaudio::error airtaudio::api::Ds::startStream() {
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
|
||||
result = buffer->Start(DSCBSTART_LOOPING);
|
||||
if (FAILED(result)) {
|
||||
@ -934,7 +933,7 @@ enum airtaudio::error airtaudio::api::Ds::startStream() {
|
||||
handle->drainCounter = 0;
|
||||
handle->internalDrain = false;
|
||||
ResetEvent(handle->condition);
|
||||
m_stream.state = airtaudio::state_running;
|
||||
m_state = airtaudio::state_running;
|
||||
unlock:
|
||||
if (FAILED(result)) {
|
||||
return airtaudio::error_systemError;
|
||||
@ -946,21 +945,21 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
HRESULT result = 0;
|
||||
LPVOID audioPtr;
|
||||
DWORD dataLen;
|
||||
DsHandle *handle = (DsHandle *) m_stream.apiHandle;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
DsHandle *handle = (DsHandle *) m_apiHandle;
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
if (handle->drainCounter == 0) {
|
||||
handle->drainCounter = 2;
|
||||
WaitForSingleObject(handle->condition, INFINITE); // block until signaled
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_state = airtaudio::state_stopped;
|
||||
// Stop the buffer and clear memory
|
||||
LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
|
||||
result = buffer->Stop();
|
||||
@ -986,12 +985,12 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() {
|
||||
// If we start playing again, we must begin at beginning of buffer.
|
||||
handle->bufferPointer[0] = 0;
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
|
||||
audioPtr = nullptr;
|
||||
dataLen = 0;
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_state = airtaudio::state_stopped;
|
||||
result = buffer->Stop();
|
||||
if (FAILED(result)) {
|
||||
ATA_ERROR("error (" << getErrorString(result) << ") stopping input buffer!");
|
||||
@ -1027,29 +1026,29 @@ enum airtaudio::error airtaudio::api::Ds::abortStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
DsHandle *handle = (DsHandle *) m_stream.apiHandle;
|
||||
DsHandle *handle = (DsHandle *) m_apiHandle;
|
||||
handle->drainCounter = 2;
|
||||
return stopStream();
|
||||
}
|
||||
|
||||
void airtaudio::api::Ds::callbackEvent() {
|
||||
if (m_stream.state == airtaudio::state_stopped || m_stream.state == airtaudio::state_stopping) {
|
||||
if (m_state == airtaudio::state_stopped || m_state == airtaudio::state_stopping) {
|
||||
Sleep(50); // sleep 50 milliseconds
|
||||
return;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("the stream is closed ... this shouldn't happen!");
|
||||
return;
|
||||
}
|
||||
CallbackInfo *info = (CallbackInfo *) &m_stream.callbackInfo;
|
||||
DsHandle *handle = (DsHandle *) m_stream.apiHandle;
|
||||
CallbackInfo *info = (CallbackInfo *) &m_callbackInfo;
|
||||
DsHandle *handle = (DsHandle *) m_apiHandle;
|
||||
// Check if we were draining the stream and signal is finished.
|
||||
if (handle->drainCounter > m_stream.nBuffers + 2) {
|
||||
m_stream.state = airtaudio::state_stopping;
|
||||
if (handle->drainCounter > m_nBuffers + 2) {
|
||||
m_state = airtaudio::state_stopping;
|
||||
if (handle->internalDrain == false) {
|
||||
SetEvent(handle->condition);
|
||||
} else {
|
||||
@ -1062,23 +1061,23 @@ void airtaudio::api::Ds::callbackEvent() {
|
||||
if (handle->drainCounter == 0) {
|
||||
double streamTime = getStreamTime();
|
||||
rtaudio::streamStatus status = 0;
|
||||
if ( m_stream.mode != airtaudio::mode_input
|
||||
if ( m_mode != airtaudio::mode_input
|
||||
&& handle->xrun[0] == true) {
|
||||
status |= RTAUDIO_airtaudio::status_underflow;
|
||||
handle->xrun[0] = false;
|
||||
}
|
||||
if ( m_stream.mode != airtaudio::mode_output
|
||||
if ( m_mode != airtaudio::mode_output
|
||||
&& handle->xrun[1] == true) {
|
||||
status |= RTAUDIO_airtaudio::mode_input_OVERFLOW;
|
||||
handle->xrun[1] = false;
|
||||
}
|
||||
int32_t cbReturnValue = info->callback(m_stream.userBuffer[0],
|
||||
m_stream.userBuffer[1],
|
||||
m_stream.bufferSize,
|
||||
int32_t cbReturnValue = info->callback(m_userBuffer[0],
|
||||
m_userBuffer[1],
|
||||
m_bufferSize,
|
||||
streamTime,
|
||||
status);
|
||||
if (cbReturnValue == 2) {
|
||||
m_stream.state = airtaudio::state_stopping;
|
||||
m_state = airtaudio::state_stopping;
|
||||
handle->drainCounter = 2;
|
||||
abortStream();
|
||||
return;
|
||||
@ -1098,7 +1097,7 @@ void airtaudio::api::Ds::callbackEvent() {
|
||||
char *buffer;
|
||||
long bufferBytes;
|
||||
if (m_buffersRolling == false) {
|
||||
if (m_stream.mode == airtaudio::mode_duplex) {
|
||||
if (m_mode == airtaudio::mode_duplex) {
|
||||
//assert(handle->dsBufferSize[0] == handle->dsBufferSize[1]);
|
||||
// It takes a while for the devices to get rolling. As a result,
|
||||
// there's no guarantee that the capture and write device pointers
|
||||
@ -1148,7 +1147,7 @@ void airtaudio::api::Ds::callbackEvent() {
|
||||
handle->bufferPointer[0] -= handle->dsBufferSize[0];
|
||||
}
|
||||
handle->bufferPointer[1] = safeReadPointer;
|
||||
} else if (m_stream.mode == airtaudio::mode_output) {
|
||||
} else if (m_mode == airtaudio::mode_output) {
|
||||
// Set the proper nextWritePosition after initial startup.
|
||||
LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
|
||||
result = dsWriteBuffer->GetCurrentPosition(¤tWritePointer, &safeWritePointer);
|
||||
@ -1163,30 +1162,30 @@ void airtaudio::api::Ds::callbackEvent() {
|
||||
}
|
||||
m_buffersRolling = true;
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
|
||||
if (handle->drainCounter > 1) { // write zeros to the output stream
|
||||
bufferBytes = m_stream.bufferSize * m_stream.nUserChannels[0];
|
||||
bufferBytes *= audio::getFormatBytes(m_stream.userFormat);
|
||||
memset(m_stream.userBuffer[0], 0, bufferBytes);
|
||||
bufferBytes = m_bufferSize * m_nUserChannels[0];
|
||||
bufferBytes *= audio::getFormatBytes(m_userFormat);
|
||||
memset(m_userBuffer[0], 0, bufferBytes);
|
||||
}
|
||||
// Setup parameters and do buffer conversion if necessary.
|
||||
if (m_stream.doConvertBuffer[0]) {
|
||||
buffer = m_stream.deviceBuffer;
|
||||
convertBuffer(buffer, m_stream.userBuffer[0], m_stream.convertInfo[0]);
|
||||
bufferBytes = m_stream.bufferSize * m_stream.nDeviceChannels[0];
|
||||
bufferBytes *= audio::getFormatBytes(m_stream.deviceFormat[0]);
|
||||
if (m_doConvertBuffer[0]) {
|
||||
buffer = m_deviceBuffer;
|
||||
convertBuffer(buffer, m_userBuffer[0], m_convertInfo[0]);
|
||||
bufferBytes = m_bufferSize * m_nDeviceChannels[0];
|
||||
bufferBytes *= audio::getFormatBytes(m_deviceFormat[0]);
|
||||
} else {
|
||||
buffer = m_stream.userBuffer[0];
|
||||
bufferBytes = m_stream.bufferSize * m_stream.nUserChannels[0];
|
||||
bufferBytes *= audio::getFormatBytes(m_stream.userFormat);
|
||||
buffer = m_userBuffer[0];
|
||||
bufferBytes = m_bufferSize * m_nUserChannels[0];
|
||||
bufferBytes *= audio::getFormatBytes(m_userFormat);
|
||||
}
|
||||
// No byte swapping necessary in DirectSound implementation.
|
||||
// Ahhh ... windoze. 16-bit data is signed but 8-bit data is
|
||||
// unsigned. So, we need to convert our signed 8-bit data here to
|
||||
// unsigned.
|
||||
if (m_stream.deviceFormat[0] == RTAUDIO_SINT8) {
|
||||
if (m_deviceFormat[0] == RTAUDIO_SINT8) {
|
||||
for (int32_t i=0; i<bufferBytes; i++) {
|
||||
buffer[i] = (unsigned char) (buffer[i] + 128);
|
||||
}
|
||||
@ -1221,7 +1220,7 @@ void airtaudio::api::Ds::callbackEvent() {
|
||||
// beyond the end of our next write region. We use the
|
||||
// Sleep() function to suspend operation until that happens.
|
||||
double millis = (endWrite - leadPointer) * 1000.0;
|
||||
millis /= (audio::getFormatBytes(m_stream.deviceFormat[0]) * m_stream.nDeviceChannels[0] * m_stream.sampleRate);
|
||||
millis /= (audio::getFormatBytes(m_deviceFormat[0]) * m_nDeviceChannels[0] * m_sampleRate);
|
||||
if (millis < 1.0) {
|
||||
millis = 1.0;
|
||||
}
|
||||
@ -1268,17 +1267,17 @@ void airtaudio::api::Ds::callbackEvent() {
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
// Setup parameters.
|
||||
if (m_stream.doConvertBuffer[1]) {
|
||||
buffer = m_stream.deviceBuffer;
|
||||
bufferBytes = m_stream.bufferSize * m_stream.nDeviceChannels[1];
|
||||
bufferBytes *= audio::getFormatBytes(m_stream.deviceFormat[1]);
|
||||
if (m_doConvertBuffer[1]) {
|
||||
buffer = m_deviceBuffer;
|
||||
bufferBytes = m_bufferSize * m_nDeviceChannels[1];
|
||||
bufferBytes *= audio::getFormatBytes(m_deviceFormat[1]);
|
||||
} else {
|
||||
buffer = m_stream.userBuffer[1];
|
||||
bufferBytes = m_stream.bufferSize * m_stream.nUserChannels[1];
|
||||
bufferBytes *= audio::getFormatBytes(m_stream.userFormat);
|
||||
buffer = m_userBuffer[1];
|
||||
bufferBytes = m_bufferSize * m_nUserChannels[1];
|
||||
bufferBytes *= audio::getFormatBytes(m_userFormat);
|
||||
}
|
||||
LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1];
|
||||
long nextReadPointer = handle->bufferPointer[1];
|
||||
@ -1306,7 +1305,7 @@ void airtaudio::api::Ds::callbackEvent() {
|
||||
// In order to minimize audible dropouts in airtaudio::mode_duplex mode, we will
|
||||
// provide a pre-roll period of 0.5 seconds in which we return
|
||||
// zeros from the read buffer while the pointers sync up.
|
||||
if (m_stream.mode == airtaudio::mode_duplex) {
|
||||
if (m_mode == airtaudio::mode_duplex) {
|
||||
if (safeReadPointer < endRead) {
|
||||
if (m_duplexPrerollBytes <= 0) {
|
||||
// Pre-roll time over. Be more agressive.
|
||||
@ -1335,10 +1334,10 @@ void airtaudio::api::Ds::callbackEvent() {
|
||||
}
|
||||
} else { // _mode == airtaudio::mode_input
|
||||
while ( safeReadPointer < endRead
|
||||
&& m_stream.callbackInfo.isRunning) {
|
||||
&& m_callbackInfo.isRunning) {
|
||||
// See comments for playback.
|
||||
double millis = (endRead - safeReadPointer) * 1000.0;
|
||||
millis /= (audio::getFormatBytes(m_stream.deviceFormat[1]) * m_stream.nDeviceChannels[1] * m_stream.sampleRate);
|
||||
millis /= (audio::getFormatBytes(m_deviceFormat[1]) * m_nDeviceChannels[1] * m_sampleRate);
|
||||
if (millis < 1.0) {
|
||||
millis = 1.0;
|
||||
}
|
||||
@ -1390,14 +1389,14 @@ void airtaudio::api::Ds::callbackEvent() {
|
||||
handle->bufferPointer[1] = nextReadPointer;
|
||||
// No byte swapping necessary in DirectSound implementation.
|
||||
// If necessary, convert 8-bit data from unsigned to signed.
|
||||
if (m_stream.deviceFormat[1] == RTAUDIO_SINT8) {
|
||||
if (m_deviceFormat[1] == RTAUDIO_SINT8) {
|
||||
for (int32_t j=0; j<bufferBytes; j++) {
|
||||
buffer[j] = (signed char) (buffer[j] - 128);
|
||||
}
|
||||
}
|
||||
// Do buffer conversion if necessary.
|
||||
if (m_stream.doConvertBuffer[1]) {
|
||||
convertBuffer(m_stream.userBuffer[1], m_stream.deviceBuffer, m_stream.convertInfo[1]);
|
||||
if (m_doConvertBuffer[1]) {
|
||||
convertBuffer(m_userBuffer[1], m_deviceBuffer, m_convertInfo[1]);
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#if !defined(__AIRTAUDIO_API_DS_H__) && defined(__WINDOWS_DS__)
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#if defined(__DUMMY__)
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#if !defined(__AIRTAUDIO_DUMMY__) && defined(__DUMMY__)
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
// must run before :
|
||||
@ -48,7 +47,7 @@ airtaudio::Api* airtaudio::api::Jack::Create() {
|
||||
// specified in the command-line, the JACK server uses default values.
|
||||
//
|
||||
// The JACK server does not have to be running when an instance of
|
||||
// RtApiJack is created, though the function getDeviceCount() will
|
||||
// airtaudio::Jack is created, though the function getDeviceCount() will
|
||||
// report 0 devices found until JACK has been started. When no
|
||||
// devices are available (i.e., the JACK server is not running), a
|
||||
// stream cannot be opened.
|
||||
@ -84,7 +83,7 @@ airtaudio::api::Jack::Jack() {
|
||||
}
|
||||
|
||||
airtaudio::api::Jack::~Jack() {
|
||||
if (m_stream.state != airtaudio::state_closed) {
|
||||
if (m_state != airtaudio::state_closed) {
|
||||
closeStream();
|
||||
}
|
||||
}
|
||||
@ -93,7 +92,7 @@ uint32_t airtaudio::api::Jack::getDeviceCount() {
|
||||
// See if we can become a jack client.
|
||||
jack_options_t options = (jack_options_t) (JackNoStartServer); //JackNullOption;
|
||||
jack_status_t *status = nullptr;
|
||||
jack_client_t *client = jack_client_open("RtApiJackCount", options, status);
|
||||
jack_client_t *client = jack_client_open("airtaudioJackCount", options, status);
|
||||
if (client == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
@ -126,7 +125,7 @@ airtaudio::DeviceInfo airtaudio::api::Jack::getDeviceInfo(uint32_t _device) {
|
||||
info.probed = false;
|
||||
jack_options_t options = (jack_options_t) (JackNoStartServer); //JackNullOption
|
||||
jack_status_t *status = nullptr;
|
||||
jack_client_t *client = jack_client_open("RtApiJackInfo", options, status);
|
||||
jack_client_t *client = jack_client_open("airtaudioJackInfo", options, status);
|
||||
if (client == nullptr) {
|
||||
ATA_ERROR("Jack server not found or connection error!");
|
||||
// TODO : airtaudio::error_warning;
|
||||
@ -211,12 +210,14 @@ airtaudio::DeviceInfo airtaudio::api::Jack::getDeviceInfo(uint32_t _device) {
|
||||
return info;
|
||||
}
|
||||
|
||||
static int32_t jackCallbackHandler(jack_nframes_t _nframes, void *_infoPointer) {
|
||||
airtaudio::CallbackInfo* info = (airtaudio::CallbackInfo*)_infoPointer;
|
||||
airtaudio::api::Jack* object = (airtaudio::api::Jack*)info->object;
|
||||
if (object->callbackEvent((uint64_t)_nframes) == false) {
|
||||
int32_t airtaudio::api::Jack::jackCallbackHandler(jack_nframes_t _nframes, void* _userData) {
|
||||
ATA_VERBOSE("Jack callback: [BEGIN] " << uint64_t(_userData));
|
||||
airtaudio::api::Jack* myClass = reinterpret_cast<airtaudio::api::Jack*>(_userData);
|
||||
if (myClass->callbackEvent((uint64_t)_nframes) == false) {
|
||||
ATA_VERBOSE("Jack callback: [END] 1");
|
||||
return 1;
|
||||
}
|
||||
ATA_VERBOSE("Jack callback: [END] 0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -224,29 +225,28 @@ static int32_t jackCallbackHandler(jack_nframes_t _nframes, void *_infoPointer)
|
||||
// server signals that it is shutting down. It is necessary to handle
|
||||
// it this way because the jackShutdown() function must return before
|
||||
// the jack_deactivate() function (in closeStream()) will return.
|
||||
static void jackCloseStream(void *_ptr) {
|
||||
airtaudio::CallbackInfo* info = (airtaudio::CallbackInfo*)_ptr;
|
||||
airtaudio::api::Jack* object = (airtaudio::api::Jack*)info->object;
|
||||
object->closeStream();
|
||||
void airtaudio::api::Jack::jackCloseStream(void* _userData) {
|
||||
airtaudio::api::Jack* myClass = reinterpret_cast<airtaudio::api::Jack*>(_userData);
|
||||
myClass->closeStream();
|
||||
}
|
||||
|
||||
static void jackShutdown(void* _infoPointer) {
|
||||
airtaudio::CallbackInfo* info = (airtaudio::CallbackInfo*)_infoPointer;
|
||||
airtaudio::api::Jack* object = (airtaudio::api::Jack*)info->object;
|
||||
void airtaudio::api::Jack::jackShutdown(void* _userData) {
|
||||
airtaudio::api::Jack* myClass = reinterpret_cast<airtaudio::api::Jack*>(_userData);
|
||||
// Check current stream state. If stopped, then we'll assume this
|
||||
// was called as a result of a call to airtaudio::api::Jack::stopStream (the
|
||||
// deactivation of a client handle causes this function to be called).
|
||||
// If not, we'll assume the Jack server is shutting down or some
|
||||
// other problem occurred and we should close the stream.
|
||||
if (object->isStreamRunning() == false) {
|
||||
if (myClass->isStreamRunning() == false) {
|
||||
return;
|
||||
}
|
||||
new std::thread(jackCloseStream, info);
|
||||
ATA_ERROR("RtApiJack: the Jack server is shutting down this client ... stream stopped and closed!!");
|
||||
new std::thread(&airtaudio::api::Jack::jackCloseStream, _userData);
|
||||
ATA_ERROR("The Jack server is shutting down this client ... stream stopped and closed!!");
|
||||
}
|
||||
|
||||
static int32_t jackXrun(void* _infoPointer) {
|
||||
JackHandle* handle = (JackHandle*)_infoPointer;
|
||||
int32_t airtaudio::api::Jack::jackXrun(void* _userData) {
|
||||
airtaudio::api::Jack* myClass = reinterpret_cast<airtaudio::api::Jack*>(_userData);
|
||||
JackHandle* handle = (JackHandle*)myClass->m_apiHandle;
|
||||
if (handle->ports[0]) {
|
||||
handle->xrun[0] = true;
|
||||
}
|
||||
@ -264,18 +264,18 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
|
||||
audio::format _format,
|
||||
uint32_t* _bufferSize,
|
||||
airtaudio::StreamOptions* _options) {
|
||||
JackHandle *handle = (JackHandle *) m_stream.apiHandle;
|
||||
JackHandle *handle = (JackHandle *) m_apiHandle;
|
||||
// Look for jack server and try to become a client (only do once per stream).
|
||||
jack_client_t *client = 0;
|
||||
if ( _mode == airtaudio::mode_output
|
||||
|| ( _mode == airtaudio::mode_input
|
||||
&& m_stream.mode != airtaudio::mode_output)) {
|
||||
&& m_mode != airtaudio::mode_output)) {
|
||||
jack_options_t jackoptions = (jack_options_t) (JackNoStartServer); //JackNullOption;
|
||||
jack_status_t *status = nullptr;
|
||||
if (_options && !_options->streamName.empty()) {
|
||||
client = jack_client_open(_options->streamName.c_str(), jackoptions, status);
|
||||
} else {
|
||||
client = jack_client_open("RtApiJack", jackoptions, status);
|
||||
client = jack_client_open("airtaudioJack", jackoptions, status);
|
||||
}
|
||||
if (client == 0) {
|
||||
ATA_ERROR("Jack server not found or connection error!");
|
||||
@ -336,7 +336,7 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
|
||||
ATA_ERROR("the requested sample rate (" << _sampleRate << ") is different than the JACK server rate (" << jackRate << ").");
|
||||
return false;
|
||||
}
|
||||
m_stream.sampleRate = jackRate;
|
||||
m_sampleRate = jackRate;
|
||||
// Get the latency of the JACK port.
|
||||
ports = jack_get_ports(client, deviceName.c_str(), nullptr, flag);
|
||||
if (ports[ _firstChannel ]) {
|
||||
@ -347,31 +347,33 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
|
||||
// get the latency range
|
||||
jack_port_get_latency_range(jack_port_by_name(client, ports[_firstChannel]), cbmode, &latrange);
|
||||
// be optimistic, use the min!
|
||||
m_stream.latency[modeToIdTable(_mode)] = latrange.min;
|
||||
//m_stream.latency[modeToIdTable(_mode)] = jack_port_get_latency(jack_port_by_name(client, ports[ _firstChannel ]));
|
||||
m_latency[modeToIdTable(_mode)] = latrange.min;
|
||||
//m_latency[modeToIdTable(_mode)] = jack_port_get_latency(jack_port_by_name(client, ports[ _firstChannel ]));
|
||||
}
|
||||
free(ports);
|
||||
// The jack server always uses 32-bit floating-point data.
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = audio::format_float;
|
||||
m_stream.userFormat = _format;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = audio::format_float;
|
||||
m_userFormat = _format;
|
||||
// Jack always uses non-interleaved buffers.
|
||||
m_stream.deviceInterleaved[modeToIdTable(_mode)] = false;
|
||||
m_deviceInterleaved[modeToIdTable(_mode)] = false;
|
||||
// Jack always provides host byte-ordered data.
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = false;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = false;
|
||||
// Get the buffer size. The buffer size and number of buffers
|
||||
// (periods) is set when the jack server is started.
|
||||
m_stream.bufferSize = (int) jack_get_buffer_size(client);
|
||||
*_bufferSize = m_stream.bufferSize;
|
||||
m_stream.nDeviceChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_stream.nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_bufferSize = (int) jack_get_buffer_size(client);
|
||||
*_bufferSize = m_bufferSize;
|
||||
m_nDeviceChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
// Set flags for buffer conversion.
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_stream.userFormat != m_stream.deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_userFormat != m_deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
ATA_CRITICAL("Can not update format ==> use RIVER lib for this ...");
|
||||
}
|
||||
if ( m_stream.deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_stream.nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if ( m_deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
ATA_ERROR("Reorder channel for the interleaving properties ...");
|
||||
//m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
// Allocate our JackHandle structure for the stream.
|
||||
if (handle == 0) {
|
||||
@ -380,26 +382,27 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
|
||||
ATA_ERROR("error allocating JackHandle memory.");
|
||||
goto error;
|
||||
}
|
||||
m_stream.apiHandle = (void *) handle;
|
||||
m_apiHandle = (void *) handle;
|
||||
handle->client = client;
|
||||
}
|
||||
handle->deviceName[modeToIdTable(_mode)] = deviceName;
|
||||
// Allocate necessary internal buffers.
|
||||
uint64_t bufferBytes;
|
||||
bufferBytes = m_stream.nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_stream.userFormat);
|
||||
m_stream.userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]);
|
||||
ATA_VERBOSE("allocate : nbChannel=" << m_nUserChannels[modeToIdTable(_mode)] << " bufferSize=" << *_bufferSize << " format=" << m_deviceFormat[modeToIdTable(_mode)] << "=" << audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]));
|
||||
m_userBuffer[modeToIdTable(_mode)].resize(bufferBytes, 0);
|
||||
if (m_userBuffer[modeToIdTable(_mode)].size() == 0) {
|
||||
ATA_ERROR("error allocating user buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
bool makeBuffer = true;
|
||||
if (_mode == airtaudio::mode_output) {
|
||||
bufferBytes = m_stream.nDeviceChannels[0] * audio::getFormatBytes(m_stream.deviceFormat[0]);
|
||||
bufferBytes = m_nDeviceChannels[0] * audio::getFormatBytes(m_deviceFormat[0]);
|
||||
} else { // _mode == airtaudio::mode_input
|
||||
bufferBytes = m_stream.nDeviceChannels[1] * audio::getFormatBytes(m_stream.deviceFormat[1]);
|
||||
if (m_stream.mode == airtaudio::mode_output && m_stream.deviceBuffer) {
|
||||
uint64_t bytesOut = m_stream.nDeviceChannels[0] * audio::getFormatBytes(m_stream.deviceFormat[0]);
|
||||
bufferBytes = m_nDeviceChannels[1] * audio::getFormatBytes(m_deviceFormat[1]);
|
||||
if (m_mode == airtaudio::mode_output && m_deviceBuffer) {
|
||||
uint64_t bytesOut = m_nDeviceChannels[0] * audio::getFormatBytes(m_deviceFormat[0]);
|
||||
if (bufferBytes < bytesOut) {
|
||||
makeBuffer = false;
|
||||
}
|
||||
@ -407,9 +410,9 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
|
||||
}
|
||||
if (makeBuffer) {
|
||||
bufferBytes *= *_bufferSize;
|
||||
if (m_stream.deviceBuffer) free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.deviceBuffer == nullptr) {
|
||||
if (m_deviceBuffer) free(m_deviceBuffer);
|
||||
m_deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_deviceBuffer == nullptr) {
|
||||
ATA_ERROR("error allocating device buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
@ -421,24 +424,23 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
|
||||
ATA_ERROR("error allocating port memory.");
|
||||
goto error;
|
||||
}
|
||||
m_stream.device[modeToIdTable(_mode)] = _device;
|
||||
m_stream.channelOffset[modeToIdTable(_mode)] = _firstChannel;
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_stream.callbackInfo.object = (void *) this;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
m_device[modeToIdTable(_mode)] = _device;
|
||||
m_channelOffset[modeToIdTable(_mode)] = _firstChannel;
|
||||
m_state = airtaudio::state_stopped;
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
&& _mode == airtaudio::mode_input) {
|
||||
// We had already set up the stream for output.
|
||||
m_stream.mode = airtaudio::mode_duplex;
|
||||
m_mode = airtaudio::mode_duplex;
|
||||
} else {
|
||||
m_stream.mode = _mode;
|
||||
jack_set_process_callback(handle->client, jackCallbackHandler, (void *) &m_stream.callbackInfo);
|
||||
jack_set_xrun_callback(handle->client, jackXrun, (void *) &handle);
|
||||
jack_on_shutdown(handle->client, jackShutdown, (void *) &m_stream.callbackInfo);
|
||||
m_mode = _mode;
|
||||
jack_set_process_callback(handle->client, &airtaudio::api::Jack::jackCallbackHandler, this);
|
||||
jack_set_xrun_callback(handle->client, &airtaudio::api::Jack::jackXrun, this);
|
||||
jack_on_shutdown(handle->client, &airtaudio::api::Jack::jackShutdown, this);
|
||||
}
|
||||
// Register our ports.
|
||||
char label[64];
|
||||
if (_mode == airtaudio::mode_output) {
|
||||
for (uint32_t i=0; i<m_stream.nUserChannels[0]; i++) {
|
||||
for (uint32_t i=0; i<m_nUserChannels[0]; i++) {
|
||||
snprintf(label, 64, "outport %d", i);
|
||||
handle->ports[0][i] = jack_port_register(handle->client,
|
||||
(const char *)label,
|
||||
@ -447,7 +449,7 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
|
||||
0);
|
||||
}
|
||||
} else {
|
||||
for (uint32_t i=0; i<m_stream.nUserChannels[1]; i++) {
|
||||
for (uint32_t i=0; i<m_nUserChannels[1]; i++) {
|
||||
snprintf(label, 64, "inport %d", i);
|
||||
handle->ports[1][i] = jack_port_register(handle->client,
|
||||
(const char *)label,
|
||||
@ -459,7 +461,7 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
|
||||
// Setup the buffer conversion information structure. We don't use
|
||||
// buffers to do channel offsets, so we override that parameter
|
||||
// here.
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
setConvertInfo(_mode, 0);
|
||||
}
|
||||
return true;
|
||||
@ -473,29 +475,26 @@ error:
|
||||
free(handle->ports[1]);
|
||||
}
|
||||
delete handle;
|
||||
m_stream.apiHandle = nullptr;
|
||||
m_apiHandle = nullptr;
|
||||
}
|
||||
for (int32_t iii=0; iii<2; ++iii) {
|
||||
if (m_stream.userBuffer[iii]) {
|
||||
free(m_stream.userBuffer[iii]);
|
||||
m_stream.userBuffer[iii] = nullptr;
|
||||
m_userBuffer[iii].clear();
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = nullptr;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::api::Jack::closeStream() {
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("no open stream to close!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
JackHandle *handle = (JackHandle *) m_stream.apiHandle;
|
||||
JackHandle *handle = (JackHandle *) m_apiHandle;
|
||||
if (handle != nullptr) {
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
if (m_state == airtaudio::state_running) {
|
||||
jack_deactivate(handle->client);
|
||||
}
|
||||
jack_client_close(handle->client);
|
||||
@ -508,20 +507,17 @@ enum airtaudio::error airtaudio::api::Jack::closeStream() {
|
||||
free(handle->ports[1]);
|
||||
}
|
||||
delete handle;
|
||||
m_stream.apiHandle = nullptr;
|
||||
m_apiHandle = nullptr;
|
||||
}
|
||||
for (int32_t i=0; i<2; i++) {
|
||||
if (m_stream.userBuffer[i]) {
|
||||
free(m_stream.userBuffer[i]);
|
||||
m_stream.userBuffer[i] = nullptr;
|
||||
m_userBuffer[i].clear();
|
||||
}
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = nullptr;
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = nullptr;
|
||||
}
|
||||
m_stream.mode = airtaudio::mode_unknow;
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_mode = airtaudio::mode_unknow;
|
||||
m_state = airtaudio::state_closed;
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
|
||||
@ -529,11 +525,11 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
if (m_state == airtaudio::state_running) {
|
||||
ATA_ERROR("the stream is already running!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
JackHandle *handle = (JackHandle *) m_stream.apiHandle;
|
||||
JackHandle *handle = (JackHandle *) m_apiHandle;
|
||||
int32_t result = jack_activate(handle->client);
|
||||
if (result) {
|
||||
ATA_ERROR("unable to activate JACK client!");
|
||||
@ -541,8 +537,8 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
|
||||
}
|
||||
const char **ports;
|
||||
// Get the list of available ports.
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
result = 1;
|
||||
ports = jack_get_ports(handle->client, handle->deviceName[0].c_str(), nullptr, JackPortIsInput);
|
||||
if (ports == nullptr) {
|
||||
@ -552,10 +548,10 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
|
||||
// Now make the port connections. Since RtAudio wasn't designed to
|
||||
// allow the user to select particular channels of a device, we'll
|
||||
// just open the first "nChannels" ports with offset.
|
||||
for (uint32_t i=0; i<m_stream.nUserChannels[0]; i++) {
|
||||
for (uint32_t i=0; i<m_nUserChannels[0]; i++) {
|
||||
result = 1;
|
||||
if (ports[ m_stream.channelOffset[0] + i ])
|
||||
result = jack_connect(handle->client, jack_port_name(handle->ports[0][i]), ports[ m_stream.channelOffset[0] + i ]);
|
||||
if (ports[ m_channelOffset[0] + i ])
|
||||
result = jack_connect(handle->client, jack_port_name(handle->ports[0][i]), ports[ m_channelOffset[0] + i ]);
|
||||
if (result) {
|
||||
free(ports);
|
||||
ATA_ERROR("error connecting output ports!");
|
||||
@ -564,8 +560,8 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
|
||||
}
|
||||
free(ports);
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
result = 1;
|
||||
ports = jack_get_ports(handle->client, handle->deviceName[1].c_str(), nullptr, JackPortIsOutput);
|
||||
if (ports == nullptr) {
|
||||
@ -573,10 +569,10 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
|
||||
goto unlock;
|
||||
}
|
||||
// Now make the port connections. See note above.
|
||||
for (uint32_t i=0; i<m_stream.nUserChannels[1]; i++) {
|
||||
for (uint32_t i=0; i<m_nUserChannels[1]; i++) {
|
||||
result = 1;
|
||||
if (ports[ m_stream.channelOffset[1] + i ]) {
|
||||
result = jack_connect(handle->client, ports[ m_stream.channelOffset[1] + i ], jack_port_name(handle->ports[1][i]));
|
||||
if (ports[ m_channelOffset[1] + i ]) {
|
||||
result = jack_connect(handle->client, ports[ m_channelOffset[1] + i ], jack_port_name(handle->ports[1][i]));
|
||||
}
|
||||
if (result) {
|
||||
free(ports);
|
||||
@ -588,7 +584,7 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
|
||||
}
|
||||
handle->drainCounter = 0;
|
||||
handle->internalDrain = false;
|
||||
m_stream.state = airtaudio::state_running;
|
||||
m_state = airtaudio::state_running;
|
||||
unlock:
|
||||
if (result == 0) {
|
||||
return airtaudio::error_none;
|
||||
@ -600,21 +596,21 @@ enum airtaudio::error airtaudio::api::Jack::stopStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
JackHandle *handle = (JackHandle *) m_stream.apiHandle;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
JackHandle *handle = (JackHandle *) m_apiHandle;
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
if (handle->drainCounter == 0) {
|
||||
handle->drainCounter = 2;
|
||||
std::unique_lock<std::mutex> lck(m_stream.mutex);
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
handle->condition.wait(lck);
|
||||
}
|
||||
}
|
||||
jack_deactivate(handle->client);
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_state = airtaudio::state_stopped;
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
|
||||
@ -622,11 +618,11 @@ enum airtaudio::error airtaudio::api::Jack::abortStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
JackHandle *handle = (JackHandle *) m_stream.apiHandle;
|
||||
JackHandle *handle = (JackHandle *) m_apiHandle;
|
||||
handle->drainCounter = 2;
|
||||
return stopStream();
|
||||
}
|
||||
@ -636,32 +632,30 @@ enum airtaudio::error airtaudio::api::Jack::abortStream() {
|
||||
// aborted. It is necessary to handle it this way because the
|
||||
// callbackEvent() function must return before the jack_deactivate()
|
||||
// function will return.
|
||||
static void jackStopStream(void *_ptr) {
|
||||
airtaudio::CallbackInfo *info = (airtaudio::CallbackInfo *) _ptr;
|
||||
airtaudio::api::Jack *object = (airtaudio::api::Jack *) info->object;
|
||||
object->stopStream();
|
||||
static void jackStopStream(void* _userData) {
|
||||
airtaudio::api::Jack* myClass = reinterpret_cast<airtaudio::api::Jack*>(_userData);
|
||||
myClass->stopStream();
|
||||
}
|
||||
|
||||
bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) {
|
||||
if ( m_stream.state == airtaudio::state_stopped
|
||||
|| m_stream.state == airtaudio::state_stopping) {
|
||||
if ( m_state == airtaudio::state_stopped
|
||||
|| m_state == airtaudio::state_stopping) {
|
||||
return true;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
ATA_ERROR("RtApiCore::callbackEvent(): the stream is closed ... this shouldn't happen!");
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("the stream is closed ... this shouldn't happen!");
|
||||
return false;
|
||||
}
|
||||
if (m_stream.bufferSize != _nframes) {
|
||||
ATA_ERROR("RtApiCore::callbackEvent(): the JACK buffer size has changed ... cannot process!");
|
||||
if (m_bufferSize != _nframes) {
|
||||
ATA_ERROR("the JACK buffer size has changed ... cannot process!");
|
||||
return false;
|
||||
}
|
||||
CallbackInfo *info = (CallbackInfo *) &m_stream.callbackInfo;
|
||||
JackHandle *handle = (JackHandle *) m_stream.apiHandle;
|
||||
JackHandle *handle = (JackHandle *) m_apiHandle;
|
||||
// Check if we were draining the stream and signal is finished.
|
||||
if (handle->drainCounter > 3) {
|
||||
m_stream.state = airtaudio::state_stopping;
|
||||
m_state = airtaudio::state_stopping;
|
||||
if (handle->internalDrain == true) {
|
||||
new std::thread(jackStopStream, info);
|
||||
new std::thread(jackStopStream, this);
|
||||
} else {
|
||||
handle->condition.notify_one();
|
||||
}
|
||||
@ -671,23 +665,23 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) {
|
||||
if (handle->drainCounter == 0) {
|
||||
double streamTime = getStreamTime();
|
||||
enum airtaudio::status status = airtaudio::status_ok;
|
||||
if (m_stream.mode != airtaudio::mode_input && handle->xrun[0] == true) {
|
||||
if (m_mode != airtaudio::mode_input && handle->xrun[0] == true) {
|
||||
status = airtaudio::status_underflow;
|
||||
handle->xrun[0] = false;
|
||||
}
|
||||
if (m_stream.mode != airtaudio::mode_output && handle->xrun[1] == true) {
|
||||
if (m_mode != airtaudio::mode_output && handle->xrun[1] == true) {
|
||||
status = airtaudio::status_overflow;
|
||||
handle->xrun[1] = false;
|
||||
}
|
||||
int32_t cbReturnValue = info->callback(m_stream.userBuffer[0],
|
||||
m_stream.userBuffer[1],
|
||||
m_stream.bufferSize,
|
||||
int32_t cbReturnValue = m_callbackInfo.callback(&m_userBuffer[0][0],
|
||||
&m_userBuffer[1][0],
|
||||
m_bufferSize,
|
||||
streamTime,
|
||||
status);
|
||||
if (cbReturnValue == 2) {
|
||||
m_stream.state = airtaudio::state_stopping;
|
||||
m_state = airtaudio::state_stopping;
|
||||
handle->drainCounter = 2;
|
||||
new std::thread(jackStopStream, info);
|
||||
new std::thread(jackStopStream, this);
|
||||
return true;
|
||||
}
|
||||
else if (cbReturnValue == 1) {
|
||||
@ -697,22 +691,23 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) {
|
||||
}
|
||||
jack_default_audio_sample_t *jackbuffer;
|
||||
uint64_t bufferBytes = _nframes * sizeof(jack_default_audio_sample_t);
|
||||
if (m_stream.mode == airtaudio::mode_output || m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
if (handle->drainCounter > 1) { // write zeros to the output stream
|
||||
for (uint32_t i=0; i<m_stream.nDeviceChannels[0]; i++) {
|
||||
for (uint32_t i=0; i<m_nDeviceChannels[0]; i++) {
|
||||
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[0][i], (jack_nframes_t) _nframes);
|
||||
memset(jackbuffer, 0, bufferBytes);
|
||||
}
|
||||
} else if (m_stream.doConvertBuffer[0]) {
|
||||
convertBuffer(m_stream.deviceBuffer, m_stream.userBuffer[0], m_stream.convertInfo[0]);
|
||||
for (uint32_t i=0; i<m_stream.nDeviceChannels[0]; i++) {
|
||||
} else if (m_doConvertBuffer[0]) {
|
||||
convertBuffer(m_deviceBuffer, &m_userBuffer[0][0], m_convertInfo[0]);
|
||||
for (uint32_t i=0; i<m_nDeviceChannels[0]; i++) {
|
||||
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[0][i], (jack_nframes_t) _nframes);
|
||||
memcpy(jackbuffer, &m_stream.deviceBuffer[i*bufferBytes], bufferBytes);
|
||||
memcpy(jackbuffer, &m_deviceBuffer[i*bufferBytes], bufferBytes);
|
||||
}
|
||||
} else { // no buffer conversion
|
||||
for (uint32_t i=0; i<m_stream.nUserChannels[0]; i++) {
|
||||
for (uint32_t i=0; i<m_nUserChannels[0]; i++) {
|
||||
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[0][i], (jack_nframes_t) _nframes);
|
||||
memcpy(jackbuffer, &m_stream.userBuffer[0][i*bufferBytes], bufferBytes);
|
||||
memcpy(jackbuffer, &m_userBuffer[0][i*bufferBytes], bufferBytes);
|
||||
}
|
||||
}
|
||||
if (handle->drainCounter) {
|
||||
@ -720,19 +715,19 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) {
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if (m_stream.doConvertBuffer[1]) {
|
||||
for (uint32_t i=0; i<m_stream.nDeviceChannels[1]; i++) {
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
if (m_doConvertBuffer[1]) {
|
||||
for (uint32_t i=0; i<m_nDeviceChannels[1]; i++) {
|
||||
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[1][i], (jack_nframes_t) _nframes);
|
||||
memcpy(&m_stream.deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes);
|
||||
memcpy(&m_deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes);
|
||||
}
|
||||
convertBuffer(m_stream.userBuffer[1], m_stream.deviceBuffer, m_stream.convertInfo[1]);
|
||||
convertBuffer(&m_userBuffer[1][0], m_deviceBuffer, m_convertInfo[1]);
|
||||
} else {
|
||||
// no buffer conversion
|
||||
for (uint32_t i=0; i<m_stream.nUserChannels[1]; i++) {
|
||||
for (uint32_t i=0; i<m_nUserChannels[1]; i++) {
|
||||
jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(handle->ports[1][i], (jack_nframes_t) _nframes);
|
||||
memcpy(&m_stream.userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes);
|
||||
memcpy(&m_userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,14 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#if !defined(__AIRTAUDIO_API_JACK_H__) && defined(__UNIX_JACK__)
|
||||
#define __AIRTAUDIO_API_JACK_H__
|
||||
|
||||
#include <jack/jack.h>
|
||||
namespace airtaudio {
|
||||
namespace api {
|
||||
class Jack: public airtaudio::Api {
|
||||
@ -32,6 +32,11 @@ namespace airtaudio {
|
||||
// which is not a member of RtAudio. External use of this function
|
||||
// will most likely produce highly undesireable results!
|
||||
bool callbackEvent(uint64_t _nframes);
|
||||
private:
|
||||
static int32_t jackXrun(void* _userData);
|
||||
static void jackCloseStream(void* _userData);
|
||||
static void jackShutdown(void* _userData);
|
||||
static int32_t jackCallbackHandler(jack_nframes_t _nframes, void* _userData);
|
||||
private:
|
||||
bool probeDeviceOpen(uint32_t _device,
|
||||
airtaudio::mode _mode,
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
|
||||
@ -49,7 +48,7 @@ airtaudio::api::Oss::Oss() {
|
||||
}
|
||||
|
||||
airtaudio::api::Oss::~Oss() {
|
||||
if (m_stream.state != airtaudio::state_closed) {
|
||||
if (m_state != airtaudio::state_closed) {
|
||||
closeStream();
|
||||
}
|
||||
}
|
||||
@ -227,12 +226,12 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
|
||||
return false;
|
||||
}
|
||||
int32_t flags = 0;
|
||||
OssHandle *handle = (OssHandle *) m_stream.apiHandle;
|
||||
OssHandle *handle = (OssHandle *) m_apiHandle;
|
||||
if (_mode == airtaudio::mode_output) {
|
||||
flags |= O_WRONLY;
|
||||
} else { // _mode == airtaudio::mode_input
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
&& m_stream.device[0] == _device) {
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
&& m_device[0] == _device) {
|
||||
// We just set the same device for playback ... close and reopen for duplex (OSS only).
|
||||
close(handle->id[0]);
|
||||
handle->id[0] = 0;
|
||||
@ -241,7 +240,7 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
|
||||
return false;
|
||||
}
|
||||
// Check that the number previously set channels is the same.
|
||||
if (m_stream.nUserChannels[0] != _channels) {
|
||||
if (m_nUserChannels[0] != _channels) {
|
||||
ATA_ERROR("input/output channels must be equal for OSS duplex device (" << ainfo.name << ").");
|
||||
return false;
|
||||
}
|
||||
@ -278,7 +277,7 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
|
||||
}
|
||||
*/
|
||||
// Check the device channel support.
|
||||
m_stream.nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
if (ainfo.max_channels < (int)(_channels + _firstChannel)) {
|
||||
close(fd);
|
||||
ATA_ERROR("the device (" << ainfo.name << ") does not support requested channel parameters.");
|
||||
@ -293,7 +292,7 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
|
||||
ATA_ERROR("error setting channel parameters on device (" << ainfo.name << ").");
|
||||
return false;
|
||||
}
|
||||
m_stream.nDeviceChannels[modeToIdTable(_mode)] = deviceChannels;
|
||||
m_nDeviceChannels[modeToIdTable(_mode)] = deviceChannels;
|
||||
// Get the data format mask
|
||||
int32_t mask;
|
||||
result = ioctl(fd, SNDCTL_DSP_GETFMTS, &mask);
|
||||
@ -303,71 +302,71 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
|
||||
return false;
|
||||
}
|
||||
// Determine how to set the device format.
|
||||
m_stream.userFormat = _format;
|
||||
m_userFormat = _format;
|
||||
int32_t deviceFormat = -1;
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = false;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = false;
|
||||
if (_format == RTAUDIO_SINT8) {
|
||||
if (mask & AFMT_S8) {
|
||||
deviceFormat = AFMT_S8;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT8;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT8;
|
||||
}
|
||||
} else if (_format == RTAUDIO_SINT16) {
|
||||
if (mask & AFMT_S16_NE) {
|
||||
deviceFormat = AFMT_S16_NE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
} else if (mask & AFMT_S16_OE) {
|
||||
deviceFormat = AFMT_S16_OE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
} else if (_format == RTAUDIO_SINT24) {
|
||||
if (mask & AFMT_S24_NE) {
|
||||
deviceFormat = AFMT_S24_NE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT24;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT24;
|
||||
} else if (mask & AFMT_S24_OE) {
|
||||
deviceFormat = AFMT_S24_OE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT24;
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT24;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
} else if (_format == RTAUDIO_SINT32) {
|
||||
if (mask & AFMT_S32_NE) {
|
||||
deviceFormat = AFMT_S32_NE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT32;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT32;
|
||||
} else if (mask & AFMT_S32_OE) {
|
||||
deviceFormat = AFMT_S32_OE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT32;
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT32;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
}
|
||||
if (deviceFormat == -1) {
|
||||
// The user requested format is not natively supported by the device.
|
||||
if (mask & AFMT_S16_NE) {
|
||||
deviceFormat = AFMT_S16_NE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
} else if (mask & AFMT_S32_NE) {
|
||||
deviceFormat = AFMT_S32_NE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT32;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT32;
|
||||
} else if (mask & AFMT_S24_NE) {
|
||||
deviceFormat = AFMT_S24_NE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT24;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT24;
|
||||
} else if (mask & AFMT_S16_OE) {
|
||||
deviceFormat = AFMT_S16_OE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT16;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
} else if (mask & AFMT_S32_OE) {
|
||||
deviceFormat = AFMT_S32_OE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT32;
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT32;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
} else if (mask & AFMT_S24_OE) {
|
||||
deviceFormat = AFMT_S24_OE;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT24;
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = true;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT24;
|
||||
m_doByteSwap[modeToIdTable(_mode)] = true;
|
||||
} else if (mask & AFMT_S8) {
|
||||
deviceFormat = AFMT_S8;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT8;
|
||||
m_deviceFormat[modeToIdTable(_mode)] = RTAUDIO_SINT8;
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceFormat[modeToIdTable(_mode)] == 0) {
|
||||
if (m_deviceFormat[modeToIdTable(_mode)] == 0) {
|
||||
// This really shouldn't happen ...
|
||||
close(fd);
|
||||
ATA_ERROR("device (" << ainfo.name << ") data format not supported by RtAudio.");
|
||||
@ -389,7 +388,7 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
|
||||
// bytes) is given as 2^SSSS and the number of buffers as 2^MMMM.
|
||||
// We'll check the actual value used near the end of the setup
|
||||
// procedure.
|
||||
int32_t ossBufferBytes = *_bufferSize * audio::getFormatBytes(m_stream.deviceFormat[modeToIdTable(_mode)]) * deviceChannels;
|
||||
int32_t ossBufferBytes = *_bufferSize * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]) * deviceChannels;
|
||||
if (ossBufferBytes < 16) {
|
||||
ossBufferBytes = 16;
|
||||
}
|
||||
@ -411,10 +410,10 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
|
||||
ATA_ERROR("error setting buffer size on device (" << ainfo.name << ").");
|
||||
return false;
|
||||
}
|
||||
m_stream.nBuffers = buffers;
|
||||
m_nBuffers = buffers;
|
||||
// Save buffer size (in sample frames).
|
||||
*_bufferSize = ossBufferBytes / (audio::getFormatBytes(m_stream.deviceFormat[modeToIdTable(_mode)]) * deviceChannels);
|
||||
m_stream.bufferSize = *_bufferSize;
|
||||
*_bufferSize = ossBufferBytes / (audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]) * deviceChannels);
|
||||
m_bufferSize = *_bufferSize;
|
||||
// Set the sample rate.
|
||||
int32_t srate = _sampleRate;
|
||||
result = ioctl(fd, SNDCTL_DSP_SPEED, &srate);
|
||||
@ -429,55 +428,55 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
|
||||
ATA_ERROR("device (" << ainfo.name << ") does not support sample rate (" << _sampleRate << ").");
|
||||
return false;
|
||||
}
|
||||
m_stream.sampleRate = _sampleRate;
|
||||
m_sampleRate = _sampleRate;
|
||||
if ( _mode == airtaudio::mode_input
|
||||
&& m_stream._mode == airtaudio::mode_output
|
||||
&& m_stream.device[0] == _device) {
|
||||
&& m__mode == airtaudio::mode_output
|
||||
&& m_device[0] == _device) {
|
||||
// We're doing duplex setup here.
|
||||
m_stream.deviceFormat[0] = m_stream.deviceFormat[1];
|
||||
m_stream.nDeviceChannels[0] = deviceChannels;
|
||||
m_deviceFormat[0] = m_deviceFormat[1];
|
||||
m_nDeviceChannels[0] = deviceChannels;
|
||||
}
|
||||
// Set interleaving parameters.
|
||||
m_stream.deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
m_deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
// Set flags for buffer conversion
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_stream.userFormat != m_stream.deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
if (m_userFormat != m_deviceFormat[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if (m_stream.nUserChannels[modeToIdTable(_mode)] < m_stream.nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if (m_nUserChannels[modeToIdTable(_mode)] < m_nDeviceChannels[modeToIdTable(_mode)]) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
if ( m_stream.deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_stream.nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
if ( m_deviceInterleaved[modeToIdTable(_mode)] == false
|
||||
&& m_nUserChannels[modeToIdTable(_mode)] > 1) {
|
||||
m_doConvertBuffer[modeToIdTable(_mode)] = true;
|
||||
}
|
||||
// Allocate the stream handles if necessary and then save.
|
||||
if (m_stream.apiHandle == 0) {
|
||||
if (m_apiHandle == 0) {
|
||||
handle = new OssHandle;
|
||||
if handle == nullptr) {
|
||||
ATA_ERROR("error allocating OssHandle memory.");
|
||||
goto error;
|
||||
}
|
||||
m_stream.apiHandle = (void *) handle;
|
||||
m_apiHandle = (void *) handle;
|
||||
} else {
|
||||
handle = (OssHandle *) m_stream.apiHandle;
|
||||
handle = (OssHandle *) m_apiHandle;
|
||||
}
|
||||
handle->id[modeToIdTable(_mode)] = fd;
|
||||
// Allocate necessary internal buffers.
|
||||
uint64_t bufferBytes;
|
||||
bufferBytes = m_stream.nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_stream.userFormat);
|
||||
m_stream.userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat);
|
||||
m_userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
ATA_ERROR("error allocating user buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
bool makeBuffer = true;
|
||||
bufferBytes = m_stream.nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_stream.deviceFormat[modeToIdTable(_mode)]);
|
||||
bufferBytes = m_nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]);
|
||||
if (_mode == airtaudio::mode_input) {
|
||||
if ( m_stream._mode == airtaudio::mode_output
|
||||
&& m_stream.deviceBuffer) {
|
||||
uint64_t bytesOut = m_stream.nDeviceChannels[0] * audio::getFormatBytes(m_stream.deviceFormat[0]);
|
||||
if ( m__mode == airtaudio::mode_output
|
||||
&& m_deviceBuffer) {
|
||||
uint64_t bytesOut = m_nDeviceChannels[0] * audio::getFormatBytes(m_deviceFormat[0]);
|
||||
if (bufferBytes <= bytesOut) {
|
||||
makeBuffer = false;
|
||||
}
|
||||
@ -485,37 +484,37 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
|
||||
}
|
||||
if (makeBuffer) {
|
||||
bufferBytes *= *_bufferSize;
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
}
|
||||
m_stream.deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.deviceBuffer == nullptr) {
|
||||
m_deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_deviceBuffer == nullptr) {
|
||||
ATA_ERROR("error allocating device buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_stream.device[modeToIdTable(_mode)] = _device;
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_device[modeToIdTable(_mode)] = _device;
|
||||
m_state = airtaudio::state_stopped;
|
||||
// Setup the buffer conversion information structure.
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
setConvertInfo(_mode, _firstChannel);
|
||||
}
|
||||
// Setup thread if necessary.
|
||||
if (m_stream.mode == airtaudio::mode_output && _mode == airtaudio::mode_input) {
|
||||
if (m_mode == airtaudio::mode_output && _mode == airtaudio::mode_input) {
|
||||
// We had already set up an output stream.
|
||||
m_stream.mode = airtaudio::mode_duplex;
|
||||
if (m_stream.device[0] == _device) {
|
||||
m_mode = airtaudio::mode_duplex;
|
||||
if (m_device[0] == _device) {
|
||||
handle->id[0] = fd;
|
||||
}
|
||||
} else {
|
||||
m_stream.mode = _mode;
|
||||
m_mode = _mode;
|
||||
// Setup callback thread.
|
||||
m_stream.callbackInfo.object = (void *) this;
|
||||
m_stream.callbackInfo.isRunning = true;
|
||||
m_stream.callbackInfo.thread = new std::thread(ossCallbackHandler, &m_stream.callbackInfo);
|
||||
if (m_stream.callbackInfo.thread == nullptr) {
|
||||
m_stream.callbackInfo.isRunning = false;
|
||||
m_callbackInfo.object = (void *) this;
|
||||
m_callbackInfo.isRunning = true;
|
||||
m_callbackInfo.thread = new std::thread(ossCallbackHandler, &m_callbackInfo);
|
||||
if (m_callbackInfo.thread == nullptr) {
|
||||
m_callbackInfo.isRunning = false;
|
||||
ATA_ERROR("creating callback thread!");
|
||||
goto error;
|
||||
}
|
||||
@ -530,41 +529,41 @@ error:
|
||||
close(handle->id[1]);
|
||||
}
|
||||
delete handle;
|
||||
m_stream.apiHandle = 0;
|
||||
m_apiHandle = 0;
|
||||
}
|
||||
for (int32_t i=0; i<2; i++) {
|
||||
if (m_stream.userBuffer[i]) {
|
||||
free(m_stream.userBuffer[i]);
|
||||
m_stream.userBuffer[i] = 0;
|
||||
if (m_userBuffer[i]) {
|
||||
free(m_userBuffer[i]);
|
||||
m_userBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = 0;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::api::Oss::closeStream() {
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("no open stream to close!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
OssHandle *handle = (OssHandle *) m_stream.apiHandle;
|
||||
m_stream.callbackInfo.isRunning = false;
|
||||
m_stream.mutex.lock();
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
OssHandle *handle = (OssHandle *) m_apiHandle;
|
||||
m_callbackInfo.isRunning = false;
|
||||
m_mutex.lock();
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
handle->runnable.notify_one();
|
||||
}
|
||||
m_stream.mutex.unlock();
|
||||
m_stream.callbackInfo.thread->join();
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
if (m_stream.mode == airtaudio::mode_output || m_stream.mode == airtaudio::mode_duplex) {
|
||||
m_mutex.unlock();
|
||||
m_callbackInfo.thread->join();
|
||||
if (m_state == airtaudio::state_running) {
|
||||
if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) {
|
||||
ioctl(handle->id[0], SNDCTL_DSP_HALT, 0);
|
||||
} else {
|
||||
ioctl(handle->id[1], SNDCTL_DSP_HALT, 0);
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_state = airtaudio::state_stopped;
|
||||
}
|
||||
if (handle) {
|
||||
if (handle->id[0]) {
|
||||
@ -574,20 +573,20 @@ enum airtaudio::error airtaudio::api::Oss::closeStream() {
|
||||
close(handle->id[1]);
|
||||
}
|
||||
delete handle;
|
||||
m_stream.apiHandle = 0;
|
||||
m_apiHandle = 0;
|
||||
}
|
||||
for (int32_t i=0; i<2; i++) {
|
||||
if (m_stream.userBuffer[i]) {
|
||||
free(m_stream.userBuffer[i]);
|
||||
m_stream.userBuffer[i] = 0;
|
||||
if (m_userBuffer[i]) {
|
||||
free(m_userBuffer[i]);
|
||||
m_userBuffer[i] = 0;
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = 0;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = 0;
|
||||
}
|
||||
m_stream.mode = airtaudio::mode_unknow;
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_mode = airtaudio::mode_unknow;
|
||||
m_state = airtaudio::state_closed;
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
|
||||
@ -595,16 +594,16 @@ enum airtaudio::error airtaudio::api::Oss::startStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
if (m_state == airtaudio::state_running) {
|
||||
ATA_ERROR("the stream is already running!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
m_stream.mutex.lock();
|
||||
m_stream.state = airtaudio::state_running;
|
||||
m_mutex.lock();
|
||||
m_state = airtaudio::state_running;
|
||||
// No need to do anything else here ... OSS automatically starts
|
||||
// when fed samples.
|
||||
m_stream.mutex.unlock();
|
||||
OssHandle *handle = (OssHandle *) m_stream.apiHandle;
|
||||
m_mutex.unlock();
|
||||
OssHandle *handle = (OssHandle *) m_apiHandle;
|
||||
handle->runnable.notify_one();
|
||||
}
|
||||
|
||||
@ -612,35 +611,35 @@ enum airtaudio::error airtaudio::api::Oss::stopStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return;
|
||||
}
|
||||
m_stream.mutex.lock();
|
||||
m_mutex.lock();
|
||||
// The state might change while waiting on a mutex.
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
m_stream.mutex.unlock();
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
m_mutex.unlock();
|
||||
return;
|
||||
}
|
||||
int32_t result = 0;
|
||||
OssHandle *handle = (OssHandle *) m_stream.apiHandle;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
OssHandle *handle = (OssHandle *) m_apiHandle;
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
// Flush the output with zeros a few times.
|
||||
char *buffer;
|
||||
int32_t samples;
|
||||
audio::format format;
|
||||
if (m_stream.doConvertBuffer[0]) {
|
||||
buffer = m_stream.deviceBuffer;
|
||||
samples = m_stream.bufferSize * m_stream.nDeviceChannels[0];
|
||||
format = m_stream.deviceFormat[0];
|
||||
if (m_doConvertBuffer[0]) {
|
||||
buffer = m_deviceBuffer;
|
||||
samples = m_bufferSize * m_nDeviceChannels[0];
|
||||
format = m_deviceFormat[0];
|
||||
} else {
|
||||
buffer = m_stream.userBuffer[0];
|
||||
samples = m_stream.bufferSize * m_stream.nUserChannels[0];
|
||||
format = m_stream.userFormat;
|
||||
buffer = m_userBuffer[0];
|
||||
samples = m_bufferSize * m_nUserChannels[0];
|
||||
format = m_userFormat;
|
||||
}
|
||||
memset(buffer, 0, samples * audio::getFormatBytes(format));
|
||||
for (uint32_t i=0; i<m_stream.nBuffers+1; i++) {
|
||||
for (uint32_t i=0; i<m_nBuffers+1; i++) {
|
||||
result = write(handle->id[0], buffer, samples * audio::getFormatBytes(format));
|
||||
if (result == -1) {
|
||||
ATA_ERROR("audio write error.");
|
||||
@ -649,23 +648,23 @@ enum airtaudio::error airtaudio::api::Oss::stopStream() {
|
||||
}
|
||||
result = ioctl(handle->id[0], SNDCTL_DSP_HALT, 0);
|
||||
if (result == -1) {
|
||||
ATA_ERROR("system error stopping callback procedure on device (" << m_stream.device[0] << ").");
|
||||
ATA_ERROR("system error stopping callback procedure on device (" << m_device[0] << ").");
|
||||
goto unlock;
|
||||
}
|
||||
handle->triggered = false;
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| ( m_stream.mode == airtaudio::mode_duplex
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| ( m_mode == airtaudio::mode_duplex
|
||||
&& handle->id[0] != handle->id[1])) {
|
||||
result = ioctl(handle->id[1], SNDCTL_DSP_HALT, 0);
|
||||
if (result == -1) {
|
||||
ATA_ERROR("system error stopping input callback procedure on device (" << m_stream.device[0] << ").");
|
||||
ATA_ERROR("system error stopping input callback procedure on device (" << m_device[0] << ").");
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_stream.mutex.unlock();
|
||||
m_state = airtaudio::state_stopped;
|
||||
m_mutex.unlock();
|
||||
if (result != -1) {
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
@ -676,36 +675,36 @@ enum airtaudio::error airtaudio::api::Oss::abortStream() {
|
||||
if (verifyStream() != airtaudio::error_none) {
|
||||
return airtaudio::error_fail;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
m_stream.mutex.lock();
|
||||
m_mutex.lock();
|
||||
// The state might change while waiting on a mutex.
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
m_stream.mutex.unlock();
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
m_mutex.unlock();
|
||||
return;
|
||||
}
|
||||
int32_t result = 0;
|
||||
OssHandle *handle = (OssHandle *) m_stream.apiHandle;
|
||||
if (m_stream.mode == airtaudio::mode_output || m_stream.mode == airtaudio::mode_duplex) {
|
||||
OssHandle *handle = (OssHandle *) m_apiHandle;
|
||||
if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) {
|
||||
result = ioctl(handle->id[0], SNDCTL_DSP_HALT, 0);
|
||||
if (result == -1) {
|
||||
ATA_ERROR("system error stopping callback procedure on device (" << m_stream.device[0] << ").");
|
||||
ATA_ERROR("system error stopping callback procedure on device (" << m_device[0] << ").");
|
||||
goto unlock;
|
||||
}
|
||||
handle->triggered = false;
|
||||
}
|
||||
if (m_stream.mode == airtaudio::mode_input || (m_stream.mode == airtaudio::mode_duplex && handle->id[0] != handle->id[1])) {
|
||||
if (m_mode == airtaudio::mode_input || (m_mode == airtaudio::mode_duplex && handle->id[0] != handle->id[1])) {
|
||||
result = ioctl(handle->id[1], SNDCTL_DSP_HALT, 0);
|
||||
if (result == -1) {
|
||||
ATA_ERROR("system error stopping input callback procedure on device (" << m_stream.device[0] << ").");
|
||||
ATA_ERROR("system error stopping input callback procedure on device (" << m_device[0] << ").");
|
||||
goto unlock;
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_stream.mutex.unlock();
|
||||
m_state = airtaudio::state_stopped;
|
||||
m_mutex.unlock();
|
||||
if (result != -1) {
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
@ -713,15 +712,15 @@ unlock:
|
||||
}
|
||||
|
||||
void airtaudio::api::Oss::callbackEvent() {
|
||||
OssHandle *handle = (OssHandle *) m_stream.apiHandle;
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
std::unique_lock<std::mutex> lck(m_stream.mutex);
|
||||
OssHandle *handle = (OssHandle *) m_apiHandle;
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
handle->runnable.wait(lck);
|
||||
if (m_stream.state != airtaudio::state_running) {
|
||||
if (m_state != airtaudio::state_running) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("the stream is closed ... this shouldn't happen!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
@ -729,52 +728,52 @@ void airtaudio::api::Oss::callbackEvent() {
|
||||
int32_t doStopStream = 0;
|
||||
double streamTime = getStreamTime();
|
||||
rtaudio::streamStatus status = 0;
|
||||
if ( m_stream.mode != airtaudio::mode_input
|
||||
if ( m_mode != airtaudio::mode_input
|
||||
&& handle->xrun[0] == true) {
|
||||
status |= RTAUDIO_airtaudio::status_underflow;
|
||||
handle->xrun[0] = false;
|
||||
}
|
||||
if ( m_stream.mode != airtaudio::mode_output
|
||||
if ( m_mode != airtaudio::mode_output
|
||||
&& handle->xrun[1] == true) {
|
||||
status |= RTAUDIO_airtaudio::mode_input_OVERFLOW;
|
||||
handle->xrun[1] = false;
|
||||
}
|
||||
doStopStream = m_stream.callbackInfo.callback(m_stream.userBuffer[0],
|
||||
m_stream.userBuffer[1],
|
||||
m_stream.bufferSize,
|
||||
doStopStream = m_callbackInfo.callback(m_userBuffer[0],
|
||||
m_userBuffer[1],
|
||||
m_bufferSize,
|
||||
streamTime,
|
||||
status);
|
||||
if (doStopStream == 2) {
|
||||
this->abortStream();
|
||||
return;
|
||||
}
|
||||
m_stream.mutex.lock();
|
||||
m_mutex.lock();
|
||||
// The state might change while waiting on a mutex.
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
goto unlock;
|
||||
}
|
||||
int32_t result;
|
||||
char *buffer;
|
||||
int32_t samples;
|
||||
audio::format format;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
// Setup parameters and do buffer conversion if necessary.
|
||||
if (m_stream.doConvertBuffer[0]) {
|
||||
buffer = m_stream.deviceBuffer;
|
||||
convertBuffer(buffer, m_stream.userBuffer[0], m_stream.convertInfo[0]);
|
||||
samples = m_stream.bufferSize * m_stream.nDeviceChannels[0];
|
||||
format = m_stream.deviceFormat[0];
|
||||
if (m_doConvertBuffer[0]) {
|
||||
buffer = m_deviceBuffer;
|
||||
convertBuffer(buffer, m_userBuffer[0], m_convertInfo[0]);
|
||||
samples = m_bufferSize * m_nDeviceChannels[0];
|
||||
format = m_deviceFormat[0];
|
||||
} else {
|
||||
buffer = m_stream.userBuffer[0];
|
||||
samples = m_stream.bufferSize * m_stream.nUserChannels[0];
|
||||
format = m_stream.userFormat;
|
||||
buffer = m_userBuffer[0];
|
||||
samples = m_bufferSize * m_nUserChannels[0];
|
||||
format = m_userFormat;
|
||||
}
|
||||
// Do byte swapping if necessary.
|
||||
if (m_stream.doByteSwap[0]) {
|
||||
if (m_doByteSwap[0]) {
|
||||
byteSwapBuffer(buffer, samples, format);
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_duplex
|
||||
if ( m_mode == airtaudio::mode_duplex
|
||||
&& handle->triggered == false) {
|
||||
int32_t trig = 0;
|
||||
ioctl(handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig);
|
||||
@ -795,17 +794,17 @@ void airtaudio::api::Oss::callbackEvent() {
|
||||
// Continue on to input section.
|
||||
}
|
||||
}
|
||||
if ( m_stream.mode == airtaudio::mode_input
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if ( m_mode == airtaudio::mode_input
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
// Setup parameters.
|
||||
if (m_stream.doConvertBuffer[1]) {
|
||||
buffer = m_stream.deviceBuffer;
|
||||
samples = m_stream.bufferSize * m_stream.nDeviceChannels[1];
|
||||
format = m_stream.deviceFormat[1];
|
||||
if (m_doConvertBuffer[1]) {
|
||||
buffer = m_deviceBuffer;
|
||||
samples = m_bufferSize * m_nDeviceChannels[1];
|
||||
format = m_deviceFormat[1];
|
||||
} else {
|
||||
buffer = m_stream.userBuffer[1];
|
||||
samples = m_stream.bufferSize * m_stream.nUserChannels[1];
|
||||
format = m_stream.userFormat;
|
||||
buffer = m_userBuffer[1];
|
||||
samples = m_bufferSize * m_nUserChannels[1];
|
||||
format = m_userFormat;
|
||||
}
|
||||
// Read samples from device.
|
||||
result = read(handle->id[1], buffer, samples * audio::getFormatBytes(format));
|
||||
@ -817,16 +816,16 @@ void airtaudio::api::Oss::callbackEvent() {
|
||||
goto unlock;
|
||||
}
|
||||
// Do byte swapping if necessary.
|
||||
if (m_stream.doByteSwap[1]) {
|
||||
if (m_doByteSwap[1]) {
|
||||
byteSwapBuffer(buffer, samples, format);
|
||||
}
|
||||
// Do buffer conversion if necessary.
|
||||
if (m_stream.doConvertBuffer[1]) {
|
||||
convertBuffer(m_stream.userBuffer[1], m_stream.deviceBuffer, m_stream.convertInfo[1]);
|
||||
if (m_doConvertBuffer[1]) {
|
||||
convertBuffer(m_userBuffer[1], m_deviceBuffer, m_convertInfo[1]);
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
m_stream.mutex.unlock();
|
||||
m_mutex.unlock();
|
||||
airtaudio::Api::tickStreamTime();
|
||||
if (doStopStream == 1) {
|
||||
this->stopStream();
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#if !defined(__AIRTAUDIO_API_OSS_H__) && defined(__LINUX_OSS__)
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
|
||||
@ -65,7 +64,7 @@ struct PulseAudioHandle {
|
||||
};
|
||||
|
||||
airtaudio::api::Pulse::~Pulse() {
|
||||
if (m_stream.state != airtaudio::state_closed) {
|
||||
if (m_state != airtaudio::state_closed) {
|
||||
closeStream();
|
||||
}
|
||||
}
|
||||
@ -92,25 +91,27 @@ airtaudio::DeviceInfo airtaudio::api::Pulse::getDeviceInfo(uint32_t _device) {
|
||||
return info;
|
||||
}
|
||||
|
||||
static void pulseaudio_callback(void* _user) {
|
||||
airtaudio::CallbackInfo *cbi = static_cast<airtaudio::CallbackInfo *>(_user);
|
||||
airtaudio::api::Pulse *context = static_cast<airtaudio::api::Pulse*>(cbi->object);
|
||||
volatile bool *isRunning = &cbi->isRunning;
|
||||
while (*isRunning) {
|
||||
context->callbackEvent();
|
||||
static void pulseaudio_callback(void* _userData) {
|
||||
airtaudio::api::Pulse* myClass = reinterpret_cast<airtaudio::api::Pulse*>(_userData);
|
||||
myClass->callbackEvent();
|
||||
}
|
||||
|
||||
void airtaudio::api::Pulse::callbackEvent() {
|
||||
while (m_callbackInfo.isRunning == true) {
|
||||
callbackEventOneCycle();
|
||||
}
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::api::Pulse::closeStream() {
|
||||
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_stream.apiHandle);
|
||||
m_stream.callbackInfo.isRunning = false;
|
||||
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_apiHandle);
|
||||
m_callbackInfo.isRunning = false;
|
||||
if (pah) {
|
||||
m_stream.mutex.lock();
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
m_mutex.lock();
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
pah->runnable = true;
|
||||
pah->runnable_cv.notify_one();;
|
||||
}
|
||||
m_stream.mutex.unlock();
|
||||
m_mutex.unlock();
|
||||
pah->thread->join();
|
||||
if (pah->s_play) {
|
||||
pa_simple_flush(pah->s_play, nullptr);
|
||||
@ -120,89 +121,83 @@ enum airtaudio::error airtaudio::api::Pulse::closeStream() {
|
||||
pa_simple_free(pah->s_rec);
|
||||
}
|
||||
delete pah;
|
||||
m_stream.apiHandle = nullptr;
|
||||
m_apiHandle = nullptr;
|
||||
}
|
||||
if (m_stream.userBuffer[0] != nullptr) {
|
||||
free(m_stream.userBuffer[0]);
|
||||
m_stream.userBuffer[0] = nullptr;
|
||||
}
|
||||
if (m_stream.userBuffer[1] != nullptr) {
|
||||
free(m_stream.userBuffer[1]);
|
||||
m_stream.userBuffer[1] = nullptr;
|
||||
}
|
||||
m_stream.state = airtaudio::state_closed;
|
||||
m_stream.mode = airtaudio::mode_unknow;
|
||||
m_userBuffer[0].clear();
|
||||
m_userBuffer[1].clear();
|
||||
m_state = airtaudio::state_closed;
|
||||
m_mode = airtaudio::mode_unknow;
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
|
||||
void airtaudio::api::Pulse::callbackEvent() {
|
||||
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_stream.apiHandle);
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
std::unique_lock<std::mutex> lck(m_stream.mutex);
|
||||
void airtaudio::api::Pulse::callbackEventOneCycle() {
|
||||
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_apiHandle);
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
std::unique_lock<std::mutex> lck(m_mutex);
|
||||
while (!pah->runnable) {
|
||||
pah->runnable_cv.wait(lck);
|
||||
}
|
||||
if (m_stream.state != airtaudio::state_running) {
|
||||
m_stream.mutex.unlock();
|
||||
if (m_state != airtaudio::state_running) {
|
||||
m_mutex.unlock();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("the stream is closed ... this shouldn't happen!");
|
||||
return;
|
||||
}
|
||||
double streamTime = getStreamTime();
|
||||
enum airtaudio::status status = airtaudio::status_ok;
|
||||
int32_t doStopStream = m_stream.callbackInfo.callback(m_stream.userBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)],
|
||||
m_stream.userBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)],
|
||||
m_stream.bufferSize,
|
||||
int32_t doStopStream = m_callbackInfo.callback(&m_userBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)][0],
|
||||
&m_userBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)][0],
|
||||
m_bufferSize,
|
||||
streamTime,
|
||||
status);
|
||||
if (doStopStream == 2) {
|
||||
abortStream();
|
||||
return;
|
||||
}
|
||||
m_stream.mutex.lock();
|
||||
void *pulse_in = m_stream.doConvertBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)] ? m_stream.deviceBuffer : m_stream.userBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)];
|
||||
void *pulse_out = m_stream.doConvertBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)] ? m_stream.deviceBuffer : m_stream.userBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)];
|
||||
if (m_stream.state != airtaudio::state_running) {
|
||||
m_mutex.lock();
|
||||
void *pulse_in = m_doConvertBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)] ? m_deviceBuffer : &m_userBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)][0];
|
||||
void *pulse_out = m_doConvertBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)] ? m_deviceBuffer : &m_userBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)][0];
|
||||
if (m_state != airtaudio::state_running) {
|
||||
goto unlock;
|
||||
}
|
||||
int32_t pa_error;
|
||||
size_t bytes;
|
||||
if ( m_stream.mode == airtaudio::mode_output
|
||||
|| m_stream.mode == airtaudio::mode_duplex) {
|
||||
if (m_stream.doConvertBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)]) {
|
||||
convertBuffer(m_stream.deviceBuffer,
|
||||
m_stream.userBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)],
|
||||
m_stream.convertInfo[airtaudio::modeToIdTable(airtaudio::mode_output)]);
|
||||
bytes = m_stream.nDeviceChannels[airtaudio::modeToIdTable(airtaudio::mode_output)] * m_stream.bufferSize * audio::getFormatBytes(m_stream.deviceFormat[airtaudio::modeToIdTable(airtaudio::mode_output)]);
|
||||
if ( m_mode == airtaudio::mode_output
|
||||
|| m_mode == airtaudio::mode_duplex) {
|
||||
if (m_doConvertBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)]) {
|
||||
convertBuffer(m_deviceBuffer,
|
||||
&m_userBuffer[airtaudio::modeToIdTable(airtaudio::mode_output)][0],
|
||||
m_convertInfo[airtaudio::modeToIdTable(airtaudio::mode_output)]);
|
||||
bytes = m_nDeviceChannels[airtaudio::modeToIdTable(airtaudio::mode_output)] * m_bufferSize * audio::getFormatBytes(m_deviceFormat[airtaudio::modeToIdTable(airtaudio::mode_output)]);
|
||||
} else {
|
||||
bytes = m_stream.nUserChannels[airtaudio::modeToIdTable(airtaudio::mode_output)] * m_stream.bufferSize * audio::getFormatBytes(m_stream.userFormat);
|
||||
bytes = m_nUserChannels[airtaudio::modeToIdTable(airtaudio::mode_output)] * m_bufferSize * audio::getFormatBytes(m_userFormat);
|
||||
}
|
||||
if (pa_simple_write(pah->s_play, pulse_out, bytes, &pa_error) < 0) {
|
||||
ATA_ERROR("audio write error, " << pa_strerror(pa_error) << ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (m_stream.mode == airtaudio::mode_input || m_stream.mode == airtaudio::mode_duplex) {
|
||||
if (m_stream.doConvertBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)]) {
|
||||
bytes = m_stream.nDeviceChannels[airtaudio::modeToIdTable(airtaudio::mode_input)] * m_stream.bufferSize * audio::getFormatBytes(m_stream.deviceFormat[airtaudio::modeToIdTable(airtaudio::mode_input)]);
|
||||
if (m_mode == airtaudio::mode_input || m_mode == airtaudio::mode_duplex) {
|
||||
if (m_doConvertBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)]) {
|
||||
bytes = m_nDeviceChannels[airtaudio::modeToIdTable(airtaudio::mode_input)] * m_bufferSize * audio::getFormatBytes(m_deviceFormat[airtaudio::modeToIdTable(airtaudio::mode_input)]);
|
||||
} else {
|
||||
bytes = m_stream.nUserChannels[airtaudio::modeToIdTable(airtaudio::mode_input)] * m_stream.bufferSize * audio::getFormatBytes(m_stream.userFormat);
|
||||
bytes = m_nUserChannels[airtaudio::modeToIdTable(airtaudio::mode_input)] * m_bufferSize * audio::getFormatBytes(m_userFormat);
|
||||
}
|
||||
if (pa_simple_read(pah->s_rec, pulse_in, bytes, &pa_error) < 0) {
|
||||
ATA_ERROR("audio read error, " << pa_strerror(pa_error) << ".");
|
||||
return;
|
||||
}
|
||||
if (m_stream.doConvertBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)]) {
|
||||
convertBuffer(m_stream.userBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)],
|
||||
m_stream.deviceBuffer,
|
||||
m_stream.convertInfo[airtaudio::modeToIdTable(airtaudio::mode_input)]);
|
||||
if (m_doConvertBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)]) {
|
||||
convertBuffer(&m_userBuffer[airtaudio::modeToIdTable(airtaudio::mode_input)][0],
|
||||
m_deviceBuffer,
|
||||
m_convertInfo[airtaudio::modeToIdTable(airtaudio::mode_input)]);
|
||||
}
|
||||
}
|
||||
unlock:
|
||||
m_stream.mutex.unlock();
|
||||
m_mutex.unlock();
|
||||
airtaudio::Api::tickStreamTime();
|
||||
if (doStopStream == 1) {
|
||||
stopStream();
|
||||
@ -212,70 +207,70 @@ unlock:
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::api::Pulse::startStream() {
|
||||
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_stream.apiHandle);
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_apiHandle);
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("the stream is not open!");
|
||||
return airtaudio::error_invalidUse;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_running) {
|
||||
if (m_state == airtaudio::state_running) {
|
||||
ATA_ERROR("the stream is already running!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
m_stream.mutex.lock();
|
||||
m_stream.state = airtaudio::state_running;
|
||||
m_mutex.lock();
|
||||
m_state = airtaudio::state_running;
|
||||
pah->runnable = true;
|
||||
pah->runnable_cv.notify_one();
|
||||
m_stream.mutex.unlock();
|
||||
m_mutex.unlock();
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::api::Pulse::stopStream() {
|
||||
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_stream.apiHandle);
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_apiHandle);
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("the stream is not open!");
|
||||
return airtaudio::error_invalidUse;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_stream.mutex.lock();
|
||||
m_state = airtaudio::state_stopped;
|
||||
m_mutex.lock();
|
||||
if (pah && pah->s_play) {
|
||||
int32_t pa_error;
|
||||
if (pa_simple_drain(pah->s_play, &pa_error) < 0) {
|
||||
ATA_ERROR("error draining output device, " << pa_strerror(pa_error) << ".");
|
||||
m_stream.mutex.unlock();
|
||||
m_mutex.unlock();
|
||||
return airtaudio::error_systemError;
|
||||
}
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_stream.mutex.unlock();
|
||||
m_state = airtaudio::state_stopped;
|
||||
m_mutex.unlock();
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
|
||||
enum airtaudio::error airtaudio::api::Pulse::abortStream() {
|
||||
PulseAudioHandle *pah = static_cast<PulseAudioHandle*>(m_stream.apiHandle);
|
||||
if (m_stream.state == airtaudio::state_closed) {
|
||||
PulseAudioHandle *pah = static_cast<PulseAudioHandle*>(m_apiHandle);
|
||||
if (m_state == airtaudio::state_closed) {
|
||||
ATA_ERROR("the stream is not open!");
|
||||
return airtaudio::error_invalidUse;
|
||||
}
|
||||
if (m_stream.state == airtaudio::state_stopped) {
|
||||
if (m_state == airtaudio::state_stopped) {
|
||||
ATA_ERROR("the stream is already stopped!");
|
||||
return airtaudio::error_warning;
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_stream.mutex.lock();
|
||||
m_state = airtaudio::state_stopped;
|
||||
m_mutex.lock();
|
||||
if (pah && pah->s_play) {
|
||||
int32_t pa_error;
|
||||
if (pa_simple_flush(pah->s_play, &pa_error) < 0) {
|
||||
ATA_ERROR("error flushing output device, " << pa_strerror(pa_error) << ".");
|
||||
m_stream.mutex.unlock();
|
||||
m_mutex.unlock();
|
||||
return airtaudio::error_systemError;
|
||||
}
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_stream.mutex.unlock();
|
||||
m_state = airtaudio::state_stopped;
|
||||
m_mutex.unlock();
|
||||
return airtaudio::error_none;
|
||||
}
|
||||
|
||||
@ -308,7 +303,7 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device,
|
||||
for (const uint32_t *sr = SUPPORTED_SAMPLERATES; *sr; ++sr) {
|
||||
if (_sampleRate == *sr) {
|
||||
sr_found = true;
|
||||
m_stream.sampleRate = _sampleRate;
|
||||
m_sampleRate = _sampleRate;
|
||||
ss.rate = _sampleRate;
|
||||
break;
|
||||
}
|
||||
@ -323,7 +318,7 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device,
|
||||
++sf) {
|
||||
if (_format == sf->airtaudio_format) {
|
||||
sf_found = true;
|
||||
m_stream.userFormat = sf->airtaudio_format;
|
||||
m_userFormat = sf->airtaudio_format;
|
||||
ss.format = sf->pa_format;
|
||||
break;
|
||||
}
|
||||
@ -332,55 +327,55 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device,
|
||||
ATA_ERROR("unsupported sample format.");
|
||||
return false;
|
||||
}
|
||||
m_stream.deviceInterleaved[modeToIdTable(_mode)] = true;
|
||||
m_stream.nBuffers = 1;
|
||||
m_stream.doByteSwap[modeToIdTable(_mode)] = false;
|
||||
m_stream.doConvertBuffer[modeToIdTable(_mode)] = false;
|
||||
m_stream.deviceFormat[modeToIdTable(_mode)] = m_stream.userFormat;
|
||||
m_stream.nUserChannels[modeToIdTable(_mode)] = _channels;
|
||||
m_stream.nDeviceChannels[modeToIdTable(_mode)] = _channels + _firstChannel;
|
||||
m_stream.channelOffset[modeToIdTable(_mode)] = 0;
|
||||
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;
|
||||
// Allocate necessary internal buffers.
|
||||
bufferBytes = m_stream.nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_stream.userFormat);
|
||||
m_stream.userBuffer[modeToIdTable(_mode)] = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.userBuffer[modeToIdTable(_mode)] == nullptr) {
|
||||
bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat);
|
||||
m_userBuffer[modeToIdTable(_mode)].resize(bufferBytes, 0);
|
||||
if (m_userBuffer[modeToIdTable(_mode)].size() == 0) {
|
||||
ATA_ERROR("error allocating user buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
m_stream.bufferSize = *_bufferSize;
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
m_bufferSize = *_bufferSize;
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
bool makeBuffer = true;
|
||||
bufferBytes = m_stream.nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_stream.deviceFormat[modeToIdTable(_mode)]);
|
||||
bufferBytes = m_nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]);
|
||||
if (_mode == airtaudio::mode_input) {
|
||||
if (m_stream.mode == airtaudio::mode_output && m_stream.deviceBuffer) {
|
||||
uint64_t bytesOut = m_stream.nDeviceChannels[0] * audio::getFormatBytes(m_stream.deviceFormat[0]);
|
||||
if (m_mode == airtaudio::mode_output && m_deviceBuffer) {
|
||||
uint64_t bytesOut = m_nDeviceChannels[0] * audio::getFormatBytes(m_deviceFormat[0]);
|
||||
if (bufferBytes <= bytesOut) makeBuffer = false;
|
||||
}
|
||||
}
|
||||
if (makeBuffer) {
|
||||
bufferBytes *= *_bufferSize;
|
||||
if (m_stream.deviceBuffer) free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_stream.deviceBuffer == nullptr) {
|
||||
if (m_deviceBuffer) free(m_deviceBuffer);
|
||||
m_deviceBuffer = (char *) calloc(bufferBytes, 1);
|
||||
if (m_deviceBuffer == nullptr) {
|
||||
ATA_ERROR("error allocating device buffer memory.");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_stream.device[modeToIdTable(_mode)] = _device;
|
||||
m_device[modeToIdTable(_mode)] = _device;
|
||||
// Setup the buffer conversion information structure.
|
||||
if (m_stream.doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
if (m_doConvertBuffer[modeToIdTable(_mode)]) {
|
||||
setConvertInfo(_mode, _firstChannel);
|
||||
}
|
||||
if (!m_stream.apiHandle) {
|
||||
if (!m_apiHandle) {
|
||||
PulseAudioHandle *pah = new PulseAudioHandle;
|
||||
if (!pah) {
|
||||
ATA_ERROR("error allocating memory for handle.");
|
||||
goto error;
|
||||
}
|
||||
m_stream.apiHandle = pah;
|
||||
m_apiHandle = pah;
|
||||
}
|
||||
pah = static_cast<PulseAudioHandle *>(m_stream.apiHandle);
|
||||
pah = static_cast<PulseAudioHandle *>(m_apiHandle);
|
||||
int32_t error;
|
||||
switch (_mode) {
|
||||
case airtaudio::mode_input:
|
||||
@ -400,38 +395,34 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device,
|
||||
default:
|
||||
goto error;
|
||||
}
|
||||
if (m_stream.mode == airtaudio::mode_unknow) {
|
||||
m_stream.mode = _mode;
|
||||
} else if (m_stream.mode == _mode) {
|
||||
if (m_mode == airtaudio::mode_unknow) {
|
||||
m_mode = _mode;
|
||||
} else if (m_mode == _mode) {
|
||||
goto error;
|
||||
}else {
|
||||
m_stream.mode = airtaudio::mode_duplex;
|
||||
m_mode = airtaudio::mode_duplex;
|
||||
}
|
||||
if (!m_stream.callbackInfo.isRunning) {
|
||||
m_stream.callbackInfo.object = this;
|
||||
m_stream.callbackInfo.isRunning = true;
|
||||
pah->thread = new std::thread(pulseaudio_callback, (void *)&m_stream.callbackInfo);
|
||||
if (!m_callbackInfo.isRunning) {
|
||||
m_callbackInfo.isRunning = true;
|
||||
pah->thread = new std::thread(pulseaudio_callback, this);
|
||||
if (pah->thread == nullptr) {
|
||||
ATA_ERROR("error creating thread.");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
m_stream.state = airtaudio::state_stopped;
|
||||
m_state = airtaudio::state_stopped;
|
||||
return true;
|
||||
error:
|
||||
if (pah && m_stream.callbackInfo.isRunning) {
|
||||
if (pah && m_callbackInfo.isRunning) {
|
||||
delete pah;
|
||||
m_stream.apiHandle = 0;
|
||||
m_apiHandle = 0;
|
||||
}
|
||||
for (int32_t i=0; i<2; i++) {
|
||||
if (m_stream.userBuffer[i]) {
|
||||
free(m_stream.userBuffer[i]);
|
||||
m_stream.userBuffer[i] = 0;
|
||||
m_userBuffer[i].clear();
|
||||
}
|
||||
}
|
||||
if (m_stream.deviceBuffer) {
|
||||
free(m_stream.deviceBuffer);
|
||||
m_stream.deviceBuffer = 0;
|
||||
if (m_deviceBuffer) {
|
||||
free(m_deviceBuffer);
|
||||
m_deviceBuffer = 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#if !defined(__AIRTAUDIO_API_PULSE_H__) && defined(__LINUX_PULSE__)
|
||||
@ -29,6 +28,7 @@ namespace airtaudio {
|
||||
// public because it is called by the internal callback handler,
|
||||
// which is not a member of RtAudio. External use of this function
|
||||
// will most likely produce highly undesireable results!
|
||||
void callbackEventOneCycle();
|
||||
void callbackEvent();
|
||||
private:
|
||||
std::vector<airtaudio::DeviceInfo> m_devices;
|
||||
|
@ -1,7 +1,6 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
@ -1,13 +1,12 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_ERROR_H__
|
||||
#define __AIRTAUDIO_ERROR_H__
|
||||
#ifndef __AIRTAUDIO_CB_H__
|
||||
#define __AIRTAUDIO_CB_H__
|
||||
|
||||
#include <thread>
|
||||
#include <condition_variable>
|
||||
@ -15,6 +14,9 @@
|
||||
#include <functional>
|
||||
#include <audio/channel.h>
|
||||
#include <audio/format.h>
|
||||
#include <airtaudio/error.h>
|
||||
#include <airtaudio/status.h>
|
||||
#include <airtaudio/Flags.h>
|
||||
|
||||
// defien type : uintXX_t and intXX_t
|
||||
#define __STDC_LIMIT_MACROS
|
||||
@ -28,28 +30,7 @@
|
||||
|
||||
namespace airtaudio {
|
||||
//! Defined error types.
|
||||
enum error {
|
||||
error_none, //!< No error
|
||||
error_fail, //!< An error occure in the operation
|
||||
error_warning, //!< A non-critical error.
|
||||
error_inputNull, //!< null input or internal errror
|
||||
error_invalidUse, //!< The function was called incorrectly.
|
||||
error_systemError //!< A system error occured.
|
||||
};
|
||||
|
||||
class Flags {
|
||||
public:
|
||||
bool m_minimizeLatency; // Simple example ==> TODO ...
|
||||
Flags() :
|
||||
m_minimizeLatency(false) {
|
||||
// nothing to do ...
|
||||
}
|
||||
};
|
||||
enum status {
|
||||
status_ok, //!< nothing...
|
||||
status_overflow, //!< Internal buffer has more data than they can accept
|
||||
status_underflow //!< The internal buffer is empty
|
||||
};
|
||||
/**
|
||||
* @brief RtAudio callback function prototype.
|
||||
*
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
*
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
*
|
||||
* @license BSD 3 clauses (see license file)
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#include <airtaudio/debug.h>
|
||||
|
@ -1,9 +1,8 @@
|
||||
/**
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
*
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
*
|
||||
* @license BSD 3 clauses (see license file)
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_DEBUG_H__
|
||||
|
9
airtaudio/error.cpp
Normal file
9
airtaudio/error.cpp
Normal file
@ -0,0 +1,9 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#include <airtaudio/error.h>
|
||||
#include <airtaudio/debug.h>
|
24
airtaudio/error.h
Normal file
24
airtaudio/error.h
Normal file
@ -0,0 +1,24 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_ERROR_H__
|
||||
#define __AIRTAUDIO_ERROR_H__
|
||||
|
||||
#include <etk/types.h>
|
||||
|
||||
namespace airtaudio {
|
||||
enum error {
|
||||
error_none, //!< No error
|
||||
error_fail, //!< An error occure in the operation
|
||||
error_warning, //!< A non-critical error.
|
||||
error_inputNull, //!< null input or internal errror
|
||||
error_invalidUse, //!< The function was called incorrectly.
|
||||
error_systemError //!< A system error occured.
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
@ -1,52 +0,0 @@
|
||||
/**
|
||||
* @author Gary P. SCAVONE
|
||||
*
|
||||
* @copyright 2001-2013 Gary P. Scavone, all right reserved
|
||||
*
|
||||
* @license like MIT (see license file)
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_INT24_T_H__
|
||||
#define __AIRTAUDIO_INT24_T_H__
|
||||
|
||||
#pragma pack(push, 1)
|
||||
class int24_t {
|
||||
protected:
|
||||
uint8_t c3[3];
|
||||
public:
|
||||
int24_t() {}
|
||||
int24_t& operator = (const int32_t& i) {
|
||||
c3[0] = (i & 0x000000ff);
|
||||
c3[1] = (i & 0x0000ff00) >> 8;
|
||||
c3[2] = (i & 0x00ff0000) >> 16;
|
||||
return *this;
|
||||
}
|
||||
|
||||
int24_t(const int24_t& v) {
|
||||
*this = v;
|
||||
}
|
||||
int24_t(const double& d) {
|
||||
*this = (int32_t)d;
|
||||
}
|
||||
int24_t(const float& f) {
|
||||
*this = (int32_t)f;
|
||||
}
|
||||
int24_t(const int16_t& s) {
|
||||
*this = (int32_t)s;
|
||||
}
|
||||
int24_t(const int8_t& c) {
|
||||
*this = (int32_t)c;
|
||||
}
|
||||
|
||||
int32_t asInt() {
|
||||
int32_t i = c3[0] | (c3[1] << 8) | (c3[2] << 16);
|
||||
if (i & 0x800000) {
|
||||
i |= ~0xffffff;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
|
||||
#endif
|
21
airtaudio/mode.cpp
Normal file
21
airtaudio/mode.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#include <airtaudio/mode.h>
|
||||
#include <airtaudio/debug.h>
|
||||
|
||||
int32_t airtaudio::modeToIdTable(enum mode _mode) {
|
||||
switch (_mode) {
|
||||
case mode_unknow:
|
||||
case mode_duplex:
|
||||
case mode_output:
|
||||
return 0;
|
||||
case mode_input:
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
23
airtaudio/mode.h
Normal file
23
airtaudio/mode.h
Normal file
@ -0,0 +1,23 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_MODE_H__
|
||||
#define __AIRTAUDIO_MODE_H__
|
||||
|
||||
#include <etk/types.h>
|
||||
|
||||
namespace airtaudio {
|
||||
enum mode {
|
||||
mode_unknow,
|
||||
mode_output,
|
||||
mode_input,
|
||||
mode_duplex
|
||||
};
|
||||
int32_t modeToIdTable(enum mode _mode);
|
||||
}
|
||||
|
||||
#endif
|
6
airtaudio/state.cpp
Normal file
6
airtaudio/state.cpp
Normal file
@ -0,0 +1,6 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
22
airtaudio/state.h
Normal file
22
airtaudio/state.h
Normal file
@ -0,0 +1,22 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_STATE_H__
|
||||
#define __AIRTAUDIO_STATE_H__
|
||||
|
||||
#include <etk/types.h>
|
||||
|
||||
namespace airtaudio {
|
||||
enum state {
|
||||
state_closed,
|
||||
state_stopped,
|
||||
state_stopping,
|
||||
state_running
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
32
airtaudio/status.cpp
Normal file
32
airtaudio/status.cpp
Normal file
@ -0,0 +1,32 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#include <airtaudio/status.h>
|
||||
#include <airtaudio/debug.h>
|
||||
const char* listValue[] = {
|
||||
"ok",
|
||||
"overflow",
|
||||
"underflow"
|
||||
};
|
||||
|
||||
std::ostream& airtaudio::operator <<(std::ostream& _os, enum airtaudio::status _obj) {
|
||||
_os << listValue[_obj];
|
||||
return _os;
|
||||
}
|
||||
|
||||
std::ostream& airtaudio::operator <<(std::ostream& _os, const std::vector<enum airtaudio::status>& _obj) {
|
||||
_os << std::string("{");
|
||||
for (size_t iii=0; iii<_obj.size(); ++iii) {
|
||||
if (iii!=0) {
|
||||
_os << std::string(";");
|
||||
}
|
||||
_os << _obj[iii];
|
||||
}
|
||||
_os << std::string("}");
|
||||
return _os;
|
||||
}
|
||||
|
24
airtaudio/status.h
Normal file
24
airtaudio/status.h
Normal file
@ -0,0 +1,24 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_STATUS_H__
|
||||
#define __AIRTAUDIO_STATUS_H__
|
||||
|
||||
#include <etk/types.h>
|
||||
|
||||
namespace airtaudio {
|
||||
enum status {
|
||||
status_ok, //!< nothing...
|
||||
status_overflow, //!< Internal buffer has more data than they can accept
|
||||
status_underflow //!< The internal buffer is empty
|
||||
};
|
||||
std::ostream& operator <<(std::ostream& _os, enum airtaudio::status _obj);
|
||||
std::ostream& operator <<(std::ostream& _os, const std::vector<enum airtaudio::status>& _obj);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
70
airtaudio/type.cpp
Normal file
70
airtaudio/type.cpp
Normal file
@ -0,0 +1,70 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#include <airtaudio/type.h>
|
||||
#include <airtaudio/debug.h>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <climits>
|
||||
|
||||
#undef __class__
|
||||
#define __class__ "type"
|
||||
|
||||
static const char* listType[] {
|
||||
"undefined",
|
||||
"alsa",
|
||||
"pulse",
|
||||
"oss",
|
||||
"jack",
|
||||
"coreOSX",
|
||||
"corIOS",
|
||||
"asio",
|
||||
"ds",
|
||||
"java",
|
||||
"dummy",
|
||||
"user1",
|
||||
"user2",
|
||||
"user3",
|
||||
"user4"
|
||||
};
|
||||
static int32_t listTypeSize = sizeof(listType)/sizeof(char*);
|
||||
|
||||
|
||||
std::ostream& airtaudio::operator <<(std::ostream& _os, const enum airtaudio::type& _obj) {
|
||||
_os << listType[_obj];
|
||||
return _os;
|
||||
}
|
||||
|
||||
std::ostream& airtaudio::operator <<(std::ostream& _os, const std::vector<enum airtaudio::type>& _obj) {
|
||||
_os << std::string("{");
|
||||
for (size_t iii=0; iii<_obj.size(); ++iii) {
|
||||
if (iii!=0) {
|
||||
_os << std::string(";");
|
||||
}
|
||||
_os << _obj[iii];
|
||||
}
|
||||
_os << std::string("}");
|
||||
return _os;
|
||||
}
|
||||
/*
|
||||
template <enum audio::format> std::string to_string(const enum audio::format& _variable) {
|
||||
return listType[_value];
|
||||
}
|
||||
*/
|
||||
std::string airtaudio::getTypeString(enum airtaudio::type _value) {
|
||||
return listType[_value];
|
||||
}
|
||||
|
||||
enum airtaudio::type airtaudio::getTypeFromString(const std::string& _value) {
|
||||
for (int32_t iii=0; iii<listTypeSize; ++iii) {
|
||||
if (_value == listType[iii]) {
|
||||
return static_cast<enum airtaudio::type>(iii);
|
||||
}
|
||||
}
|
||||
return airtaudio::type_undefined;
|
||||
}
|
41
airtaudio/type.h
Normal file
41
airtaudio/type.h
Normal file
@ -0,0 +1,41 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2011, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @fork from RTAudio
|
||||
*/
|
||||
|
||||
#ifndef __AIRTAUDIO_TYPE_H__
|
||||
#define __AIRTAUDIO_TYPE_H__
|
||||
|
||||
#include <etk/types.h>
|
||||
#include <etk/stdTools.h>
|
||||
|
||||
namespace airtaudio {
|
||||
/**
|
||||
* @brief Audio API specifier arguments.
|
||||
*/
|
||||
enum type {
|
||||
type_undefined, //!< Error API.
|
||||
type_alsa, //!< LINUX The Advanced Linux Sound Architecture.
|
||||
type_pulse, //!< LINUX The Linux PulseAudio.
|
||||
type_oss, //!< LINUX The Linux Open Sound System.
|
||||
type_jack, //!< UNIX The Jack Low-Latency Audio Server.
|
||||
type_coreOSX, //!< Macintosh OSX Core Audio.
|
||||
type_coreIOS, //!< Macintosh iOS Core Audio.
|
||||
type_asio, //!< WINDOWS The Steinberg Audio Stream I/O.
|
||||
type_ds, //!< WINDOWS The Microsoft Direct Sound.
|
||||
type_java, //!< ANDROID Interface.
|
||||
type_dummy, //!< Empty wrapper (non-functional).
|
||||
type_user1, //!< User interface 1.
|
||||
type_user2, //!< User interface 2.
|
||||
type_user3, //!< User interface 3.
|
||||
type_user4, //!< User interface 4.
|
||||
};
|
||||
std::ostream& operator <<(std::ostream& _os, const enum airtaudio::type& _obj);
|
||||
std::ostream& operator <<(std::ostream& _os, const std::vector<enum airtaudio::type>& _obj);
|
||||
std::string getTypeString(enum airtaudio::type _value);
|
||||
enum airtaudio::type getTypeFromString(const std::string& _value);
|
||||
}
|
||||
|
||||
#endif
|
@ -12,10 +12,16 @@ def create(target):
|
||||
|
||||
myModule.add_src_file([
|
||||
'airtaudio/debug.cpp',
|
||||
'airtaudio/status.cpp',
|
||||
'airtaudio/type.cpp',
|
||||
'airtaudio/mode.cpp',
|
||||
'airtaudio/state.cpp',
|
||||
'airtaudio/error.cpp',
|
||||
'airtaudio/base.cpp',
|
||||
'airtaudio/Interface.cpp',
|
||||
'airtaudio/Flags.cpp',
|
||||
'airtaudio/Api.cpp',
|
||||
'airtaudio/api/Dummy.cpp',
|
||||
'airtaudio/api/Dummy.cpp'
|
||||
])
|
||||
myModule.add_module_depend(['audio', 'etk'])
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user