[DEV] set member in private for internal backend interface

This commit is contained in:
Edouard DUPIN 2015-02-10 21:01:53 +01:00
parent 4febe7b119
commit 5c9361c199
18 changed files with 593 additions and 716 deletions

View File

@ -49,7 +49,6 @@ airtaudio::Api::Api() :
m_device[1] = 11111; m_device[1] = 11111;
m_state = airtaudio::state_closed; m_state = airtaudio::state_closed;
m_mode = airtaudio::mode_unknow; m_mode = airtaudio::mode_unknow;
m_apiHandle = 0;
} }
airtaudio::Api::~Api() { airtaudio::Api::~Api() {

View File

@ -62,8 +62,6 @@ namespace airtaudio {
protected: protected:
mutable std::mutex m_mutex; mutable std::mutex m_mutex;
uint32_t m_device[2]; // Playback and record, respectively. 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::mode m_mode; // airtaudio::mode_output, airtaudio::mode_input, or airtaudio::mode_duplex.
enum airtaudio::state m_state; // STOPPED, RUNNING, or CLOSED enum airtaudio::state m_state; // STOPPED, RUNNING, or CLOSED
std::vector<char> m_userBuffer[2]; // Playback and record, respectively. std::vector<char> m_userBuffer[2]; // Playback and record, respectively.

View File

@ -21,17 +21,17 @@ airtaudio::Api* airtaudio::api::Alsa::Create() {
return new airtaudio::api::Alsa(); return new airtaudio::api::Alsa();
} }
namespace airtaudio {
// A structure to hold various information related to the ALSA API namespace api {
// implementation. class AlsaPrivate {
struct AlsaHandle { public:
snd_pcm_t *handles[2]; snd_pcm_t *handles[2];
bool synchronized; bool synchronized;
bool xrun[2]; bool xrun[2];
std::condition_variable runnable_cv; std::condition_variable runnable_cv;
bool runnable; bool runnable;
AlsaHandle() : AlsaPrivate() :
synchronized(false), synchronized(false),
runnable(false) { runnable(false) {
handles[0] = nullptr; handles[0] = nullptr;
@ -40,10 +40,11 @@ struct AlsaHandle {
xrun[1] = false; xrun[1] = false;
} }
}; };
};
};
airtaudio::api::Alsa::Alsa() :
m_private(new airtaudio::api::AlsaPrivate()) {
airtaudio::api::Alsa::Alsa() {
// Nothing to do here. // Nothing to do here.
} }
@ -631,19 +632,7 @@ foundDevice:
&& m_nUserChannels[modeToIdTable(_mode)] > 1) { && m_nUserChannels[modeToIdTable(_mode)] > 1) {
m_doConvertBuffer[modeToIdTable(_mode)] = true; m_doConvertBuffer[modeToIdTable(_mode)] = true;
} }
// Allocate the ApiHandle if necessary and then save. m_private->handles[modeToIdTable(_mode)] = phandle;
AlsaHandle *apiInfo = nullptr;
if (m_apiHandle == nullptr) {
apiInfo = (AlsaHandle *) new AlsaHandle;
if (apiInfo == nullptr) {
ATA_ERROR("error allocating AlsaHandle memory.");
goto error;
}
m_apiHandle = (void *) apiInfo;
} else {
apiInfo = (AlsaHandle *) m_apiHandle;
}
apiInfo->handles[modeToIdTable(_mode)] = phandle;
phandle = 0; phandle = 0;
// Allocate necessary internal buffers. // Allocate necessary internal buffers.
uint64_t bufferBytes; uint64_t bufferBytes;
@ -692,9 +681,9 @@ foundDevice:
// We had already set up an output stream. // We had already set up an output stream.
m_mode = airtaudio::mode_duplex; m_mode = airtaudio::mode_duplex;
// Link the streams if possible. // Link the streams if possible.
apiInfo->synchronized = false; m_private->synchronized = false;
if (snd_pcm_link(apiInfo->handles[0], apiInfo->handles[1]) == 0) { if (snd_pcm_link(m_private->handles[0], m_private->handles[1]) == 0) {
apiInfo->synchronized = true; m_private->synchronized = true;
} else { } else {
ATA_ERROR("unable to synchronize input and output devices."); ATA_ERROR("unable to synchronize input and output devices.");
// TODO : airtaudio::error_warning; // TODO : airtaudio::error_warning;
@ -712,16 +701,13 @@ foundDevice:
} }
return true; return true;
error: error:
if (apiInfo != nullptr) { if (m_private->handles[0]) {
if (apiInfo->handles[0]) { snd_pcm_close(m_private->handles[0]);
snd_pcm_close(apiInfo->handles[0]); m_private->handles[0] = nullptr;
} }
if (apiInfo->handles[1]) { if (m_private->handles[1]) {
snd_pcm_close(apiInfo->handles[1]); snd_pcm_close(m_private->handles[1]);
} m_private->handles[1] = nullptr;
delete apiInfo;
apiInfo = nullptr;
m_apiHandle = 0;
} }
if (phandle) { if (phandle) {
snd_pcm_close(phandle); snd_pcm_close(phandle);
@ -742,12 +728,11 @@ enum airtaudio::error airtaudio::api::Alsa::closeStream() {
ATA_ERROR("no open stream to close!"); ATA_ERROR("no open stream to close!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle;
m_callbackInfo.isRunning = false; m_callbackInfo.isRunning = false;
m_mutex.lock(); m_mutex.lock();
if (m_state == airtaudio::state_stopped) { if (m_state == airtaudio::state_stopped) {
apiInfo->runnable = true; m_private->runnable = true;
apiInfo->runnable_cv.notify_one(); m_private->runnable_cv.notify_one();
} }
m_mutex.unlock(); m_mutex.unlock();
if (m_callbackInfo.thread != nullptr) { if (m_callbackInfo.thread != nullptr) {
@ -757,23 +742,21 @@ enum airtaudio::error airtaudio::api::Alsa::closeStream() {
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
snd_pcm_drop(apiInfo->handles[0]); snd_pcm_drop(m_private->handles[0]);
} }
if ( m_mode == airtaudio::mode_input if ( m_mode == airtaudio::mode_input
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
snd_pcm_drop(apiInfo->handles[1]); snd_pcm_drop(m_private->handles[1]);
} }
} }
if (apiInfo != nullptr) { // close all stream :
if (apiInfo->handles[0]) { if (m_private->handles[0]) {
snd_pcm_close(apiInfo->handles[0]); snd_pcm_close(m_private->handles[0]);
m_private->handles[0] = nullptr;
} }
if (apiInfo->handles[1]) { if (m_private->handles[1]) {
snd_pcm_close(apiInfo->handles[1]); snd_pcm_close(m_private->handles[1]);
} m_private->handles[1] = nullptr;
delete apiInfo;
apiInfo = nullptr;
m_apiHandle = 0;
} }
for (int32_t iii=0; iii<2; ++iii) { for (int32_t iii=0; iii<2; ++iii) {
m_userBuffer[iii].clear(); m_userBuffer[iii].clear();
@ -799,8 +782,7 @@ enum airtaudio::error airtaudio::api::Alsa::startStream() {
std::unique_lock<std::mutex> lck(m_mutex); std::unique_lock<std::mutex> lck(m_mutex);
int32_t result = 0; int32_t result = 0;
snd_pcm_state_t state; snd_pcm_state_t state;
AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle; snd_pcm_t **handle = (snd_pcm_t **) m_private->handles;
snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
if (handle[0] == nullptr) { if (handle[0] == nullptr) {
@ -820,7 +802,7 @@ enum airtaudio::error airtaudio::api::Alsa::startStream() {
} }
if ( ( m_mode == airtaudio::mode_input if ( ( m_mode == airtaudio::mode_input
|| m_mode == airtaudio::mode_duplex) || m_mode == airtaudio::mode_duplex)
&& !apiInfo->synchronized) { && !m_private->synchronized) {
if (handle[1] == nullptr) { if (handle[1] == nullptr) {
ATA_ERROR("send nullptr to alsa ..."); ATA_ERROR("send nullptr to alsa ...");
if (handle[0] != nullptr) { if (handle[0] != nullptr) {
@ -838,8 +820,8 @@ enum airtaudio::error airtaudio::api::Alsa::startStream() {
} }
m_state = airtaudio::state_running; m_state = airtaudio::state_running;
unlock: unlock:
apiInfo->runnable = true; m_private->runnable = true;
apiInfo->runnable_cv.notify_one(); m_private->runnable_cv.notify_one();
if (result >= 0) { if (result >= 0) {
return airtaudio::error_none; return airtaudio::error_none;
} }
@ -857,11 +839,10 @@ enum airtaudio::error airtaudio::api::Alsa::stopStream() {
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
std::unique_lock<std::mutex> lck(m_mutex); std::unique_lock<std::mutex> lck(m_mutex);
int32_t result = 0; int32_t result = 0;
AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle; snd_pcm_t **handle = (snd_pcm_t **) m_private->handles;
snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
if (apiInfo->synchronized) { if (m_private->synchronized) {
result = snd_pcm_drop(handle[0]); result = snd_pcm_drop(handle[0]);
} else { } else {
result = snd_pcm_drain(handle[0]); result = snd_pcm_drain(handle[0]);
@ -873,7 +854,7 @@ enum airtaudio::error airtaudio::api::Alsa::stopStream() {
} }
if ( ( m_mode == airtaudio::mode_input if ( ( m_mode == airtaudio::mode_input
|| m_mode == airtaudio::mode_duplex) || m_mode == airtaudio::mode_duplex)
&& !apiInfo->synchronized) { && !m_private->synchronized) {
result = snd_pcm_drop(handle[1]); result = snd_pcm_drop(handle[1]);
if (result < 0) { if (result < 0) {
ATA_ERROR("error stopping input pcm device, " << snd_strerror(result) << "."); ATA_ERROR("error stopping input pcm device, " << snd_strerror(result) << ".");
@ -898,8 +879,7 @@ enum airtaudio::error airtaudio::api::Alsa::abortStream() {
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
std::unique_lock<std::mutex> lck(m_mutex); std::unique_lock<std::mutex> lck(m_mutex);
int32_t result = 0; int32_t result = 0;
AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle; snd_pcm_t **handle = (snd_pcm_t **) m_private->handles;
snd_pcm_t **handle = (snd_pcm_t **) apiInfo->handles;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
result = snd_pcm_drop(handle[0]); result = snd_pcm_drop(handle[0]);
@ -910,7 +890,7 @@ enum airtaudio::error airtaudio::api::Alsa::abortStream() {
} }
if ( ( m_mode == airtaudio::mode_input if ( ( m_mode == airtaudio::mode_input
|| m_mode == airtaudio::mode_duplex) || m_mode == airtaudio::mode_duplex)
&& !apiInfo->synchronized) { && !m_private->synchronized) {
result = snd_pcm_drop(handle[1]); result = snd_pcm_drop(handle[1]);
if (result < 0) { if (result < 0) {
ATA_ERROR("error aborting input pcm device, " << snd_strerror(result) << "."); ATA_ERROR("error aborting input pcm device, " << snd_strerror(result) << ".");
@ -937,13 +917,12 @@ void airtaudio::api::Alsa::callbackEvent() {
} }
void airtaudio::api::Alsa::callbackEventOneCycle() { void airtaudio::api::Alsa::callbackEventOneCycle() {
AlsaHandle *apiInfo = (AlsaHandle *) m_apiHandle;
if (m_state == airtaudio::state_stopped) { if (m_state == airtaudio::state_stopped) {
std::unique_lock<std::mutex> lck(m_mutex); std::unique_lock<std::mutex> lck(m_mutex);
// TODO : Set this back .... // TODO : Set this back ....
/* /*
while (!apiInfo->runnable) { while (!m_private->runnable) {
apiInfo->runnable_cv.wait(lck); m_private->runnable_cv.wait(lck);
} }
*/ */
if (m_state != airtaudio::state_running) { if (m_state != airtaudio::state_running) {
@ -957,13 +936,13 @@ void airtaudio::api::Alsa::callbackEventOneCycle() {
int32_t doStopStream = 0; int32_t doStopStream = 0;
double streamTime = getStreamTime(); double streamTime = getStreamTime();
enum airtaudio::status status = airtaudio::status_ok; enum airtaudio::status status = airtaudio::status_ok;
if (m_mode != airtaudio::mode_input && apiInfo->xrun[0] == true) { if (m_mode != airtaudio::mode_input && m_private->xrun[0] == true) {
status = airtaudio::status_underflow; status = airtaudio::status_underflow;
apiInfo->xrun[0] = false; m_private->xrun[0] = false;
} }
if (m_mode != airtaudio::mode_output && apiInfo->xrun[1] == true) { if (m_mode != airtaudio::mode_output && m_private->xrun[1] == true) {
status = airtaudio::status_overflow; status = airtaudio::status_overflow;
apiInfo->xrun[1] = false; m_private->xrun[1] = false;
} }
doStopStream = m_callbackInfo.callback(&m_userBuffer[0][0], doStopStream = m_callbackInfo.callback(&m_userBuffer[0][0],
&m_userBuffer[1][0], &m_userBuffer[1][0],
@ -985,7 +964,7 @@ void airtaudio::api::Alsa::callbackEventOneCycle() {
snd_pcm_t **handle; snd_pcm_t **handle;
snd_pcm_sframes_t frames; snd_pcm_sframes_t frames;
audio::format format; audio::format format;
handle = (snd_pcm_t **) apiInfo->handles; handle = (snd_pcm_t **) m_private->handles;
if ( m_mode == airtaudio::mode_input if ( m_mode == airtaudio::mode_input
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
// Setup parameters. // Setup parameters.
@ -1013,7 +992,7 @@ void airtaudio::api::Alsa::callbackEventOneCycle() {
if (result == -EPIPE) { if (result == -EPIPE) {
snd_pcm_state_t state = snd_pcm_state(handle[1]); snd_pcm_state_t state = snd_pcm_state(handle[1]);
if (state == SND_PCM_STATE_XRUN) { if (state == SND_PCM_STATE_XRUN) {
apiInfo->xrun[1] = true; m_private->xrun[1] = true;
result = snd_pcm_prepare(handle[1]); result = snd_pcm_prepare(handle[1]);
if (result < 0) { if (result < 0) {
ATA_ERROR("error preparing device after overrun, " << snd_strerror(result) << "."); ATA_ERROR("error preparing device after overrun, " << snd_strerror(result) << ".");
@ -1076,7 +1055,7 @@ tryOutput:
if (result == -EPIPE) { if (result == -EPIPE) {
snd_pcm_state_t state = snd_pcm_state(handle[0]); snd_pcm_state_t state = snd_pcm_state(handle[0]);
if (state == SND_PCM_STATE_XRUN) { if (state == SND_PCM_STATE_XRUN) {
apiInfo->xrun[0] = true; m_private->xrun[0] = true;
result = snd_pcm_prepare(handle[0]); result = snd_pcm_prepare(handle[0]);
if (result < 0) { if (result < 0) {
ATA_ERROR("error preparing device after underrun, " << snd_strerror(result) << "."); ATA_ERROR("error preparing device after underrun, " << snd_strerror(result) << ".");

View File

@ -10,6 +10,7 @@
namespace airtaudio { namespace airtaudio {
namespace api { namespace api {
class AlsaPrivate;
class Alsa: public airtaudio::Api { class Alsa: public airtaudio::Api {
public: public:
static airtaudio::Api* Create(); static airtaudio::Api* Create();
@ -34,6 +35,7 @@ namespace airtaudio {
private: private:
static void alsaCallbackEvent(void* _userData); static void alsaCallbackEvent(void* _userData);
private: private:
std::unique_ptr<AlsaPrivate> m_private;
std::vector<airtaudio::DeviceInfo> m_devices; std::vector<airtaudio::DeviceInfo> m_devices;
void saveDeviceInfo(); void saveDeviceInfo();
bool probeDeviceOpen(uint32_t _device, bool probeDeviceOpen(uint32_t _device,

View File

@ -47,25 +47,31 @@ static ASIODriverInfo driverInfo;
static CallbackInfo *asioCallbackInfo; static CallbackInfo *asioCallbackInfo;
static bool asioXRun; static bool asioXRun;
struct AsioHandle { namespace airtaudio {
namespace api {
class AsioPrivate {
public:
int32_t drainCounter; // Tracks callback counts when draining int32_t drainCounter; // Tracks callback counts when draining
bool internalDrain; // Indicates if stop is initiated from callback or not. bool internalDrain; // Indicates if stop is initiated from callback or not.
ASIOBufferInfo *bufferInfos; ASIOBufferInfo *bufferInfos;
HANDLE condition; HANDLE condition;
AsioHandle() : AsioPrivate() :
drainCounter(0), drainCounter(0),
internalDrain(false), internalDrain(false),
bufferInfos(0) { bufferInfos(0) {
} }
}; };
}
}
// Function declarations (definitions at end of section) // Function declarations (definitions at end of section)
static const char* getAsioErrorString(ASIOError _result); static const char* getAsioErrorString(ASIOError _result);
static void sampleRateChanged(ASIOSampleRate _sRate); static void sampleRateChanged(ASIOSampleRate _sRate);
static long asioMessages(long _selector, long _value, void* _message, double* _opt); static long asioMessages(long _selector, long _value, void* _message, double* _opt);
airtaudio::api::Asio::Asio() { airtaudio::api::Asio::Asio() :
m_private(new airtaudio::api::AsioPrivate()) {
// ASIO cannot run on a multi-threaded appartment. You can call // ASIO cannot run on a multi-threaded appartment. You can call
// CoInitialize beforehand, but it must be for appartment threading // CoInitialize beforehand, but it must be for appartment threading
// (in which case, CoInitilialize will return S_FALSE here). // (in which case, CoInitilialize will return S_FALSE here).
@ -403,23 +409,12 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
m_nBuffers = 2; m_nBuffers = 2;
// ASIO always uses non-interleaved buffers. // ASIO always uses non-interleaved buffers.
m_deviceInterleaved[modeToIdTable(_mode)] = false; m_deviceInterleaved[modeToIdTable(_mode)] = false;
// Allocate, if necessary, our AsioHandle structure for the stream. m_private->bufferInfos = 0;
AsioHandle *handle = (AsioHandle *) m_apiHandle;
if (handle == nullptr) {
handle = new AsioHandle;
if (handle == nullptr) {
drivers.removeCurrentDriver();
ATA_ERROR("error allocating AsioHandle memory.");
return false;
}
handle->bufferInfos = 0;
// Create a manual-reset event. // Create a manual-reset event.
handle->condition = CreateEvent(nullptr, // no security m_private->condition = CreateEvent(nullptr, // no security
TRUE, // manual-reset TRUE, // manual-reset
FALSE, // non-signaled initially FALSE, // non-signaled initially
nullptr); // unnamed nullptr); // unnamed
m_apiHandle = (void *) handle;
}
// Create the ASIO internal buffers. Since RtAudio sets up input // Create the ASIO internal buffers. Since RtAudio sets up input
// and output separately, we'll have to dispose of previously // and output separately, we'll have to dispose of previously
// created output buffers for a duplex stream. // created output buffers for a duplex stream.
@ -427,21 +422,21 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
if ( _mode == airtaudio::mode_input if ( _mode == airtaudio::mode_input
&& m_mode == airtaudio::mode_output) { && m_mode == airtaudio::mode_output) {
ASIODisposeBuffers(); ASIODisposeBuffers();
if (handle->bufferInfos == nullptr) { if (m_private->bufferInfos == nullptr) {
free(handle->bufferInfos); free(m_private->bufferInfos);
handle->bufferInfos = nullptr; m_private->bufferInfos = nullptr;
} }
} }
// Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure. // Allocate, initialize, and save the bufferInfos in our stream callbackInfo structure.
bool buffersAllocated = false; bool buffersAllocated = false;
uint32_t i, nChannels = m_nDeviceChannels[0] + m_nDeviceChannels[1]; uint32_t i, nChannels = m_nDeviceChannels[0] + m_nDeviceChannels[1];
handle->bufferInfos = (ASIOBufferInfo *) malloc(nChannels * sizeof(ASIOBufferInfo)); m_private->bufferInfos = (ASIOBufferInfo *) malloc(nChannels * sizeof(ASIOBufferInfo));
if (handle->bufferInfos == nullptr) { if (m_private->bufferInfos == nullptr) {
ATA_ERROR("error allocating bufferInfo memory for driver (" << driverName << ")."); ATA_ERROR("error allocating bufferInfo memory for driver (" << driverName << ").");
goto error; goto error;
} }
ASIOBufferInfo *infos; ASIOBufferInfo *infos;
infos = handle->bufferInfos; infos = m_private->bufferInfos;
for (i=0; i<m_nDeviceChannels[0]; i++, infos++) { for (i=0; i<m_nDeviceChannels[0]; i++, infos++) {
infos->isInput = ASIOFalse; infos->isInput = ASIOFalse;
infos->channelNum = i + m_channelOffset[0]; infos->channelNum = i + m_channelOffset[0];
@ -457,7 +452,7 @@ bool airtaudio::api::Asio::probeDeviceOpen(uint32_t _device,
asioCallbacks.sampleRateDidChange = &sampleRateChanged; asioCallbacks.sampleRateDidChange = &sampleRateChanged;
asioCallbacks.asioMessage = &asioMessages; asioCallbacks.asioMessage = &asioMessages;
asioCallbacks.bufferSwitchTimeInfo = nullptr; asioCallbacks.bufferSwitchTimeInfo = nullptr;
result = ASIOCreateBuffers(handle->bufferInfos, nChannels, m_bufferSize, &asioCallbacks); result = ASIOCreateBuffers(m_private->bufferInfos, nChannels, m_bufferSize, &asioCallbacks);
if (result != ASE_OK) { if (result != ASE_OK) {
ATA_ERROR("driver (" << driverName << ") error (" << getAsioErrorString(result) << ") creating buffers."); ATA_ERROR("driver (" << driverName << ") error (" << getAsioErrorString(result) << ") creating buffers.");
goto error; goto error;
@ -536,15 +531,10 @@ error:
ASIODisposeBuffers(); ASIODisposeBuffers();
} }
drivers.removeCurrentDriver(); drivers.removeCurrentDriver();
if (handle) { CloseHandle(m_private->condition);
CloseHandle(handle->condition); if (m_private->bufferInfos != nullptr) {
if (handle->bufferInfos) { free(m_private->bufferInfos);
free(handle->bufferInfos); m_private->bufferInfos = nullptr;
handle->bufferInfos = nullptr;
}
delete handle;
handle = nullptr;
m_apiHandle = 0;
} }
for (int32_t i=0; i<2; i++) { for (int32_t i=0; i<2; i++) {
if (m_userBuffer[i]) { if (m_userBuffer[i]) {
@ -570,14 +560,9 @@ enum airtaudio::error airtaudio::api::Asio::closeStream() {
} }
ASIODisposeBuffers(); ASIODisposeBuffers();
drivers.removeCurrentDriver(); drivers.removeCurrentDriver();
AsioHandle *handle = (AsioHandle *) m_apiHandle; CloseHandle(m_private->condition);
if (handle) { if (m_private->bufferInfos) {
CloseHandle(handle->condition); free(m_private->bufferInfos);
if (handle->bufferInfos) {
free(handle->bufferInfos);
}
delete handle;
m_apiHandle = 0;
} }
for (int32_t i=0; i<2; i++) { for (int32_t i=0; i<2; i++) {
if (m_userBuffer[i]) { if (m_userBuffer[i]) {
@ -604,15 +589,14 @@ enum airtaudio::error airtaudio::api::Asio::startStream() {
ATA_ERROR("the stream is already running!"); ATA_ERROR("the stream is already running!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
AsioHandle *handle = (AsioHandle *) m_apiHandle;
ASIOError result = ASIOStart(); ASIOError result = ASIOStart();
if (result != ASE_OK) { if (result != ASE_OK) {
ATA_ERROR("error (" << getAsioErrorString(result) << ") starting device."); ATA_ERROR("error (" << getAsioErrorString(result) << ") starting device.");
goto unlock; goto unlock;
} }
handle->drainCounter = 0; m_private->drainCounter = 0;
handle->internalDrain = false; m_private->internalDrain = false;
ResetEvent(handle->condition); ResetEvent(m_private->condition);
m_state = airtaudio::state_running; m_state = airtaudio::state_running;
asioXRun = false; asioXRun = false;
unlock: unlock:
@ -631,11 +615,10 @@ enum airtaudio::error airtaudio::api::Asio::stopStream() {
ATA_ERROR("the stream is already stopped!"); ATA_ERROR("the stream is already stopped!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
AsioHandle *handle = (AsioHandle *) m_apiHandle;
if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) {
if (handle->drainCounter == 0) { if (m_private->drainCounter == 0) {
handle->drainCounter = 2; m_private->drainCounter = 2;
WaitForSingleObject(handle->condition, INFINITE); // block until signaled WaitForSingleObject(m_private->condition, INFINITE); // block until signaled
} }
} }
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
@ -663,7 +646,6 @@ enum airtaudio::error airtaudio::api::Asio::abortStream() {
// noted where the device buffers need to be zeroed to avoid // noted where the device buffers need to be zeroed to avoid
// continuing sound, even when the device buffers are completely // continuing sound, even when the device buffers are completely
// disposed. So now, calling abort is the same as calling stop. // disposed. So now, calling abort is the same as calling stop.
// AsioHandle *handle = (AsioHandle *) m_apiHandle;
// handle->drainCounter = 2; // handle->drainCounter = 2;
return stopStream(); return stopStream();
} }
@ -691,22 +673,25 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
return false; return false;
} }
CallbackInfo *info = (CallbackInfo *) &m_callbackInfo; CallbackInfo *info = (CallbackInfo *) &m_callbackInfo;
AsioHandle *handle = (AsioHandle *) m_apiHandle;
// Check if we were draining the stream and signal if finished. // Check if we were draining the stream and signal if finished.
if (handle->drainCounter > 3) { if (m_private->drainCounter > 3) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;
if (handle->internalDrain == false) { if (m_private->internalDrain == false) {
SetEvent(handle->condition); SetEvent(m_private->condition);
} else { // spawn a thread to stop the stream } else { // spawn a thread to stop the stream
unsigned threadId; unsigned threadId;
m_callbackInfo.thread = _beginthreadex(nullptr, 0, &asioStopStream, m_callbackInfo.thread = _beginthreadex(nullptr,
&m_callbackInfo, 0, &threadId); 0,
&asioStopStream,
&m_callbackInfo,
0,
&threadId);
} }
return true; return true;
} }
// Invoke user callback to get fresh output data UNLESS we are // Invoke user callback to get fresh output data UNLESS we are
// draining stream. // draining stream.
if (handle->drainCounter == 0) { if (m_private->drainCounter == 0) {
double streamTime = getStreamTime(); double streamTime = getStreamTime();
rtaudio::streamStatus status = 0; rtaudio::streamStatus status = 0;
if (m_mode != airtaudio::mode_input && asioXRun == true) { if (m_mode != airtaudio::mode_input && asioXRun == true) {
@ -724,7 +709,7 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
status); status);
if (cbReturnValue == 2) { if (cbReturnValue == 2) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;
handle->drainCounter = 2; m_private->drainCounter = 2;
unsigned threadId; unsigned threadId;
m_callbackInfo.thread = _beginthreadex(nullptr, m_callbackInfo.thread = _beginthreadex(nullptr,
0, 0,
@ -734,8 +719,8 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
&threadId); &threadId);
return true; return true;
} else if (cbReturnValue == 1) { } else if (cbReturnValue == 1) {
handle->drainCounter = 1; m_private->drainCounter = 1;
handle->internalDrain = true; m_private->internalDrain = true;
} }
} }
uint32_t nChannels, bufferBytes, i, j; uint32_t nChannels, bufferBytes, i, j;
@ -743,10 +728,10 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
bufferBytes = m_bufferSize * audio::getFormatBytes(m_deviceFormat[0]); bufferBytes = m_bufferSize * audio::getFormatBytes(m_deviceFormat[0]);
if (handle->drainCounter > 1) { // write zeros to the output stream if (m_private->drainCounter > 1) { // write zeros to the output stream
for (i=0, j=0; i<nChannels; i++) { for (i=0, j=0; i<nChannels; i++) {
if (handle->bufferInfos[i].isInput != ASIOTrue) { if (m_private->bufferInfos[i].isInput != ASIOTrue) {
memset(handle->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes); memset(m_private->bufferInfos[i].buffers[bufferIndex], 0, bufferBytes);
} }
} }
} else if (m_doConvertBuffer[0]) { } else if (m_doConvertBuffer[0]) {
@ -757,8 +742,8 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
m_deviceFormat[0]); m_deviceFormat[0]);
} }
for (i=0, j=0; i<nChannels; i++) { for (i=0, j=0; i<nChannels; i++) {
if (handle->bufferInfos[i].isInput != ASIOTrue) { if (m_private->bufferInfos[i].isInput != ASIOTrue) {
memcpy(handle->bufferInfos[i].buffers[bufferIndex], memcpy(m_private->bufferInfos[i].buffers[bufferIndex],
&m_deviceBuffer[j++*bufferBytes], &m_deviceBuffer[j++*bufferBytes],
bufferBytes); bufferBytes);
} }
@ -770,15 +755,15 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
m_userFormat); m_userFormat);
} }
for (i=0, j=0; i<nChannels; i++) { for (i=0, j=0; i<nChannels; i++) {
if (handle->bufferInfos[i].isInput != ASIOTrue) { if (m_private->bufferInfos[i].isInput != ASIOTrue) {
memcpy(handle->bufferInfos[i].buffers[bufferIndex], memcpy(m_private->bufferInfos[i].buffers[bufferIndex],
&m_userBuffer[0][bufferBytes*j++], &m_userBuffer[0][bufferBytes*j++],
bufferBytes); bufferBytes);
} }
} }
} }
if (handle->drainCounter) { if (m_private->drainCounter) {
handle->drainCounter++; m_private->drainCounter++;
goto unlock; goto unlock;
} }
} }
@ -788,9 +773,9 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
if (m_doConvertBuffer[1]) { if (m_doConvertBuffer[1]) {
// Always interleave ASIO input data. // Always interleave ASIO input data.
for (i=0, j=0; i<nChannels; i++) { for (i=0, j=0; i<nChannels; i++) {
if (handle->bufferInfos[i].isInput == ASIOTrue) { if (m_private->bufferInfos[i].isInput == ASIOTrue) {
memcpy(&m_deviceBuffer[j++*bufferBytes], memcpy(&m_deviceBuffer[j++*bufferBytes],
handle->bufferInfos[i].buffers[bufferIndex], m_private->bufferInfos[i].buffers[bufferIndex],
bufferBytes); bufferBytes);
} }
} }
@ -804,9 +789,9 @@ bool airtaudio::api::Asio::callbackEvent(long bufferIndex) {
m_convertInfo[1]); m_convertInfo[1]);
} else { } else {
for (i=0, j=0; i<nChannels; i++) { for (i=0, j=0; i<nChannels; i++) {
if (handle->bufferInfos[i].isInput == ASIOTrue) { if (m_private->bufferInfos[i].isInput == ASIOTrue) {
memcpy(&m_userBuffer[1][bufferBytes*j++], memcpy(&m_userBuffer[1][bufferBytes*j++],
handle->bufferInfos[i].buffers[bufferIndex], m_private->bufferInfos[i].buffers[bufferIndex],
bufferBytes); bufferBytes);
} }
} }

View File

@ -10,6 +10,7 @@
namespace airtaudio { namespace airtaudio {
namespace api { namespace api {
class AsioPrivate:
class Asio: public airtaudio::Api { class Asio: public airtaudio::Api {
public: public:
static airtaudio::Api* Create(); static airtaudio::Api* Create();
@ -32,6 +33,7 @@ namespace airtaudio {
// will most likely produce highly undesireable results! // will most likely produce highly undesireable results!
bool callbackEvent(long _bufferIndex); bool callbackEvent(long _bufferIndex);
private: private:
std::unique_ptr<AsioPrivate> m_private;
std::vector<airtaudio::DeviceInfo> m_devices; std::vector<airtaudio::DeviceInfo> m_devices;
void saveDeviceInfo(); void saveDeviceInfo();
bool m_coInitialized; bool m_coInitialized;

View File

@ -24,24 +24,10 @@ airtaudio::Api* airtaudio::api::Core::Create() {
#undef __class__ #undef __class__
#define __class__ "api::Core" #define __class__ "api::Core"
// The OS X CoreAudio API is designed to use a separate callback namespace airtaudio {
// procedure for each of its audio devices. A single RtAudio duplex namespace api {
// stream using two different devices is supported here, though it class CorePrivate {
// cannot be guaranteed to always behave correctly because we cannot public:
// synchronize these two callbacks.
//
// A property listener is installed for over/underrun information.
// However, no functionality is currently provided to allow property
// listeners to trigger user handlers because it is unclear what could
// be done if a critical stream parameter (buffer size, sample rate,
// device disconnect) notification arrived. The listeners entail
// quite a bit of extra code and most likely, a user program wouldn't
// be prepared for the result anyway. However, we do provide a flag
// to the client callback function to inform of an over/underrun.
// A structure to hold various information related to the CoreAudio API
// implementation.
struct CoreHandle {
AudioDeviceID id[2]; // device ids AudioDeviceID id[2]; // device ids
#if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
AudioDeviceIOProcID procId[2]; AudioDeviceIOProcID procId[2];
@ -53,7 +39,7 @@ struct CoreHandle {
std::condition_variable condition; std::condition_variable condition;
int32_t drainCounter; // Tracks callback counts when draining int32_t drainCounter; // Tracks callback counts when draining
bool internalDrain; // Indicates if stop is initiated from callback or not. bool internalDrain; // Indicates if stop is initiated from callback or not.
CoreHandle() : CorePrivate() :
deviceBuffer(0), deviceBuffer(0),
drainCounter(0), drainCounter(0),
internalDrain(false) { internalDrain(false) {
@ -65,8 +51,11 @@ struct CoreHandle {
xrun[1] = false; xrun[1] = false;
} }
}; };
}
}
airtaudio::api::Core::Core() { airtaudio::api::Core::Core() :
m_private(new airtaudio::api::CorePrivate()) {
#if defined(AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER) #if defined(AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER)
// This is a largely undocumented but absolutely necessary // This is a largely undocumented but absolutely necessary
// requirement starting with OS-X 10.6. If not called, queries and // requirement starting with OS-X 10.6. If not called, queries and
@ -402,14 +391,14 @@ OSStatus airtaudio::api::Core::callbackEvent(AudioDeviceID _inDevice,
static OSStatus xrunListener(AudioObjectID _inDevice, static OSStatus xrunListener(AudioObjectID _inDevice,
uint32_t _nAddresses, uint32_t _nAddresses,
const AudioObjectPropertyAddress _properties[], const AudioObjectPropertyAddress _properties[],
void* _handlePointer) { void* _userData) {
CoreHandle* handle = (CoreHandle*)_handlePointer; airtaudio::api::Core* myClass = reinterpret_cast<airtaudio::api::Core*>(_userData);
for (uint32_t i=0; i<_nAddresses; i++) { for (uint32_t i=0; i<_nAddresses; i++) {
if (_properties[i].mSelector == kAudioDeviceProcessorOverload) { if (_properties[i].mSelector == kAudioDeviceProcessorOverload) {
if (_properties[i].mScope == kAudioDevicePropertyScopeInput) { if (_properties[i].mScope == kAudioDevicePropertyScopeInput) {
handle->xrun[1] = true; myClass->m_private->xrun[1] = true;
} else { } else {
handle->xrun[0] = true; myClass->m_private->xrun[0] = true;
} }
} }
} }
@ -803,21 +792,9 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device,
} else if (monoMode) { } else if (monoMode) {
m_doConvertBuffer[modeToIdTable(_mode)] = true; m_doConvertBuffer[modeToIdTable(_mode)] = true;
} }
// Allocate our CoreHandle structure for the stream. m_private->iStream[modeToIdTable(_mode)] = firstStream;
CoreHandle *handle = 0; m_private->nStreams[modeToIdTable(_mode)] = streamCount;
if (m_apiHandle == 0) { m_private->id[modeToIdTable(_mode)] = id;
handle = new CoreHandle;
if (handle == nullptr) {
ATA_ERROR("error allocating CoreHandle memory.");
return false;
}
m_apiHandle = (void *) handle;
} else {
handle = (CoreHandle *) m_apiHandle;
}
handle->iStream[modeToIdTable(_mode)] = firstStream;
handle->nStreams[modeToIdTable(_mode)] = streamCount;
handle->id[modeToIdTable(_mode)] = id;
// Allocate necessary internal buffers. // Allocate necessary internal buffers.
uint64_t bufferBytes; uint64_t bufferBytes;
bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat); bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat);
@ -832,7 +809,7 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device,
// "device buffers". However, we can't do this if using multiple // "device buffers". However, we can't do this if using multiple
// streams. // streams.
if ( m_doConvertBuffer[modeToIdTable(_mode)] if ( m_doConvertBuffer[modeToIdTable(_mode)]
&& handle->nStreams[modeToIdTable(_mode)] > 1) { && m_private->nStreams[modeToIdTable(_mode)] > 1) {
bool makeBuffer = true; bool makeBuffer = true;
bufferBytes = m_nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]); bufferBytes = m_nDeviceChannels[modeToIdTable(_mode)] * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]);
if (_mode == airtaudio::mode_input) { if (_mode == airtaudio::mode_input) {
@ -876,7 +853,7 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device,
m_mode = airtaudio::mode_duplex; m_mode = airtaudio::mode_duplex;
} else { } else {
#if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) #if defined(MAC_OS_X_VERSION_10_5) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
result = AudioDeviceCreateIOProcID(id, &airtaudio::api::Core::callbackEvent, (void *) &m_callbackInfo, &handle->procId[modeToIdTable(_mode)]); result = AudioDeviceCreateIOProcID(id, &airtaudio::api::Core::callbackEvent, (void *) &m_callbackInfo, &m_private->procId[modeToIdTable(_mode)]);
#else #else
// deprecated in favor of AudioDeviceCreateIOProcID() // deprecated in favor of AudioDeviceCreateIOProcID()
result = AudioDeviceAddIOProc(id, &airtaudio::api::Core::callbackEvent, (void *) &m_callbackInfo); result = AudioDeviceAddIOProc(id, &airtaudio::api::Core::callbackEvent, (void *) &m_callbackInfo);
@ -894,13 +871,9 @@ bool airtaudio::api::Core::probeDeviceOpen(uint32_t _device,
} }
// Setup the device property listener for over/underload. // Setup the device property listener for over/underload.
property.mSelector = kAudioDeviceProcessorOverload; property.mSelector = kAudioDeviceProcessorOverload;
result = AudioObjectAddPropertyListener(id, &property, xrunListener, (void *) handle); result = AudioObjectAddPropertyListener(id, &property, xrunListener, this);
return true; return true;
error: error:
if (handle) {
delete handle;
m_apiHandle = 0;
}
for (int32_t i=0; i<2; i++) { for (int32_t i=0; i<2; i++) {
if (m_userBuffer[i]) { if (m_userBuffer[i]) {
free(m_userBuffer[i]); free(m_userBuffer[i]);
@ -920,30 +893,29 @@ enum airtaudio::error airtaudio::api::Core::closeStream() {
ATA_ERROR("no open stream to close!"); ATA_ERROR("no open stream to close!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
CoreHandle *handle = (CoreHandle *) m_apiHandle;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
if (m_state == airtaudio::state_running) { if (m_state == airtaudio::state_running) {
AudioDeviceStop(handle->id[0], &airtaudio::api::Core::callbackEvent); AudioDeviceStop(m_private->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) #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]); AudioDeviceDestroyIOProcID(m_private->id[0], m_private->procId[0]);
#else #else
// deprecated in favor of AudioDeviceDestroyIOProcID() // deprecated in favor of AudioDeviceDestroyIOProcID()
AudioDeviceRemoveIOProc(handle->id[0], &airtaudio::api::Core::callbackEvent); AudioDeviceRemoveIOProc(m_private->id[0], &airtaudio::api::Core::callbackEvent);
#endif #endif
} }
if ( m_mode == airtaudio::mode_input if ( m_mode == airtaudio::mode_input
|| ( m_mode == airtaudio::mode_duplex || ( m_mode == airtaudio::mode_duplex
&& m_device[0] != m_device[1])) { && m_device[0] != m_device[1])) {
if (m_state == airtaudio::state_running) { if (m_state == airtaudio::state_running) {
AudioDeviceStop(handle->id[1], &airtaudio::api::Core::callbackEvent); AudioDeviceStop(m_private->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) #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]); AudioDeviceDestroyIOProcID(m_private->id[1], m_private->procId[1]);
#else #else
// deprecated in favor of AudioDeviceDestroyIOProcID() // deprecated in favor of AudioDeviceDestroyIOProcID()
AudioDeviceRemoveIOProc(handle->id[1], &airtaudio::api::Core::callbackEvent); AudioDeviceRemoveIOProc(m_private->id[1], &airtaudio::api::Core::callbackEvent);
#endif #endif
} }
for (int32_t i=0; i<2; i++) { for (int32_t i=0; i<2; i++) {
@ -956,8 +928,6 @@ enum airtaudio::error airtaudio::api::Core::closeStream() {
free(m_deviceBuffer); free(m_deviceBuffer);
m_deviceBuffer = nullptr; m_deviceBuffer = nullptr;
} }
delete handle;
m_apiHandle = 0;
m_mode = airtaudio::mode_unknow; m_mode = airtaudio::mode_unknow;
m_state = airtaudio::state_closed; m_state = airtaudio::state_closed;
return airtaudio::error_none; return airtaudio::error_none;
@ -972,10 +942,9 @@ enum airtaudio::error airtaudio::api::Core::startStream() {
return airtaudio::error_warning; return airtaudio::error_warning;
} }
OSStatus result = noErr; OSStatus result = noErr;
CoreHandle *handle = (CoreHandle *) m_apiHandle;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
result = AudioDeviceStart(handle->id[0], &airtaudio::api::Core::callbackEvent); result = AudioDeviceStart(m_private->id[0], &airtaudio::api::Core::callbackEvent);
if (result != noErr) { if (result != noErr) {
ATA_ERROR("system error (" << getErrorCode(result) << ") starting callback procedure on device (" << m_device[0] << ")."); ATA_ERROR("system error (" << getErrorCode(result) << ") starting callback procedure on device (" << m_device[0] << ").");
goto unlock; goto unlock;
@ -984,14 +953,14 @@ enum airtaudio::error airtaudio::api::Core::startStream() {
if ( m_mode == airtaudio::mode_input if ( m_mode == airtaudio::mode_input
|| ( m_mode == airtaudio::mode_duplex || ( m_mode == airtaudio::mode_duplex
&& m_device[0] != m_device[1])) { && m_device[0] != m_device[1])) {
result = AudioDeviceStart(handle->id[1], &airtaudio::api::Core::callbackEvent); result = AudioDeviceStart(m_private->id[1], &airtaudio::api::Core::callbackEvent);
if (result != noErr) { if (result != noErr) {
ATA_ERROR("system error starting input callback procedure on device (" << m_device[1] << ")."); ATA_ERROR("system error starting input callback procedure on device (" << m_device[1] << ").");
goto unlock; goto unlock;
} }
} }
handle->drainCounter = 0; m_private->drainCounter = 0;
handle->internalDrain = false; m_private->internalDrain = false;
m_state = airtaudio::state_running; m_state = airtaudio::state_running;
unlock: unlock:
if (result == noErr) { if (result == noErr) {
@ -1009,15 +978,14 @@ enum airtaudio::error airtaudio::api::Core::stopStream() {
return airtaudio::error_warning; return airtaudio::error_warning;
} }
OSStatus result = noErr; OSStatus result = noErr;
CoreHandle *handle = (CoreHandle *) m_apiHandle;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
if (handle->drainCounter == 0) { if (m_private->drainCounter == 0) {
std::unique_lock<std::mutex> lck(m_mutex); std::unique_lock<std::mutex> lck(m_mutex);
handle->drainCounter = 2; m_private->drainCounter = 2;
handle->condition.wait(lck); m_private->condition.wait(lck);
} }
result = AudioDeviceStop(handle->id[0], &airtaudio::api::Core::callbackEvent); result = AudioDeviceStop(m_private->id[0], &airtaudio::api::Core::callbackEvent);
if (result != noErr) { if (result != noErr) {
ATA_ERROR("system error (" << getErrorCode(result) << ") stopping callback procedure on device (" << m_device[0] << ")."); ATA_ERROR("system error (" << getErrorCode(result) << ") stopping callback procedure on device (" << m_device[0] << ").");
goto unlock; goto unlock;
@ -1026,7 +994,7 @@ enum airtaudio::error airtaudio::api::Core::stopStream() {
if ( m_mode == airtaudio::mode_input if ( m_mode == airtaudio::mode_input
|| ( m_mode == airtaudio::mode_duplex || ( m_mode == airtaudio::mode_duplex
&& m_device[0] != m_device[1])) { && m_device[0] != m_device[1])) {
result = AudioDeviceStop(handle->id[1], &airtaudio::api::Core::callbackEvent); result = AudioDeviceStop(m_private->id[1], &airtaudio::api::Core::callbackEvent);
if (result != noErr) { if (result != noErr) {
ATA_ERROR("system error (" << getErrorCode(result) << ") stopping input callback procedure on device (" << m_device[1] << ")."); ATA_ERROR("system error (" << getErrorCode(result) << ") stopping input callback procedure on device (" << m_device[1] << ").");
goto unlock; goto unlock;
@ -1048,8 +1016,7 @@ enum airtaudio::error airtaudio::api::Core::abortStream() {
ATA_ERROR("the stream is already stopped!"); ATA_ERROR("the stream is already stopped!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
CoreHandle* handle = (CoreHandle*)m_apiHandle; m_private->drainCounter = 2;
handle->drainCounter = 2;
return stopStream(); return stopStream();
} }
@ -1075,34 +1042,33 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
return false; return false;
} }
CallbackInfo *info = (CallbackInfo *) &m_callbackInfo; CallbackInfo *info = (CallbackInfo *) &m_callbackInfo;
CoreHandle *handle = (CoreHandle *) m_apiHandle;
// Check if we were draining the stream and signal is finished. // Check if we were draining the stream and signal is finished.
if (handle->drainCounter > 3) { if (m_private->drainCounter > 3) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;
if (handle->internalDrain == true) { if (m_private->internalDrain == true) {
new std::thread(&airtaudio::api::Core::coreStopStream, this); new std::thread(&airtaudio::api::Core::coreStopStream, this);
} else { } else {
// external call to stopStream() // external call to stopStream()
handle->condition.notify_one(); m_private->condition.notify_one();
} }
return true; return true;
} }
AudioDeviceID outputDevice = handle->id[0]; AudioDeviceID outputDevice = m_private->id[0];
// Invoke user callback to get fresh output data UNLESS we are // Invoke user callback to get fresh output data UNLESS we are
// draining stream or duplex mode AND the input/output devices are // draining stream or duplex mode AND the input/output devices are
// different AND this function is called for the input device. // different AND this function is called for the input device.
if (handle->drainCounter == 0 && (m_mode != airtaudio::mode_duplex || _deviceId == outputDevice)) { if (m_private->drainCounter == 0 && (m_mode != airtaudio::mode_duplex || _deviceId == outputDevice)) {
double streamTime = getStreamTime(); double streamTime = getStreamTime();
enum airtaudio::status status = airtaudio::status_ok; enum airtaudio::status status = airtaudio::status_ok;
if ( m_mode != airtaudio::mode_input if ( m_mode != airtaudio::mode_input
&& handle->xrun[0] == true) { && m_private->xrun[0] == true) {
status |= airtaudio::status_underflow; status |= airtaudio::status_underflow;
handle->xrun[0] = false; m_private->xrun[0] = false;
} }
if ( m_mode != airtaudio::mode_output if ( m_mode != airtaudio::mode_output
&& handle->xrun[1] == true) { && m_private->xrun[1] == true) {
status |= airtaudio::mode_input_OVERFLOW; status |= airtaudio::mode_input_OVERFLOW;
handle->xrun[1] = false; m_private->xrun[1] = false;
} }
int32_t cbReturnValue = info->callback(m_userBuffer[0], int32_t cbReturnValue = info->callback(m_userBuffer[0],
m_userBuffer[1], m_userBuffer[1],
@ -1111,42 +1077,42 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
status); status);
if (cbReturnValue == 2) { if (cbReturnValue == 2) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;
handle->drainCounter = 2; m_private->drainCounter = 2;
abortStream(); abortStream();
return true; return true;
} else if (cbReturnValue == 1) { } else if (cbReturnValue == 1) {
handle->drainCounter = 1; m_private->drainCounter = 1;
handle->internalDrain = true; m_private->internalDrain = true;
} }
} }
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| ( m_mode == airtaudio::mode_duplex || ( m_mode == airtaudio::mode_duplex
&& _deviceId == outputDevice)) { && _deviceId == outputDevice)) {
if (handle->drainCounter > 1) { if (m_private->drainCounter > 1) {
// write zeros to the output stream // write zeros to the output stream
if (handle->nStreams[0] == 1) { if (m_private->nStreams[0] == 1) {
memset(_outBufferList->mBuffers[handle->iStream[0]].mData, memset(_outBufferList->mBuffers[m_private->iStream[0]].mData,
0, 0,
_outBufferList->mBuffers[handle->iStream[0]].mDataByteSize); _outBufferList->mBuffers[m_private->iStream[0]].mDataByteSize);
} else { } else {
// fill multiple streams with zeros // fill multiple streams with zeros
for (uint32_t i=0; i<handle->nStreams[0]; i++) { for (uint32_t i=0; i<m_private->nStreams[0]; i++) {
memset(_outBufferList->mBuffers[handle->iStream[0]+i].mData, memset(_outBufferList->mBuffers[m_private->iStream[0]+i].mData,
0, 0,
_outBufferList->mBuffers[handle->iStream[0]+i].mDataByteSize); _outBufferList->mBuffers[m_private->iStream[0]+i].mDataByteSize);
} }
} }
} else if (handle->nStreams[0] == 1) { } else if (m_private->nStreams[0] == 1) {
if (m_doConvertBuffer[0]) { if (m_doConvertBuffer[0]) {
// convert directly to CoreAudio stream buffer // convert directly to CoreAudio stream buffer
convertBuffer((char*)_outBufferList->mBuffers[handle->iStream[0]].mData, convertBuffer((char*)_outBufferList->mBuffers[m_private->iStream[0]].mData,
m_userBuffer[0], m_userBuffer[0],
m_convertInfo[0]); m_convertInfo[0]);
} else { } else {
// copy from user buffer // copy from user buffer
memcpy(_outBufferList->mBuffers[handle->iStream[0]].mData, memcpy(_outBufferList->mBuffers[m_private->iStream[0]].mData,
m_userBuffer[0], m_userBuffer[0],
_outBufferList->mBuffers[handle->iStream[0]].mDataByteSize); _outBufferList->mBuffers[m_private->iStream[0]].mDataByteSize);
} }
} else { } else {
// fill multiple streams // fill multiple streams
@ -1156,9 +1122,9 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
inBuffer = (float *) m_deviceBuffer; inBuffer = (float *) m_deviceBuffer;
} }
if (m_deviceInterleaved[0] == false) { // mono mode if (m_deviceInterleaved[0] == false) { // mono mode
uint32_t bufferBytes = _outBufferList->mBuffers[handle->iStream[0]].mDataByteSize; uint32_t bufferBytes = _outBufferList->mBuffers[m_private->iStream[0]].mDataByteSize;
for (uint32_t i=0; i<m_nUserChannels[0]; i++) { for (uint32_t i=0; i<m_nUserChannels[0]; i++) {
memcpy(_outBufferList->mBuffers[handle->iStream[0]+i].mData, memcpy(_outBufferList->mBuffers[m_private->iStream[0]+i].mData,
(void *)&inBuffer[i*m_bufferSize], (void *)&inBuffer[i*m_bufferSize],
bufferBytes); bufferBytes);
} }
@ -1178,10 +1144,10 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
inOffset = m_bufferSize; inOffset = m_bufferSize;
} }
channelsLeft = inChannels; channelsLeft = inChannels;
for (uint32_t i=0; i<handle->nStreams[0]; i++) { for (uint32_t i=0; i<m_private->nStreams[0]; i++) {
in = inBuffer; in = inBuffer;
out = (float *) _outBufferList->mBuffers[handle->iStream[0]+i].mData; out = (float *) _outBufferList->mBuffers[m_private->iStream[0]+i].mData;
streamChannels = _outBufferList->mBuffers[handle->iStream[0]+i].mNumberChannels; streamChannels = _outBufferList->mBuffers[m_private->iStream[0]+i].mNumberChannels;
outJump = 0; outJump = 0;
// Account for possible channel offset in first stream // Account for possible channel offset in first stream
if (i == 0 && m_channelOffset[0] > 0) { if (i == 0 && m_channelOffset[0] > 0) {
@ -1213,26 +1179,26 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
} }
} }
} }
if (handle->drainCounter) { if (m_private->drainCounter) {
handle->drainCounter++; m_private->drainCounter++;
goto unlock; goto unlock;
} }
} }
AudioDeviceID inputDevice; AudioDeviceID inputDevice;
inputDevice = handle->id[1]; inputDevice = m_private->id[1];
if ( m_mode == airtaudio::mode_input if ( m_mode == airtaudio::mode_input
|| ( m_mode == airtaudio::mode_duplex || ( m_mode == airtaudio::mode_duplex
&& _deviceId == inputDevice)) { && _deviceId == inputDevice)) {
if (handle->nStreams[1] == 1) { if (m_private->nStreams[1] == 1) {
if (m_doConvertBuffer[1]) { if (m_doConvertBuffer[1]) {
// convert directly from CoreAudio stream buffer // convert directly from CoreAudio stream buffer
convertBuffer(m_userBuffer[1], convertBuffer(m_userBuffer[1],
(char *) _inBufferList->mBuffers[handle->iStream[1]].mData, (char *) _inBufferList->mBuffers[m_private->iStream[1]].mData,
m_convertInfo[1]); m_convertInfo[1]);
} else { // copy to user buffer } else { // copy to user buffer
memcpy(m_userBuffer[1], memcpy(m_userBuffer[1],
_inBufferList->mBuffers[handle->iStream[1]].mData, _inBufferList->mBuffers[m_private->iStream[1]].mData,
_inBufferList->mBuffers[handle->iStream[1]].mDataByteSize); _inBufferList->mBuffers[m_private->iStream[1]].mDataByteSize);
} }
} else { // read from multiple streams } else { // read from multiple streams
float *outBuffer = (float *) m_userBuffer[1]; float *outBuffer = (float *) m_userBuffer[1];
@ -1241,10 +1207,10 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
} }
if (m_deviceInterleaved[1] == false) { if (m_deviceInterleaved[1] == false) {
// mono mode // mono mode
uint32_t bufferBytes = _inBufferList->mBuffers[handle->iStream[1]].mDataByteSize; uint32_t bufferBytes = _inBufferList->mBuffers[m_private->iStream[1]].mDataByteSize;
for (uint32_t i=0; i<m_nUserChannels[1]; i++) { for (uint32_t i=0; i<m_nUserChannels[1]; i++) {
memcpy((void *)&outBuffer[i*m_bufferSize], memcpy((void *)&outBuffer[i*m_bufferSize],
_inBufferList->mBuffers[handle->iStream[1]+i].mData, _inBufferList->mBuffers[m_private->iStream[1]+i].mData,
bufferBytes); bufferBytes);
} }
} else { } else {
@ -1263,10 +1229,10 @@ bool airtaudio::api::Core::callbackEvent(AudioDeviceID _deviceId,
outOffset = m_bufferSize; outOffset = m_bufferSize;
} }
channelsLeft = outChannels; channelsLeft = outChannels;
for (uint32_t i=0; i<handle->nStreams[1]; i++) { for (uint32_t i=0; i<m_private->nStreams[1]; i++) {
out = outBuffer; out = outBuffer;
in = (float *) _inBufferList->mBuffers[handle->iStream[1]+i].mData; in = (float *) _inBufferList->mBuffers[m_private->iStream[1]+i].mData;
streamChannels = _inBufferList->mBuffers[handle->iStream[1]+i].mNumberChannels; streamChannels = _inBufferList->mBuffers[m_private->iStream[1]+i].mNumberChannels;
inJump = 0; inJump = 0;
// Account for possible channel offset in first stream // Account for possible channel offset in first stream
if (i == 0 && m_channelOffset[1] > 0) { if (i == 0 && m_channelOffset[1] > 0) {

View File

@ -12,6 +12,7 @@
namespace airtaudio { namespace airtaudio {
namespace api { namespace api {
class CorePrivate;
class Core: public airtaudio::Api { class Core: public airtaudio::Api {
public: public:
static airtaudio::Api* Create(); static airtaudio::Api* Create();
@ -46,6 +47,7 @@ namespace airtaudio {
void* _infoPointer); void* _infoPointer);
void coreStopStream(void *_userData); void coreStopStream(void *_userData);
private: private:
std::unique_ptr<CorePrivate> m_private;
bool probeDeviceOpen(uint32_t _device, bool probeDeviceOpen(uint32_t _device,
airtaudio::mode _mode, airtaudio::mode _mode,
uint32_t _channels, uint32_t _channels,

View File

@ -46,7 +46,7 @@ namespace airtaudio {
void callBackEvent(void* _data, void callBackEvent(void* _data,
int32_t _frameRate); int32_t _frameRate);
private: private:
CoreIosPrivate* m_private; std::unique_ptr<CoreIosPrivate> m_private;
static OSStatus playbackCallback(void *_userData, static OSStatus playbackCallback(void *_userData,
AudioUnitRenderActionFlags* _ioActionFlags, AudioUnitRenderActionFlags* _ioActionFlags,
const AudioTimeStamp* _inTimeStamp, const AudioTimeStamp* _inTimeStamp,

View File

@ -38,7 +38,7 @@ namespace airtaudio {
airtaudio::api::CoreIos::CoreIos(void) : airtaudio::api::CoreIos::CoreIos(void) :
m_private(new airtaudio::api::CoreIosPrivate) { m_private(new airtaudio::api::CoreIosPrivate()) {
ATA_INFO("new CoreIos"); ATA_INFO("new CoreIos");
int32_t deviceCount = 2; int32_t deviceCount = 2;
ATA_ERROR("Get count devices : " << 2); ATA_ERROR("Get count devices : " << 2);

View File

@ -56,9 +56,11 @@ static inline DWORD dsPointerBetween(DWORD _pointer, DWORD _laterPointer, DWORD
return _pointer >= _earlierPointer && _pointer < _laterPointer; return _pointer >= _earlierPointer && _pointer < _laterPointer;
} }
// A structure to hold various information related to the DirectSound
// API implementation. namespace airtaudio {
struct DsHandle { namespace api {
class DsPrivate {
public:
uint32_t drainCounter; // Tracks callback counts when draining uint32_t drainCounter; // Tracks callback counts when draining
bool internalDrain; // Indicates if stop is initiated from callback or not. bool internalDrain; // Indicates if stop is initiated from callback or not.
void *id[2]; void *id[2];
@ -69,7 +71,7 @@ struct DsHandle {
DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by. DWORD dsPointerLeadTime[2]; // the number of bytes ahead of the safe pointer to lead by.
HANDLE condition; HANDLE condition;
DsHandle() : DsPrivate() :
drainCounter(0), drainCounter(0),
internalDrain(false) { internalDrain(false) {
id[0] = 0; id[0] = 0;
@ -82,6 +84,8 @@ struct DsHandle {
bufferPointer[1] = 0; bufferPointer[1] = 0;
} }
}; };
}
}
// Declarations for utility functions, callbacks, and structures // Declarations for utility functions, callbacks, and structures
// specific to the DirectSound implementation. // specific to the DirectSound implementation.
@ -111,7 +115,8 @@ struct DsProbeData {
std::vector<struct DsDevice>* dsDevices; std::vector<struct DsDevice>* dsDevices;
}; };
airtaudio::api::Ds::Ds() { airtaudio::api::Ds::Ds() :
m_private(new airtaudio::api::DsPrivate()) {
// Dsound will run both-threaded. If CoInitialize fails, then just // Dsound will run both-threaded. If CoInitialize fails, then just
// accept whatever the mainline chose for a threading model. // accept whatever the mainline chose for a threading model.
m_coInitialized = false; m_coInitialized = false;
@ -755,26 +760,15 @@ bool airtaudio::api::Ds::probeDeviceOpen(uint32_t _device,
} }
} }
} }
// Allocate our DsHandle structures for the stream.
if (m_apiHandle == 0) {
handle = new DsHandle;
if (handle == nullptr) {
ATA_ERROR("error allocating AsioHandle memory.");
goto error;
}
// Create a manual-reset event. // Create a manual-reset event.
handle->condition = CreateEvent(nullptr, // no security m_private->condition = CreateEvent(nullptr, // no security
TRUE, // manual-reset TRUE, // manual-reset
FALSE, // non-signaled initially FALSE, // non-signaled initially
nullptr); // unnamed nullptr); // unnamed
m_apiHandle = (void *) handle; m_private->id[modeToIdTable(_mode)] = ohandle;
} else { m_private->buffer[modeToIdTable(_mode)] = bhandle;
handle = (DsHandle *) m_apiHandle; m_private->dsBufferSize[modeToIdTable(_mode)] = dsBufferSize;
} m_private->dsPointerLeadTime[modeToIdTable(_mode)] = dsPointerLeadTime;
handle->id[modeToIdTable(_mode)] = ohandle;
handle->buffer[modeToIdTable(_mode)] = bhandle;
handle->dsBufferSize[modeToIdTable(_mode)] = dsBufferSize;
handle->dsPointerLeadTime[modeToIdTable(_mode)] = dsPointerLeadTime;
m_device[modeToIdTable(_mode)] = _device; m_device[modeToIdTable(_mode)] = _device;
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
@ -810,27 +804,23 @@ bool airtaudio::api::Ds::probeDeviceOpen(uint32_t _device,
} }
return true; return true;
error: error:
if (handle) { if (m_private->buffer[0]) {
if (handle->buffer[0]) {
// the object pointer can be nullptr and valid // the object pointer can be nullptr and valid
LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0]; LPDIRECTSOUND object = (LPDIRECTSOUND) m_private->id[0];
LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
if (buffer) { if (buffer) {
buffer->Release(); buffer->Release();
} }
object->Release(); object->Release();
} }
if (handle->buffer[1]) { if (m_private->buffer[1]) {
LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1]; LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) m_private->id[1];
LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1];
if (buffer != nullptr) { if (buffer != nullptr) {
buffer->Release(); buffer->Release();
} }
} }
CloseHandle(handle->condition); CloseHandle(m_private->condition);
delete handle;
m_apiHandle = 0;
}
for (int32_t i=0; i<2; i++) { for (int32_t i=0; i<2; i++) {
if (m_userBuffer[i]) { if (m_userBuffer[i]) {
free(m_userBuffer[i]); free(m_userBuffer[i]);
@ -854,30 +844,25 @@ enum airtaudio::error airtaudio::api::Ds::closeStream() {
m_callbackInfo.isRunning = false; m_callbackInfo.isRunning = false;
WaitForSingleObject((HANDLE) m_callbackInfo.thread, INFINITE); WaitForSingleObject((HANDLE) m_callbackInfo.thread, INFINITE);
CloseHandle((HANDLE) m_callbackInfo.thread); CloseHandle((HANDLE) m_callbackInfo.thread);
DsHandle *handle = (DsHandle *) m_apiHandle; if (m_private->buffer[0]) { // the object pointer can be nullptr and valid
if (handle) { LPDIRECTSOUND object = (LPDIRECTSOUND) m_private->id[0];
if (handle->buffer[0]) { // the object pointer can be nullptr and valid LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
LPDIRECTSOUND object = (LPDIRECTSOUND) handle->id[0];
LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0];
if (buffer) { if (buffer) {
buffer->Stop(); buffer->Stop();
buffer->Release(); buffer->Release();
} }
object->Release(); object->Release();
} }
if (handle->buffer[1]) { if (m_private->buffer[1]) {
LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) handle->id[1]; LPDIRECTSOUNDCAPTURE object = (LPDIRECTSOUNDCAPTURE) m_private->id[1];
LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1];
if (buffer) { if (buffer) {
buffer->Stop(); buffer->Stop();
buffer->Release(); buffer->Release();
} }
object->Release(); object->Release();
} }
CloseHandle(handle->condition); CloseHandle(m_private->condition);
delete handle;
m_apiHandle = 0;
}
for (int32_t i=0; i<2; i++) { for (int32_t i=0; i<2; i++) {
if (m_userBuffer[i]) { if (m_userBuffer[i]) {
free(m_userBuffer[i]); free(m_userBuffer[i]);
@ -900,7 +885,6 @@ enum airtaudio::error airtaudio::api::Ds::startStream() {
ATA_ERROR("the stream is already running!"); ATA_ERROR("the stream is already running!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
DsHandle *handle = (DsHandle *) m_apiHandle;
// Increase scheduler frequency on lesser windows (a side-effect of // Increase scheduler frequency on lesser windows (a side-effect of
// increasing timer accuracy). On greater windows (Win2K or later), // increasing timer accuracy). On greater windows (Win2K or later),
// this is already in effect. // this is already in effect.
@ -914,7 +898,7 @@ enum airtaudio::error airtaudio::api::Ds::startStream() {
HRESULT result = 0; HRESULT result = 0;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
result = buffer->Play(0, 0, DSBPLAY_LOOPING); result = buffer->Play(0, 0, DSBPLAY_LOOPING);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") starting output buffer!"); ATA_ERROR("error (" << getErrorString(result) << ") starting output buffer!");
@ -923,16 +907,16 @@ enum airtaudio::error airtaudio::api::Ds::startStream() {
} }
if ( m_mode == airtaudio::mode_input if ( m_mode == airtaudio::mode_input
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1];
result = buffer->Start(DSCBSTART_LOOPING); result = buffer->Start(DSCBSTART_LOOPING);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") starting input buffer!"); ATA_ERROR("error (" << getErrorString(result) << ") starting input buffer!");
goto unlock; goto unlock;
} }
} }
handle->drainCounter = 0; m_private->drainCounter = 0;
handle->internalDrain = false; m_private->internalDrain = false;
ResetEvent(handle->condition); ResetEvent(m_private->condition);
m_state = airtaudio::state_running; m_state = airtaudio::state_running;
unlock: unlock:
if (FAILED(result)) { if (FAILED(result)) {
@ -952,16 +936,15 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() {
HRESULT result = 0; HRESULT result = 0;
LPVOID audioPtr; LPVOID audioPtr;
DWORD dataLen; DWORD dataLen;
DsHandle *handle = (DsHandle *) m_apiHandle;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
if (handle->drainCounter == 0) { if (m_private->drainCounter == 0) {
handle->drainCounter = 2; m_private->drainCounter = 2;
WaitForSingleObject(handle->condition, INFINITE); // block until signaled WaitForSingleObject(m_private->condition, INFINITE); // block until signaled
} }
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
// Stop the buffer and clear memory // Stop the buffer and clear memory
LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; LPDIRECTSOUNDBUFFER buffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
result = buffer->Stop(); result = buffer->Stop();
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") stopping output buffer!"); ATA_ERROR("error (" << getErrorString(result) << ") stopping output buffer!");
@ -969,7 +952,7 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() {
} }
// Lock the buffer and clear it so that if we start to play again, // Lock the buffer and clear it so that if we start to play again,
// we won't have old data playing. // we won't have old data playing.
result = buffer->Lock(0, handle->dsBufferSize[0], &audioPtr, &dataLen, nullptr, nullptr, 0); result = buffer->Lock(0, m_private->dsBufferSize[0], &audioPtr, &dataLen, nullptr, nullptr, 0);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") locking output buffer!"); ATA_ERROR("error (" << getErrorString(result) << ") locking output buffer!");
goto unlock; goto unlock;
@ -983,11 +966,11 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() {
goto unlock; goto unlock;
} }
// If we start playing again, we must begin at beginning of buffer. // If we start playing again, we must begin at beginning of buffer.
handle->bufferPointer[0] = 0; m_private->bufferPointer[0] = 0;
} }
if ( m_mode == airtaudio::mode_input if ( m_mode == airtaudio::mode_input
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; LPDIRECTSOUNDCAPTUREBUFFER buffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1];
audioPtr = nullptr; audioPtr = nullptr;
dataLen = 0; dataLen = 0;
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
@ -998,7 +981,7 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() {
} }
// Lock the buffer and clear it so that if we start to play again, // Lock the buffer and clear it so that if we start to play again,
// we won't have old data playing. // we won't have old data playing.
result = buffer->Lock(0, handle->dsBufferSize[1], &audioPtr, &dataLen, nullptr, nullptr, 0); result = buffer->Lock(0, m_private->dsBufferSize[1], &audioPtr, &dataLen, nullptr, nullptr, 0);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") locking input buffer!"); ATA_ERROR("error (" << getErrorString(result) << ") locking input buffer!");
goto unlock; goto unlock;
@ -1012,7 +995,7 @@ enum airtaudio::error airtaudio::api::Ds::stopStream() {
goto unlock; goto unlock;
} }
// If we start recording again, we must begin at beginning of buffer. // If we start recording again, we must begin at beginning of buffer.
handle->bufferPointer[1] = 0; m_private->bufferPointer[1] = 0;
} }
unlock: unlock:
timeEndPeriod(1); // revert to normal scheduler frequency on lesser windows. timeEndPeriod(1); // revert to normal scheduler frequency on lesser windows.
@ -1030,8 +1013,7 @@ enum airtaudio::error airtaudio::api::Ds::abortStream() {
ATA_ERROR("the stream is already stopped!"); ATA_ERROR("the stream is already stopped!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
DsHandle *handle = (DsHandle *) m_apiHandle; m_private->drainCounter = 2;
handle->drainCounter = 2;
return stopStream(); return stopStream();
} }
@ -1045,12 +1027,11 @@ void airtaudio::api::Ds::callbackEvent() {
return; return;
} }
CallbackInfo *info = (CallbackInfo *) &m_callbackInfo; CallbackInfo *info = (CallbackInfo *) &m_callbackInfo;
DsHandle *handle = (DsHandle *) m_apiHandle;
// Check if we were draining the stream and signal is finished. // Check if we were draining the stream and signal is finished.
if (handle->drainCounter > m_nBuffers + 2) { if (m_private->drainCounter > m_nBuffers + 2) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;
if (handle->internalDrain == false) { if (m_private->internalDrain == false) {
SetEvent(handle->condition); SetEvent(m_private->condition);
} else { } else {
stopStream(); stopStream();
} }
@ -1058,18 +1039,18 @@ void airtaudio::api::Ds::callbackEvent() {
} }
// Invoke user callback to get fresh output data UNLESS we are // Invoke user callback to get fresh output data UNLESS we are
// draining stream. // draining stream.
if (handle->drainCounter == 0) { if (m_private->drainCounter == 0) {
double streamTime = getStreamTime(); double streamTime = getStreamTime();
rtaudio::streamStatus status = 0; rtaudio::streamStatus status = 0;
if ( m_mode != airtaudio::mode_input if ( m_mode != airtaudio::mode_input
&& handle->xrun[0] == true) { && m_private->xrun[0] == true) {
status |= RTAUDIO_airtaudio::status_underflow; status |= RTAUDIO_airtaudio::status_underflow;
handle->xrun[0] = false; m_private->xrun[0] = false;
} }
if ( m_mode != airtaudio::mode_output if ( m_mode != airtaudio::mode_output
&& handle->xrun[1] == true) { && m_private->xrun[1] == true) {
status |= RTAUDIO_airtaudio::mode_input_OVERFLOW; status |= RTAUDIO_airtaudio::mode_input_OVERFLOW;
handle->xrun[1] = false; m_private->xrun[1] = false;
} }
int32_t cbReturnValue = info->callback(m_userBuffer[0], int32_t cbReturnValue = info->callback(m_userBuffer[0],
m_userBuffer[1], m_userBuffer[1],
@ -1078,12 +1059,12 @@ void airtaudio::api::Ds::callbackEvent() {
status); status);
if (cbReturnValue == 2) { if (cbReturnValue == 2) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;
handle->drainCounter = 2; m_private->drainCounter = 2;
abortStream(); abortStream();
return; return;
} else if (cbReturnValue == 1) { } else if (cbReturnValue == 1) {
handle->drainCounter = 1; m_private->drainCounter = 1;
handle->internalDrain = true; m_private->internalDrain = true;
} }
} }
HRESULT result; HRESULT result;
@ -1098,7 +1079,7 @@ void airtaudio::api::Ds::callbackEvent() {
long bufferBytes; long bufferBytes;
if (m_buffersRolling == false) { if (m_buffersRolling == false) {
if (m_mode == airtaudio::mode_duplex) { if (m_mode == airtaudio::mode_duplex) {
//assert(handle->dsBufferSize[0] == handle->dsBufferSize[1]); //assert(m_private->dsBufferSize[0] == m_private->dsBufferSize[1]);
// It takes a while for the devices to get rolling. As a result, // It takes a while for the devices to get rolling. As a result,
// there's no guarantee that the capture and write device pointers // there's no guarantee that the capture and write device pointers
// will move in lockstep. Wait here for both devices to start // will move in lockstep. Wait here for both devices to start
@ -1111,8 +1092,8 @@ void airtaudio::api::Ds::callbackEvent() {
// Realtime priority, maybe; but I'm not sure what priority the // Realtime priority, maybe; but I'm not sure what priority the
// DirectSound service threads run at. We *should* be roughly // DirectSound service threads run at. We *should* be roughly
// within a ms or so of correct. // within a ms or so of correct.
LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; LPDIRECTSOUNDCAPTUREBUFFER dsCaptureBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1];
DWORD startSafeWritePointer, startSafeReadPointer; DWORD startSafeWritePointer, startSafeReadPointer;
result = dsWriteBuffer->GetCurrentPosition(nullptr, &startSafeWritePointer); result = dsWriteBuffer->GetCurrentPosition(nullptr, &startSafeWritePointer);
if (FAILED(result)) { if (FAILED(result)) {
@ -1141,31 +1122,31 @@ void airtaudio::api::Ds::callbackEvent() {
} }
Sleep(1); Sleep(1);
} }
//assert(handle->dsBufferSize[0] == handle->dsBufferSize[1]); //assert(m_private->dsBufferSize[0] == m_private->dsBufferSize[1]);
handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; m_private->bufferPointer[0] = safeWritePointer + m_private->dsPointerLeadTime[0];
if (handle->bufferPointer[0] >= handle->dsBufferSize[0]) { if (m_private->bufferPointer[0] >= m_private->dsBufferSize[0]) {
handle->bufferPointer[0] -= handle->dsBufferSize[0]; m_private->bufferPointer[0] -= m_private->dsBufferSize[0];
} }
handle->bufferPointer[1] = safeReadPointer; m_private->bufferPointer[1] = safeReadPointer;
} else if (m_mode == airtaudio::mode_output) { } else if (m_mode == airtaudio::mode_output) {
// Set the proper nextWritePosition after initial startup. // Set the proper nextWritePosition after initial startup.
LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; LPDIRECTSOUNDBUFFER dsWriteBuffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
result = dsWriteBuffer->GetCurrentPosition(&currentWritePointer, &safeWritePointer); result = dsWriteBuffer->GetCurrentPosition(&currentWritePointer, &safeWritePointer);
if (FAILED(result)) { if (FAILED(result)) {
ATA_ERROR("error (" << getErrorString(result) << ") getting current write position!"); ATA_ERROR("error (" << getErrorString(result) << ") getting current write position!");
return; return;
} }
handle->bufferPointer[0] = safeWritePointer + handle->dsPointerLeadTime[0]; m_private->bufferPointer[0] = safeWritePointer + m_private->dsPointerLeadTime[0];
if (handle->bufferPointer[0] >= handle->dsBufferSize[0]) { if (m_private->bufferPointer[0] >= m_private->dsBufferSize[0]) {
handle->bufferPointer[0] -= handle->dsBufferSize[0]; m_private->bufferPointer[0] -= m_private->dsBufferSize[0];
} }
} }
m_buffersRolling = true; m_buffersRolling = true;
} }
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) handle->buffer[0]; LPDIRECTSOUNDBUFFER dsBuffer = (LPDIRECTSOUNDBUFFER) m_private->buffer[0];
if (handle->drainCounter > 1) { // write zeros to the output stream if (m_private->drainCounter > 1) { // write zeros to the output stream
bufferBytes = m_bufferSize * m_nUserChannels[0]; bufferBytes = m_bufferSize * m_nUserChannels[0];
bufferBytes *= audio::getFormatBytes(m_userFormat); bufferBytes *= audio::getFormatBytes(m_userFormat);
memset(m_userBuffer[0], 0, bufferBytes); memset(m_userBuffer[0], 0, bufferBytes);
@ -1190,8 +1171,8 @@ void airtaudio::api::Ds::callbackEvent() {
buffer[i] = (unsigned char) (buffer[i] + 128); buffer[i] = (unsigned char) (buffer[i] + 128);
} }
} }
DWORD dsBufferSize = handle->dsBufferSize[0]; DWORD dsBufferSize = m_private->dsBufferSize[0];
nextWritePointer = handle->bufferPointer[0]; nextWritePointer = m_private->bufferPointer[0];
DWORD endWrite, leadPointer; DWORD endWrite, leadPointer;
while (true) { while (true) {
// Find out where the read and "safe write" pointers are. // Find out where the read and "safe write" pointers are.
@ -1203,7 +1184,7 @@ void airtaudio::api::Ds::callbackEvent() {
// We will copy our output buffer into the region between // We will copy our output buffer into the region between
// safeWritePointer and leadPointer. If leadPointer is not // safeWritePointer and leadPointer. If leadPointer is not
// beyond the next endWrite position, wait until it is. // beyond the next endWrite position, wait until it is.
leadPointer = safeWritePointer + handle->dsPointerLeadTime[0]; leadPointer = safeWritePointer + m_private->dsPointerLeadTime[0];
//std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl; //std::cout << "safeWritePointer = " << safeWritePointer << ", leadPointer = " << leadPointer << ", nextWritePointer = " << nextWritePointer << std::endl;
if (leadPointer > dsBufferSize) { if (leadPointer > dsBufferSize) {
leadPointer -= dsBufferSize; leadPointer -= dsBufferSize;
@ -1229,12 +1210,12 @@ void airtaudio::api::Ds::callbackEvent() {
if ( dsPointerBetween(nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize) if ( dsPointerBetween(nextWritePointer, safeWritePointer, currentWritePointer, dsBufferSize)
|| dsPointerBetween(endWrite, safeWritePointer, currentWritePointer, dsBufferSize)) { || dsPointerBetween(endWrite, safeWritePointer, currentWritePointer, dsBufferSize)) {
// We've strayed into the forbidden zone ... resync the read pointer. // We've strayed into the forbidden zone ... resync the read pointer.
handle->xrun[0] = true; m_private->xrun[0] = true;
nextWritePointer = safeWritePointer + handle->dsPointerLeadTime[0] - bufferBytes; nextWritePointer = safeWritePointer + m_private->dsPointerLeadTime[0] - bufferBytes;
if (nextWritePointer >= dsBufferSize) { if (nextWritePointer >= dsBufferSize) {
nextWritePointer -= dsBufferSize; nextWritePointer -= dsBufferSize;
} }
handle->bufferPointer[0] = nextWritePointer; m_private->bufferPointer[0] = nextWritePointer;
endWrite = nextWritePointer + bufferBytes; endWrite = nextWritePointer + bufferBytes;
} }
// Lock free space in the buffer // Lock free space in the buffer
@ -1261,9 +1242,9 @@ void airtaudio::api::Ds::callbackEvent() {
return; return;
} }
nextWritePointer = (nextWritePointer + bufferSize1 + bufferSize2) % dsBufferSize; nextWritePointer = (nextWritePointer + bufferSize1 + bufferSize2) % dsBufferSize;
handle->bufferPointer[0] = nextWritePointer; m_private->bufferPointer[0] = nextWritePointer;
if (handle->drainCounter) { if (m_private->drainCounter) {
handle->drainCounter++; m_private->drainCounter++;
goto unlock; goto unlock;
} }
} }
@ -1279,9 +1260,9 @@ void airtaudio::api::Ds::callbackEvent() {
bufferBytes = m_bufferSize * m_nUserChannels[1]; bufferBytes = m_bufferSize * m_nUserChannels[1];
bufferBytes *= audio::getFormatBytes(m_userFormat); bufferBytes *= audio::getFormatBytes(m_userFormat);
} }
LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) handle->buffer[1]; LPDIRECTSOUNDCAPTUREBUFFER dsBuffer = (LPDIRECTSOUNDCAPTUREBUFFER) m_private->buffer[1];
long nextReadPointer = handle->bufferPointer[1]; long nextReadPointer = m_private->bufferPointer[1];
DWORD dsBufferSize = handle->dsBufferSize[1]; DWORD dsBufferSize = m_private->dsBufferSize[1];
// Find out where the write and "safe read" pointers are. // Find out where the write and "safe read" pointers are.
result = dsBuffer->GetCurrentPosition(&currentReadPointer, &safeReadPointer); result = dsBuffer->GetCurrentPosition(&currentReadPointer, &safeReadPointer);
if (FAILED(result)) { if (FAILED(result)) {
@ -1310,7 +1291,7 @@ void airtaudio::api::Ds::callbackEvent() {
if (m_duplexPrerollBytes <= 0) { if (m_duplexPrerollBytes <= 0) {
// Pre-roll time over. Be more agressive. // Pre-roll time over. Be more agressive.
int32_t adjustment = endRead-safeReadPointer; int32_t adjustment = endRead-safeReadPointer;
handle->xrun[1] = true; m_private->xrun[1] = true;
// Two cases: // Two cases:
// - large adjustments: we've probably run out of CPU cycles, so just resync exactly, // - large adjustments: we've probably run out of CPU cycles, so just resync exactly,
// and perform fine adjustments later. // and perform fine adjustments later.
@ -1386,7 +1367,7 @@ void airtaudio::api::Ds::callbackEvent() {
ATA_ERROR("error (" << getErrorString(result) << ") unlocking capture buffer!"); ATA_ERROR("error (" << getErrorString(result) << ") unlocking capture buffer!");
return; return;
} }
handle->bufferPointer[1] = nextReadPointer; m_private->bufferPointer[1] = nextReadPointer;
// No byte swapping necessary in DirectSound implementation. // No byte swapping necessary in DirectSound implementation.
// If necessary, convert 8-bit data from unsigned to signed. // If necessary, convert 8-bit data from unsigned to signed.
if (m_deviceFormat[1] == RTAUDIO_SINT8) { if (m_deviceFormat[1] == RTAUDIO_SINT8) {

View File

@ -10,6 +10,7 @@
namespace airtaudio { namespace airtaudio {
namespace api { namespace api {
class DsPrivate;
class Ds: public airtaudio::Api { class Ds: public airtaudio::Api {
public: public:
static airtaudio::Api* Create(); static airtaudio::Api* Create();
@ -34,6 +35,7 @@ namespace airtaudio {
// will most likely produce highly undesireable results! // will most likely produce highly undesireable results!
void callbackEvent(); void callbackEvent();
private: private:
std::unique_ptr<DsPrivate> m_private;
bool m_coInitialized; bool m_coInitialized;
bool m_buffersRolling; bool m_buffersRolling;
long m_duplexPrerollBytes; long m_duplexPrerollBytes;

View File

@ -56,9 +56,11 @@ airtaudio::Api* airtaudio::api::Jack::Create() {
#include <unistd.h> #include <unistd.h>
#include <cstdio> #include <cstdio>
// A structure to hold various information related to the Jack API
// implementation. namespace airtaudio {
struct JackHandle { namespace api {
class JackPrivate {
public:
jack_client_t *client; jack_client_t *client;
jack_port_t **ports[2]; jack_port_t **ports[2];
std::string deviceName[2]; std::string deviceName[2];
@ -67,7 +69,7 @@ struct JackHandle {
int32_t drainCounter; // Tracks callback counts when draining int32_t drainCounter; // Tracks callback counts when draining
bool internalDrain; // Indicates if stop is initiated from callback or not. bool internalDrain; // Indicates if stop is initiated from callback or not.
JackHandle() : JackPrivate() :
client(0), client(0),
drainCounter(0), drainCounter(0),
internalDrain(false) { internalDrain(false) {
@ -77,8 +79,11 @@ struct JackHandle {
xrun[1] = false; xrun[1] = false;
} }
}; };
}
}
airtaudio::api::Jack::Jack() { airtaudio::api::Jack::Jack() :
m_private(new airtaudio::api::JackPrivate()) {
// Nothing to do here. // Nothing to do here.
} }
@ -246,12 +251,11 @@ void airtaudio::api::Jack::jackShutdown(void* _userData) {
int32_t airtaudio::api::Jack::jackXrun(void* _userData) { int32_t airtaudio::api::Jack::jackXrun(void* _userData) {
airtaudio::api::Jack* myClass = reinterpret_cast<airtaudio::api::Jack*>(_userData); airtaudio::api::Jack* myClass = reinterpret_cast<airtaudio::api::Jack*>(_userData);
JackHandle* handle = (JackHandle*)myClass->m_apiHandle; if (myClass->m_private->ports[0]) {
if (handle->ports[0]) { myClass->m_private->xrun[0] = true;
handle->xrun[0] = true;
} }
if (handle->ports[1]) { if (myClass->m_private->ports[1]) {
handle->xrun[1] = true; myClass->m_private->xrun[1] = true;
} }
return 0; return 0;
} }
@ -264,7 +268,6 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
audio::format _format, audio::format _format,
uint32_t* _bufferSize, uint32_t* _bufferSize,
airtaudio::StreamOptions* _options) { airtaudio::StreamOptions* _options) {
JackHandle *handle = (JackHandle *) m_apiHandle;
// Look for jack server and try to become a client (only do once per stream). // Look for jack server and try to become a client (only do once per stream).
jack_client_t *client = 0; jack_client_t *client = 0;
if ( _mode == airtaudio::mode_output if ( _mode == airtaudio::mode_output
@ -283,7 +286,7 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
} }
} else { } else {
// The handle must have been created on an earlier pass. // The handle must have been created on an earlier pass.
client = handle->client; client = m_private->client;
} }
const char **ports; const char **ports;
std::string port, previousPort, deviceName; std::string port, previousPort, deviceName;
@ -376,16 +379,8 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
m_doConvertBuffer[modeToIdTable(_mode)] = true; m_doConvertBuffer[modeToIdTable(_mode)] = true;
} }
// Allocate our JackHandle structure for the stream. // Allocate our JackHandle structure for the stream.
if (handle == 0) { m_private->client = client;
handle = new JackHandle; m_private->deviceName[modeToIdTable(_mode)] = deviceName;
if (handle == nullptr) {
ATA_ERROR("error allocating JackHandle memory.");
goto error;
}
m_apiHandle = (void *) handle;
handle->client = client;
}
handle->deviceName[modeToIdTable(_mode)] = deviceName;
// Allocate necessary internal buffers. // Allocate necessary internal buffers.
uint64_t bufferBytes; uint64_t bufferBytes;
bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]); bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_deviceFormat[modeToIdTable(_mode)]);
@ -419,8 +414,8 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
} }
} }
// Allocate memory for the Jack ports (channels) identifiers. // Allocate memory for the Jack ports (channels) identifiers.
handle->ports[modeToIdTable(_mode)] = (jack_port_t **) malloc (sizeof (jack_port_t *) * _channels); m_private->ports[modeToIdTable(_mode)] = (jack_port_t **) malloc (sizeof (jack_port_t *) * _channels);
if (handle->ports[modeToIdTable(_mode)] == nullptr) { if (m_private->ports[modeToIdTable(_mode)] == nullptr) {
ATA_ERROR("error allocating port memory."); ATA_ERROR("error allocating port memory.");
goto error; goto error;
} }
@ -433,16 +428,16 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
m_mode = airtaudio::mode_duplex; m_mode = airtaudio::mode_duplex;
} else { } else {
m_mode = _mode; m_mode = _mode;
jack_set_process_callback(handle->client, &airtaudio::api::Jack::jackCallbackHandler, this); jack_set_process_callback(m_private->client, &airtaudio::api::Jack::jackCallbackHandler, this);
jack_set_xrun_callback(handle->client, &airtaudio::api::Jack::jackXrun, this); jack_set_xrun_callback(m_private->client, &airtaudio::api::Jack::jackXrun, this);
jack_on_shutdown(handle->client, &airtaudio::api::Jack::jackShutdown, this); jack_on_shutdown(m_private->client, &airtaudio::api::Jack::jackShutdown, this);
} }
// Register our ports. // Register our ports.
char label[64]; char label[64];
if (_mode == airtaudio::mode_output) { if (_mode == airtaudio::mode_output) {
for (uint32_t i=0; i<m_nUserChannels[0]; i++) { for (uint32_t i=0; i<m_nUserChannels[0]; i++) {
snprintf(label, 64, "outport %d", i); snprintf(label, 64, "outport %d", i);
handle->ports[0][i] = jack_port_register(handle->client, m_private->ports[0][i] = jack_port_register(m_private->client,
(const char *)label, (const char *)label,
JACK_DEFAULT_AUDIO_TYPE, JACK_DEFAULT_AUDIO_TYPE,
JackPortIsOutput, JackPortIsOutput,
@ -451,7 +446,7 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
} else { } else {
for (uint32_t i=0; i<m_nUserChannels[1]; i++) { for (uint32_t i=0; i<m_nUserChannels[1]; i++) {
snprintf(label, 64, "inport %d", i); snprintf(label, 64, "inport %d", i);
handle->ports[1][i] = jack_port_register(handle->client, m_private->ports[1][i] = jack_port_register(m_private->client,
(const char *)label, (const char *)label,
JACK_DEFAULT_AUDIO_TYPE, JACK_DEFAULT_AUDIO_TYPE,
JackPortIsInput, JackPortIsInput,
@ -466,16 +461,14 @@ bool airtaudio::api::Jack::probeDeviceOpen(uint32_t _device,
} }
return true; return true;
error: error:
if (handle) { jack_client_close(m_private->client);
jack_client_close(handle->client); if (m_private->ports[0] != nullptr) {
if (handle->ports[0]) { free(m_private->ports[0]);
free(handle->ports[0]); m_private->ports[0] = nullptr;
} }
if (handle->ports[1]) { if (m_private->ports[1] != nullptr) {
free(handle->ports[1]); free(m_private->ports[1]);
} m_private->ports[1] = nullptr;
delete handle;
m_apiHandle = nullptr;
} }
for (int32_t iii=0; iii<2; ++iii) { for (int32_t iii=0; iii<2; ++iii) {
m_userBuffer[iii].clear(); m_userBuffer[iii].clear();
@ -492,22 +485,19 @@ enum airtaudio::error airtaudio::api::Jack::closeStream() {
ATA_ERROR("no open stream to close!"); ATA_ERROR("no open stream to close!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
JackHandle *handle = (JackHandle *) m_apiHandle; if (m_private != nullptr) {
if (handle != nullptr) {
if (m_state == airtaudio::state_running) { if (m_state == airtaudio::state_running) {
jack_deactivate(handle->client); jack_deactivate(m_private->client);
} }
jack_client_close(handle->client); jack_client_close(m_private->client);
} }
if (handle != nullptr) { if (m_private->ports[0] != nullptr) {
if (handle->ports[0]) { free(m_private->ports[0]);
free(handle->ports[0]); m_private->ports[0] = nullptr;
} }
if (handle->ports[1]) { if (m_private->ports[1] != nullptr) {
free(handle->ports[1]); free(m_private->ports[1]);
} m_private->ports[1] = nullptr;
delete handle;
m_apiHandle = nullptr;
} }
for (int32_t i=0; i<2; i++) { for (int32_t i=0; i<2; i++) {
m_userBuffer[i].clear(); m_userBuffer[i].clear();
@ -529,8 +519,7 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
ATA_ERROR("the stream is already running!"); ATA_ERROR("the stream is already running!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
JackHandle *handle = (JackHandle *) m_apiHandle; int32_t result = jack_activate(m_private->client);
int32_t result = jack_activate(handle->client);
if (result) { if (result) {
ATA_ERROR("unable to activate JACK client!"); ATA_ERROR("unable to activate JACK client!");
goto unlock; goto unlock;
@ -540,7 +529,7 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
result = 1; result = 1;
ports = jack_get_ports(handle->client, handle->deviceName[0].c_str(), nullptr, JackPortIsInput); ports = jack_get_ports(m_private->client, m_private->deviceName[0].c_str(), nullptr, JackPortIsInput);
if (ports == nullptr) { if (ports == nullptr) {
ATA_ERROR("error determining available JACK input ports!"); ATA_ERROR("error determining available JACK input ports!");
goto unlock; goto unlock;
@ -551,7 +540,7 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
for (uint32_t i=0; i<m_nUserChannels[0]; i++) { for (uint32_t i=0; i<m_nUserChannels[0]; i++) {
result = 1; result = 1;
if (ports[ m_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 ]); result = jack_connect(m_private->client, jack_port_name(m_private->ports[0][i]), ports[ m_channelOffset[0] + i ]);
if (result) { if (result) {
free(ports); free(ports);
ATA_ERROR("error connecting output ports!"); ATA_ERROR("error connecting output ports!");
@ -563,7 +552,7 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
if ( m_mode == airtaudio::mode_input if ( m_mode == airtaudio::mode_input
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
result = 1; result = 1;
ports = jack_get_ports(handle->client, handle->deviceName[1].c_str(), nullptr, JackPortIsOutput); ports = jack_get_ports(m_private->client, m_private->deviceName[1].c_str(), nullptr, JackPortIsOutput);
if (ports == nullptr) { if (ports == nullptr) {
ATA_ERROR("error determining available JACK output ports!"); ATA_ERROR("error determining available JACK output ports!");
goto unlock; goto unlock;
@ -572,7 +561,7 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
for (uint32_t i=0; i<m_nUserChannels[1]; i++) { for (uint32_t i=0; i<m_nUserChannels[1]; i++) {
result = 1; result = 1;
if (ports[ m_channelOffset[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])); result = jack_connect(m_private->client, ports[ m_channelOffset[1] + i ], jack_port_name(m_private->ports[1][i]));
} }
if (result) { if (result) {
free(ports); free(ports);
@ -582,8 +571,8 @@ enum airtaudio::error airtaudio::api::Jack::startStream() {
} }
free(ports); free(ports);
} }
handle->drainCounter = 0; m_private->drainCounter = 0;
handle->internalDrain = false; m_private->internalDrain = false;
m_state = airtaudio::state_running; m_state = airtaudio::state_running;
unlock: unlock:
if (result == 0) { if (result == 0) {
@ -600,16 +589,15 @@ enum airtaudio::error airtaudio::api::Jack::stopStream() {
ATA_ERROR("the stream is already stopped!"); ATA_ERROR("the stream is already stopped!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
JackHandle *handle = (JackHandle *) m_apiHandle;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
if (handle->drainCounter == 0) { if (m_private->drainCounter == 0) {
handle->drainCounter = 2; m_private->drainCounter = 2;
std::unique_lock<std::mutex> lck(m_mutex); std::unique_lock<std::mutex> lck(m_mutex);
handle->condition.wait(lck); m_private->condition.wait(lck);
} }
} }
jack_deactivate(handle->client); jack_deactivate(m_private->client);
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
return airtaudio::error_none; return airtaudio::error_none;
} }
@ -622,8 +610,7 @@ enum airtaudio::error airtaudio::api::Jack::abortStream() {
ATA_ERROR("the stream is already stopped!"); ATA_ERROR("the stream is already stopped!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
JackHandle *handle = (JackHandle *) m_apiHandle; m_private->drainCounter = 2;
handle->drainCounter = 2;
return stopStream(); return stopStream();
} }
@ -650,28 +637,27 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) {
ATA_ERROR("the JACK buffer size has changed ... cannot process!"); ATA_ERROR("the JACK buffer size has changed ... cannot process!");
return false; return false;
} }
JackHandle *handle = (JackHandle *) m_apiHandle;
// Check if we were draining the stream and signal is finished. // Check if we were draining the stream and signal is finished.
if (handle->drainCounter > 3) { if (m_private->drainCounter > 3) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;
if (handle->internalDrain == true) { if (m_private->internalDrain == true) {
new std::thread(jackStopStream, this); new std::thread(jackStopStream, this);
} else { } else {
handle->condition.notify_one(); m_private->condition.notify_one();
} }
return true; return true;
} }
// Invoke user callback first, to get fresh output data. // Invoke user callback first, to get fresh output data.
if (handle->drainCounter == 0) { if (m_private->drainCounter == 0) {
double streamTime = getStreamTime(); double streamTime = getStreamTime();
enum airtaudio::status status = airtaudio::status_ok; enum airtaudio::status status = airtaudio::status_ok;
if (m_mode != airtaudio::mode_input && handle->xrun[0] == true) { if (m_mode != airtaudio::mode_input && m_private->xrun[0] == true) {
status = airtaudio::status_underflow; status = airtaudio::status_underflow;
handle->xrun[0] = false; m_private->xrun[0] = false;
} }
if (m_mode != airtaudio::mode_output && handle->xrun[1] == true) { if (m_mode != airtaudio::mode_output && m_private->xrun[1] == true) {
status = airtaudio::status_overflow; status = airtaudio::status_overflow;
handle->xrun[1] = false; m_private->xrun[1] = false;
} }
int32_t cbReturnValue = m_callbackInfo.callback(&m_userBuffer[0][0], int32_t cbReturnValue = m_callbackInfo.callback(&m_userBuffer[0][0],
&m_userBuffer[1][0], &m_userBuffer[1][0],
@ -680,38 +666,38 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) {
status); status);
if (cbReturnValue == 2) { if (cbReturnValue == 2) {
m_state = airtaudio::state_stopping; m_state = airtaudio::state_stopping;
handle->drainCounter = 2; m_private->drainCounter = 2;
new std::thread(jackStopStream, this); new std::thread(jackStopStream, this);
return true; return true;
} }
else if (cbReturnValue == 1) { else if (cbReturnValue == 1) {
handle->drainCounter = 1; m_private->drainCounter = 1;
handle->internalDrain = true; m_private->internalDrain = true;
} }
} }
jack_default_audio_sample_t *jackbuffer; jack_default_audio_sample_t *jackbuffer;
uint64_t bufferBytes = _nframes * sizeof(jack_default_audio_sample_t); uint64_t bufferBytes = _nframes * sizeof(jack_default_audio_sample_t);
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
if (handle->drainCounter > 1) { // write zeros to the output stream if (m_private->drainCounter > 1) { // write zeros to the output stream
for (uint32_t i=0; i<m_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); jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(m_private->ports[0][i], (jack_nframes_t) _nframes);
memset(jackbuffer, 0, bufferBytes); memset(jackbuffer, 0, bufferBytes);
} }
} else if (m_doConvertBuffer[0]) { } else if (m_doConvertBuffer[0]) {
convertBuffer(m_deviceBuffer, &m_userBuffer[0][0], m_convertInfo[0]); convertBuffer(m_deviceBuffer, &m_userBuffer[0][0], m_convertInfo[0]);
for (uint32_t i=0; i<m_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); jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(m_private->ports[0][i], (jack_nframes_t) _nframes);
memcpy(jackbuffer, &m_deviceBuffer[i*bufferBytes], bufferBytes); memcpy(jackbuffer, &m_deviceBuffer[i*bufferBytes], bufferBytes);
} }
} else { // no buffer conversion } else { // no buffer conversion
for (uint32_t i=0; i<m_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); jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(m_private->ports[0][i], (jack_nframes_t) _nframes);
memcpy(jackbuffer, &m_userBuffer[0][i*bufferBytes], bufferBytes); memcpy(jackbuffer, &m_userBuffer[0][i*bufferBytes], bufferBytes);
} }
} }
if (handle->drainCounter) { if (m_private->drainCounter) {
handle->drainCounter++; m_private->drainCounter++;
goto unlock; goto unlock;
} }
} }
@ -719,14 +705,14 @@ bool airtaudio::api::Jack::callbackEvent(uint64_t _nframes) {
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
if (m_doConvertBuffer[1]) { if (m_doConvertBuffer[1]) {
for (uint32_t i=0; i<m_nDeviceChannels[1]; i++) { 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); jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(m_private->ports[1][i], (jack_nframes_t) _nframes);
memcpy(&m_deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes); memcpy(&m_deviceBuffer[i*bufferBytes], jackbuffer, bufferBytes);
} }
convertBuffer(&m_userBuffer[1][0], m_deviceBuffer, m_convertInfo[1]); convertBuffer(&m_userBuffer[1][0], m_deviceBuffer, m_convertInfo[1]);
} else { } else {
// no buffer conversion // no buffer conversion
for (uint32_t i=0; i<m_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); jackbuffer = (jack_default_audio_sample_t *) jack_port_get_buffer(m_private->ports[1][i], (jack_nframes_t) _nframes);
memcpy(&m_userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes); memcpy(&m_userBuffer[1][i*bufferBytes], jackbuffer, bufferBytes);
} }
} }

View File

@ -11,6 +11,7 @@
#include <jack/jack.h> #include <jack/jack.h>
namespace airtaudio { namespace airtaudio {
namespace api { namespace api {
class JackPrivate;
class Jack: public airtaudio::Api { class Jack: public airtaudio::Api {
public: public:
static airtaudio::Api* Create(); static airtaudio::Api* Create();
@ -38,6 +39,7 @@ namespace airtaudio {
static void jackShutdown(void* _userData); static void jackShutdown(void* _userData);
static int32_t jackCallbackHandler(jack_nframes_t _nframes, void* _userData); static int32_t jackCallbackHandler(jack_nframes_t _nframes, void* _userData);
private: private:
std::unique_ptr<JackPrivate> m_private;
bool probeDeviceOpen(uint32_t _device, bool probeDeviceOpen(uint32_t _device,
airtaudio::mode _mode, airtaudio::mode _mode,
uint32_t _channels, uint32_t _channels,

View File

@ -25,16 +25,18 @@ airtaudio::Api* airtaudio::api::Oss::Create() {
return new airtaudio::api::Oss(); return new airtaudio::api::Oss();
} }
static void *ossCallbackHandler(void* _ptr); static void *ossCallbackHandler(void* _userData);
// A structure to hold various information related to the OSS API
// implementation. namespace airtaudio {
struct OssHandle { namespace api {
class OssPrivate {
public:
int32_t id[2]; // device ids int32_t id[2]; // device ids
bool xrun[2]; bool xrun[2];
bool triggered; bool triggered;
std::condition_variable runnable; std::condition_variable runnable;
OssHandle(): OssPrivate():
triggered(false) { triggered(false) {
id[0] = 0; id[0] = 0;
id[1] = 0; id[1] = 0;
@ -42,8 +44,11 @@ struct OssHandle {
xrun[1] = false; xrun[1] = false;
} }
}; };
}
}
airtaudio::api::Oss::Oss() { airtaudio::api::Oss::Oss() :
m_private(new airtaudio::api::OssPrivate()) {
// Nothing to do here. // Nothing to do here.
} }
@ -226,15 +231,14 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
return false; return false;
} }
int32_t flags = 0; int32_t flags = 0;
OssHandle *handle = (OssHandle *) m_apiHandle;
if (_mode == airtaudio::mode_output) { if (_mode == airtaudio::mode_output) {
flags |= O_WRONLY; flags |= O_WRONLY;
} else { // _mode == airtaudio::mode_input } else { // _mode == airtaudio::mode_input
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
&& m_device[0] == _device) { && m_device[0] == _device) {
// We just set the same device for playback ... close and reopen for duplex (OSS only). // We just set the same device for playback ... close and reopen for duplex (OSS only).
close(handle->id[0]); close(m_private->id[0]);
handle->id[0] = 0; m_private->id[0] = 0;
if (!(ainfo.caps & PCM_CAP_airtaudio::mode_duplex)) { if (!(ainfo.caps & PCM_CAP_airtaudio::mode_duplex)) {
ATA_ERROR("device (" << ainfo.name << ") does not support duplex mode."); ATA_ERROR("device (" << ainfo.name << ") does not support duplex mode.");
return false; return false;
@ -450,18 +454,7 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
&& m_nUserChannels[modeToIdTable(_mode)] > 1) { && m_nUserChannels[modeToIdTable(_mode)] > 1) {
m_doConvertBuffer[modeToIdTable(_mode)] = true; m_doConvertBuffer[modeToIdTable(_mode)] = true;
} }
// Allocate the stream handles if necessary and then save. m_private->id[modeToIdTable(_mode)] = fd;
if (m_apiHandle == 0) {
handle = new OssHandle;
if handle == nullptr) {
ATA_ERROR("error allocating OssHandle memory.");
goto error;
}
m_apiHandle = (void *) handle;
} else {
handle = (OssHandle *) m_apiHandle;
}
handle->id[modeToIdTable(_mode)] = fd;
// Allocate necessary internal buffers. // Allocate necessary internal buffers.
uint64_t bufferBytes; uint64_t bufferBytes;
bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat); bufferBytes = m_nUserChannels[modeToIdTable(_mode)] * *_bufferSize * audio::getFormatBytes(m_userFormat);
@ -505,14 +498,13 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
// We had already set up an output stream. // We had already set up an output stream.
m_mode = airtaudio::mode_duplex; m_mode = airtaudio::mode_duplex;
if (m_device[0] == _device) { if (m_device[0] == _device) {
handle->id[0] = fd; m_private->id[0] = fd;
} }
} else { } else {
m_mode = _mode; m_mode = _mode;
// Setup callback thread. // Setup callback thread.
m_callbackInfo.object = (void *) this;
m_callbackInfo.isRunning = true; m_callbackInfo.isRunning = true;
m_callbackInfo.thread = new std::thread(ossCallbackHandler, &m_callbackInfo); m_callbackInfo.thread = new std::thread(ossCallbackHandler, this);
if (m_callbackInfo.thread == nullptr) { if (m_callbackInfo.thread == nullptr) {
m_callbackInfo.isRunning = false; m_callbackInfo.isRunning = false;
ATA_ERROR("creating callback thread!"); ATA_ERROR("creating callback thread!");
@ -521,15 +513,13 @@ bool airtaudio::api::Oss::probeDeviceOpen(uint32_t _device,
} }
return true; return true;
error: error:
if (handle) { if (m_private->id[0] != nullptr) {
if (handle->id[0]) { close(m_private->id[0]);
close(handle->id[0]); m_private->id[0] = nullptr;
} }
if (handle->id[1]) { if (m_private->id[1] != nullptr) {
close(handle->id[1]); close(m_private->id[1]);
} m_private->id[1] = nullptr;
delete handle;
m_apiHandle = 0;
} }
for (int32_t i=0; i<2; i++) { for (int32_t i=0; i<2; i++) {
if (m_userBuffer[i]) { if (m_userBuffer[i]) {
@ -549,31 +539,28 @@ enum airtaudio::error airtaudio::api::Oss::closeStream() {
ATA_ERROR("no open stream to close!"); ATA_ERROR("no open stream to close!");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
OssHandle *handle = (OssHandle *) m_apiHandle;
m_callbackInfo.isRunning = false; m_callbackInfo.isRunning = false;
m_mutex.lock(); m_mutex.lock();
if (m_state == airtaudio::state_stopped) { if (m_state == airtaudio::state_stopped) {
handle->runnable.notify_one(); m_private->runnable.notify_one();
} }
m_mutex.unlock(); m_mutex.unlock();
m_callbackInfo.thread->join(); m_callbackInfo.thread->join();
if (m_state == airtaudio::state_running) { if (m_state == airtaudio::state_running) {
if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) {
ioctl(handle->id[0], SNDCTL_DSP_HALT, 0); ioctl(m_private->id[0], SNDCTL_DSP_HALT, 0);
} else { } else {
ioctl(handle->id[1], SNDCTL_DSP_HALT, 0); ioctl(m_private->id[1], SNDCTL_DSP_HALT, 0);
} }
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
} }
if (handle) { if (m_private->id[0] != nullptr) {
if (handle->id[0]) { close(m_private->id[0]);
close(handle->id[0]); m_private->id[0] = nullptr;
} }
if (handle->id[1]) { if (m_private->id[1] != nullptr) {
close(handle->id[1]); close(m_private->id[1]);
} m_private->id[1] = nullptr;
delete handle;
m_apiHandle = 0;
} }
for (int32_t i=0; i<2; i++) { for (int32_t i=0; i<2; i++) {
if (m_userBuffer[i]) { if (m_userBuffer[i]) {
@ -603,8 +590,7 @@ enum airtaudio::error airtaudio::api::Oss::startStream() {
// No need to do anything else here ... OSS automatically starts // No need to do anything else here ... OSS automatically starts
// when fed samples. // when fed samples.
m_mutex.unlock(); m_mutex.unlock();
OssHandle *handle = (OssHandle *) m_apiHandle; m_private->runnable.notify_one();
handle->runnable.notify_one();
} }
enum airtaudio::error airtaudio::api::Oss::stopStream() { enum airtaudio::error airtaudio::api::Oss::stopStream() {
@ -622,7 +608,6 @@ enum airtaudio::error airtaudio::api::Oss::stopStream() {
return; return;
} }
int32_t result = 0; int32_t result = 0;
OssHandle *handle = (OssHandle *) m_apiHandle;
if ( m_mode == airtaudio::mode_output if ( m_mode == airtaudio::mode_output
|| m_mode == airtaudio::mode_duplex) { || m_mode == airtaudio::mode_duplex) {
// Flush the output with zeros a few times. // Flush the output with zeros a few times.
@ -640,23 +625,23 @@ enum airtaudio::error airtaudio::api::Oss::stopStream() {
} }
memset(buffer, 0, samples * audio::getFormatBytes(format)); memset(buffer, 0, samples * audio::getFormatBytes(format));
for (uint32_t i=0; i<m_nBuffers+1; i++) { for (uint32_t i=0; i<m_nBuffers+1; i++) {
result = write(handle->id[0], buffer, samples * audio::getFormatBytes(format)); result = write(m_private->id[0], buffer, samples * audio::getFormatBytes(format));
if (result == -1) { if (result == -1) {
ATA_ERROR("audio write error."); ATA_ERROR("audio write error.");
return airtaudio::error_warning; return airtaudio::error_warning;
} }
} }
result = ioctl(handle->id[0], SNDCTL_DSP_HALT, 0); result = ioctl(m_private->id[0], SNDCTL_DSP_HALT, 0);
if (result == -1) { if (result == -1) {
ATA_ERROR("system error stopping callback procedure on device (" << m_device[0] << ")."); ATA_ERROR("system error stopping callback procedure on device (" << m_device[0] << ").");
goto unlock; goto unlock;
} }
handle->triggered = false; m_private->triggered = false;
} }
if ( m_mode == airtaudio::mode_input if ( m_mode == airtaudio::mode_input
|| ( m_mode == airtaudio::mode_duplex || ( m_mode == airtaudio::mode_duplex
&& handle->id[0] != handle->id[1])) { && m_private->id[0] != m_private->id[1])) {
result = ioctl(handle->id[1], SNDCTL_DSP_HALT, 0); result = ioctl(m_private->id[1], SNDCTL_DSP_HALT, 0);
if (result == -1) { if (result == -1) {
ATA_ERROR("system error stopping input callback procedure on device (" << m_device[0] << ")."); ATA_ERROR("system error stopping input callback procedure on device (" << m_device[0] << ").");
goto unlock; goto unlock;
@ -686,17 +671,16 @@ enum airtaudio::error airtaudio::api::Oss::abortStream() {
return; return;
} }
int32_t result = 0; int32_t result = 0;
OssHandle *handle = (OssHandle *) m_apiHandle;
if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) { if (m_mode == airtaudio::mode_output || m_mode == airtaudio::mode_duplex) {
result = ioctl(handle->id[0], SNDCTL_DSP_HALT, 0); result = ioctl(m_private->id[0], SNDCTL_DSP_HALT, 0);
if (result == -1) { if (result == -1) {
ATA_ERROR("system error stopping callback procedure on device (" << m_device[0] << ")."); ATA_ERROR("system error stopping callback procedure on device (" << m_device[0] << ").");
goto unlock; goto unlock;
} }
handle->triggered = false; m_private->triggered = false;
} }
if (m_mode == airtaudio::mode_input || (m_mode == airtaudio::mode_duplex && handle->id[0] != handle->id[1])) { if (m_mode == airtaudio::mode_input || (m_mode == airtaudio::mode_duplex && m_private->id[0] != m_private->id[1])) {
result = ioctl(handle->id[1], SNDCTL_DSP_HALT, 0); result = ioctl(m_private->id[1], SNDCTL_DSP_HALT, 0);
if (result == -1) { if (result == -1) {
ATA_ERROR("system error stopping input callback procedure on device (" << m_device[0] << ")."); ATA_ERROR("system error stopping input callback procedure on device (" << m_device[0] << ").");
goto unlock; goto unlock;
@ -712,10 +696,9 @@ unlock:
} }
void airtaudio::api::Oss::callbackEvent() { void airtaudio::api::Oss::callbackEvent() {
OssHandle *handle = (OssHandle *) m_apiHandle;
if (m_state == airtaudio::state_stopped) { if (m_state == airtaudio::state_stopped) {
std::unique_lock<std::mutex> lck(m_mutex); std::unique_lock<std::mutex> lck(m_mutex);
handle->runnable.wait(lck); m_private->runnable.wait(lck);
if (m_state != airtaudio::state_running) { if (m_state != airtaudio::state_running) {
return; return;
} }
@ -729,14 +712,14 @@ void airtaudio::api::Oss::callbackEvent() {
double streamTime = getStreamTime(); double streamTime = getStreamTime();
rtaudio::streamStatus status = 0; rtaudio::streamStatus status = 0;
if ( m_mode != airtaudio::mode_input if ( m_mode != airtaudio::mode_input
&& handle->xrun[0] == true) { && m_private->xrun[0] == true) {
status |= RTAUDIO_airtaudio::status_underflow; status |= RTAUDIO_airtaudio::status_underflow;
handle->xrun[0] = false; m_private->xrun[0] = false;
} }
if ( m_mode != airtaudio::mode_output if ( m_mode != airtaudio::mode_output
&& handle->xrun[1] == true) { && m_private->xrun[1] == true) {
status |= RTAUDIO_airtaudio::mode_input_OVERFLOW; status |= RTAUDIO_airtaudio::mode_input_OVERFLOW;
handle->xrun[1] = false; m_private->xrun[1] = false;
} }
doStopStream = m_callbackInfo.callback(m_userBuffer[0], doStopStream = m_callbackInfo.callback(m_userBuffer[0],
m_userBuffer[1], m_userBuffer[1],
@ -774,21 +757,21 @@ void airtaudio::api::Oss::callbackEvent() {
byteSwapBuffer(buffer, samples, format); byteSwapBuffer(buffer, samples, format);
} }
if ( m_mode == airtaudio::mode_duplex if ( m_mode == airtaudio::mode_duplex
&& handle->triggered == false) { && m_private->triggered == false) {
int32_t trig = 0; int32_t trig = 0;
ioctl(handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig); ioctl(m_private->id[0], SNDCTL_DSP_SETTRIGGER, &trig);
result = write(handle->id[0], buffer, samples * audio::getFormatBytes(format)); result = write(m_private->id[0], buffer, samples * audio::getFormatBytes(format));
trig = PCM_ENABLE_airtaudio::mode_input|PCM_ENABLE_airtaudio::mode_output; trig = PCM_ENABLE_airtaudio::mode_input|PCM_ENABLE_airtaudio::mode_output;
ioctl(handle->id[0], SNDCTL_DSP_SETTRIGGER, &trig); ioctl(m_private->id[0], SNDCTL_DSP_SETTRIGGER, &trig);
handle->triggered = true; m_private->triggered = true;
} else { } else {
// Write samples to device. // Write samples to device.
result = write(handle->id[0], buffer, samples * audio::getFormatBytes(format)); result = write(m_private->id[0], buffer, samples * audio::getFormatBytes(format));
} }
if (result == -1) { if (result == -1) {
// We'll assume this is an underrun, though there isn't a // We'll assume this is an underrun, though there isn't a
// specific means for determining that. // specific means for determining that.
handle->xrun[0] = true; m_private->xrun[0] = true;
ATA_ERROR("audio write error."); ATA_ERROR("audio write error.");
//error(airtaudio::error_warning); //error(airtaudio::error_warning);
// Continue on to input section. // Continue on to input section.
@ -807,11 +790,11 @@ void airtaudio::api::Oss::callbackEvent() {
format = m_userFormat; format = m_userFormat;
} }
// Read samples from device. // Read samples from device.
result = read(handle->id[1], buffer, samples * audio::getFormatBytes(format)); result = read(m_private->id[1], buffer, samples * audio::getFormatBytes(format));
if (result == -1) { if (result == -1) {
// We'll assume this is an overrun, though there isn't a // We'll assume this is an overrun, though there isn't a
// specific means for determining that. // specific means for determining that.
handle->xrun[1] = true; m_private->xrun[1] = true;
ATA_ERROR("audio read error."); ATA_ERROR("audio read error.");
goto unlock; goto unlock;
} }
@ -832,12 +815,10 @@ unlock:
} }
} }
static void ossCallbackHandler(void* _ptr) { static void ossCallbackHandler(void* _userData) {
CallbackInfo* info = (CallbackInfo*)_ptr; airtaudio::api::Alsa* myClass = reinterpret_cast<airtaudio::api::Oss*>(_userData);
RtApiOss* object = (RtApiOss*)info->object; while (myClass->m_callbackInfo->isRunning == true) {
bool *isRunning = &info->isRunning; myClass->callbackEvent();
while (*isRunning == true) {
object->callbackEvent();
} }
} }

View File

@ -10,6 +10,7 @@
namespace airtaudio { namespace airtaudio {
namespace api { namespace api {
class OssPrivate;
class Oss: public airtaudio::Api { class Oss: public airtaudio::Api {
public: public:
static airtaudio::Api* Create(); static airtaudio::Api* Create();
@ -31,6 +32,7 @@ namespace airtaudio {
// will most likely produce highly undesireable results! // will most likely produce highly undesireable results!
void callbackEvent(); void callbackEvent();
private: private:
std::unique_ptr<OssPrivate> m_private;
bool probeDeviceOpen(uint32_t _device, bool probeDeviceOpen(uint32_t _device,
airtaudio::mode _mode, airtaudio::mode _mode,
uint32_t _channels, uint32_t _channels,

View File

@ -49,19 +49,29 @@ static const rtaudio_pa_format_mapping_t supported_sampleformats[] = {
{audio::format_float, PA_SAMPLE_FLOAT32LE}, {audio::format_float, PA_SAMPLE_FLOAT32LE},
{audio::format_unknow, PA_SAMPLE_INVALID}}; {audio::format_unknow, PA_SAMPLE_INVALID}};
struct PulseAudioHandle {
namespace airtaudio {
namespace api {
class PulsePrivate {
public:
pa_simple *s_play; pa_simple *s_play;
pa_simple *s_rec; pa_simple *s_rec;
std::thread* thread; std::thread* thread;
std::condition_variable runnable_cv; std::condition_variable runnable_cv;
bool runnable; bool runnable;
PulseAudioHandle() : PulsePrivate() :
s_play(0), s_play(0),
s_rec(0), s_rec(0),
runnable(false) { runnable(false) {
} }
}; };
}
}
airtaudio::api::Pulse::Pulse() :
m_private(new airtaudio::api::PulsePrivate()) {
}
airtaudio::api::Pulse::~Pulse() { airtaudio::api::Pulse::~Pulse() {
if (m_state != airtaudio::state_closed) { if (m_state != airtaudio::state_closed) {
@ -103,25 +113,20 @@ void airtaudio::api::Pulse::callbackEvent() {
} }
enum airtaudio::error airtaudio::api::Pulse::closeStream() { enum airtaudio::error airtaudio::api::Pulse::closeStream() {
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_apiHandle);
m_callbackInfo.isRunning = false; m_callbackInfo.isRunning = false;
if (pah) {
m_mutex.lock(); m_mutex.lock();
if (m_state == airtaudio::state_stopped) { if (m_state == airtaudio::state_stopped) {
pah->runnable = true; m_private->runnable = true;
pah->runnable_cv.notify_one();; m_private->runnable_cv.notify_one();;
} }
m_mutex.unlock(); m_mutex.unlock();
pah->thread->join(); m_private->thread->join();
if (pah->s_play) { if (m_private->s_play) {
pa_simple_flush(pah->s_play, nullptr); pa_simple_flush(m_private->s_play, nullptr);
pa_simple_free(pah->s_play); pa_simple_free(m_private->s_play);
} }
if (pah->s_rec) { if (m_private->s_rec) {
pa_simple_free(pah->s_rec); pa_simple_free(m_private->s_rec);
}
delete pah;
m_apiHandle = nullptr;
} }
m_userBuffer[0].clear(); m_userBuffer[0].clear();
m_userBuffer[1].clear(); m_userBuffer[1].clear();
@ -131,11 +136,10 @@ enum airtaudio::error airtaudio::api::Pulse::closeStream() {
} }
void airtaudio::api::Pulse::callbackEventOneCycle() { void airtaudio::api::Pulse::callbackEventOneCycle() {
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_apiHandle);
if (m_state == airtaudio::state_stopped) { if (m_state == airtaudio::state_stopped) {
std::unique_lock<std::mutex> lck(m_mutex); std::unique_lock<std::mutex> lck(m_mutex);
while (!pah->runnable) { while (!m_private->runnable) {
pah->runnable_cv.wait(lck); m_private->runnable_cv.wait(lck);
} }
if (m_state != airtaudio::state_running) { if (m_state != airtaudio::state_running) {
m_mutex.unlock(); m_mutex.unlock();
@ -175,7 +179,7 @@ void airtaudio::api::Pulse::callbackEventOneCycle() {
} else { } else {
bytes = m_nUserChannels[airtaudio::modeToIdTable(airtaudio::mode_output)] * m_bufferSize * audio::getFormatBytes(m_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) { if (pa_simple_write(m_private->s_play, pulse_out, bytes, &pa_error) < 0) {
ATA_ERROR("audio write error, " << pa_strerror(pa_error) << "."); ATA_ERROR("audio write error, " << pa_strerror(pa_error) << ".");
return; return;
} }
@ -186,7 +190,7 @@ void airtaudio::api::Pulse::callbackEventOneCycle() {
} else { } else {
bytes = m_nUserChannels[airtaudio::modeToIdTable(airtaudio::mode_input)] * m_bufferSize * audio::getFormatBytes(m_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) { if (pa_simple_read(m_private->s_rec, pulse_in, bytes, &pa_error) < 0) {
ATA_ERROR("audio read error, " << pa_strerror(pa_error) << "."); ATA_ERROR("audio read error, " << pa_strerror(pa_error) << ".");
return; return;
} }
@ -207,7 +211,6 @@ unlock:
} }
enum airtaudio::error airtaudio::api::Pulse::startStream() { enum airtaudio::error airtaudio::api::Pulse::startStream() {
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_apiHandle);
if (m_state == airtaudio::state_closed) { if (m_state == airtaudio::state_closed) {
ATA_ERROR("the stream is not open!"); ATA_ERROR("the stream is not open!");
return airtaudio::error_invalidUse; return airtaudio::error_invalidUse;
@ -218,14 +221,13 @@ enum airtaudio::error airtaudio::api::Pulse::startStream() {
} }
m_mutex.lock(); m_mutex.lock();
m_state = airtaudio::state_running; m_state = airtaudio::state_running;
pah->runnable = true; m_private->runnable = true;
pah->runnable_cv.notify_one(); m_private->runnable_cv.notify_one();
m_mutex.unlock(); m_mutex.unlock();
return airtaudio::error_none; return airtaudio::error_none;
} }
enum airtaudio::error airtaudio::api::Pulse::stopStream() { enum airtaudio::error airtaudio::api::Pulse::stopStream() {
PulseAudioHandle *pah = static_cast<PulseAudioHandle *>(m_apiHandle);
if (m_state == airtaudio::state_closed) { if (m_state == airtaudio::state_closed) {
ATA_ERROR("the stream is not open!"); ATA_ERROR("the stream is not open!");
return airtaudio::error_invalidUse; return airtaudio::error_invalidUse;
@ -236,9 +238,9 @@ enum airtaudio::error airtaudio::api::Pulse::stopStream() {
} }
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
m_mutex.lock(); m_mutex.lock();
if (pah && pah->s_play) { if (m_private->s_play) {
int32_t pa_error; int32_t pa_error;
if (pa_simple_drain(pah->s_play, &pa_error) < 0) { if (pa_simple_drain(m_private->s_play, &pa_error) < 0) {
ATA_ERROR("error draining output device, " << pa_strerror(pa_error) << "."); ATA_ERROR("error draining output device, " << pa_strerror(pa_error) << ".");
m_mutex.unlock(); m_mutex.unlock();
return airtaudio::error_systemError; return airtaudio::error_systemError;
@ -250,7 +252,6 @@ enum airtaudio::error airtaudio::api::Pulse::stopStream() {
} }
enum airtaudio::error airtaudio::api::Pulse::abortStream() { enum airtaudio::error airtaudio::api::Pulse::abortStream() {
PulseAudioHandle *pah = static_cast<PulseAudioHandle*>(m_apiHandle);
if (m_state == airtaudio::state_closed) { if (m_state == airtaudio::state_closed) {
ATA_ERROR("the stream is not open!"); ATA_ERROR("the stream is not open!");
return airtaudio::error_invalidUse; return airtaudio::error_invalidUse;
@ -261,9 +262,9 @@ enum airtaudio::error airtaudio::api::Pulse::abortStream() {
} }
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
m_mutex.lock(); m_mutex.lock();
if (pah && pah->s_play) { if (m_private && m_private->s_play) {
int32_t pa_error; int32_t pa_error;
if (pa_simple_flush(pah->s_play, &pa_error) < 0) { if (pa_simple_flush(m_private->s_play, &pa_error) < 0) {
ATA_ERROR("error flushing output device, " << pa_strerror(pa_error) << "."); ATA_ERROR("error flushing output device, " << pa_strerror(pa_error) << ".");
m_mutex.unlock(); m_mutex.unlock();
return airtaudio::error_systemError; return airtaudio::error_systemError;
@ -282,7 +283,6 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device,
audio::format _format, audio::format _format,
uint32_t *_bufferSize, uint32_t *_bufferSize,
airtaudio::StreamOptions *_options) { airtaudio::StreamOptions *_options) {
PulseAudioHandle *pah = 0;
uint64_t bufferBytes = 0; uint64_t bufferBytes = 0;
pa_sample_spec ss; pa_sample_spec ss;
if (_device != 0) { if (_device != 0) {
@ -367,27 +367,18 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device,
if (m_doConvertBuffer[modeToIdTable(_mode)]) { if (m_doConvertBuffer[modeToIdTable(_mode)]) {
setConvertInfo(_mode, _firstChannel); setConvertInfo(_mode, _firstChannel);
} }
if (!m_apiHandle) {
PulseAudioHandle *pah = new PulseAudioHandle;
if (!pah) {
ATA_ERROR("error allocating memory for handle.");
goto error;
}
m_apiHandle = pah;
}
pah = static_cast<PulseAudioHandle *>(m_apiHandle);
int32_t error; int32_t error;
switch (_mode) { switch (_mode) {
case airtaudio::mode_input: case airtaudio::mode_input:
pah->s_rec = pa_simple_new(nullptr, "airtAudio", PA_STREAM_RECORD, nullptr, "Record", &ss, nullptr, nullptr, &error); m_private->s_rec = pa_simple_new(nullptr, "airtAudio", PA_STREAM_RECORD, nullptr, "Record", &ss, nullptr, nullptr, &error);
if (!pah->s_rec) { if (!m_private->s_rec) {
ATA_ERROR("error connecting input to PulseAudio server."); ATA_ERROR("error connecting input to PulseAudio server.");
goto error; goto error;
} }
break; break;
case airtaudio::mode_output: case airtaudio::mode_output:
pah->s_play = pa_simple_new(nullptr, "airtAudio", PA_STREAM_PLAYBACK, nullptr, "Playback", &ss, nullptr, nullptr, &error); m_private->s_play = pa_simple_new(nullptr, "airtAudio", PA_STREAM_PLAYBACK, nullptr, "Playback", &ss, nullptr, nullptr, &error);
if (!pah->s_play) { if (!m_private->s_play) {
ATA_ERROR("error connecting output to PulseAudio server."); ATA_ERROR("error connecting output to PulseAudio server.");
goto error; goto error;
} }
@ -404,8 +395,8 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device,
} }
if (!m_callbackInfo.isRunning) { if (!m_callbackInfo.isRunning) {
m_callbackInfo.isRunning = true; m_callbackInfo.isRunning = true;
pah->thread = new std::thread(pulseaudio_callback, this); m_private->thread = new std::thread(pulseaudio_callback, this);
if (pah->thread == nullptr) { if (m_private->thread == nullptr) {
ATA_ERROR("error creating thread."); ATA_ERROR("error creating thread.");
goto error; goto error;
} }
@ -413,10 +404,6 @@ bool airtaudio::api::Pulse::probeDeviceOpen(uint32_t _device,
m_state = airtaudio::state_stopped; m_state = airtaudio::state_stopped;
return true; return true;
error: error:
if (pah && m_callbackInfo.isRunning) {
delete pah;
m_apiHandle = 0;
}
for (int32_t i=0; i<2; i++) { for (int32_t i=0; i<2; i++) {
m_userBuffer[i].clear(); m_userBuffer[i].clear();
} }

View File

@ -10,10 +10,12 @@
namespace airtaudio { namespace airtaudio {
namespace api { namespace api {
class PulsePrivate;
class Pulse: public airtaudio::Api { class Pulse: public airtaudio::Api {
public: public:
static airtaudio::Api* Create(); static airtaudio::Api* Create();
public: public:
Pulse();
virtual ~Pulse(); virtual ~Pulse();
enum airtaudio::type getCurrentApi() { enum airtaudio::type getCurrentApi() {
return airtaudio::type_pulse; return airtaudio::type_pulse;
@ -31,6 +33,7 @@ namespace airtaudio {
void callbackEventOneCycle(); void callbackEventOneCycle();
void callbackEvent(); void callbackEvent();
private: private:
std::unique_ptr<PulsePrivate> m_private;
std::vector<airtaudio::DeviceInfo> m_devices; std::vector<airtaudio::DeviceInfo> m_devices;
void saveDeviceInfo(); void saveDeviceInfo();
bool probeDeviceOpen(uint32_t _device, bool probeDeviceOpen(uint32_t _device,