[DEV] set member in private for internal backend interface
This commit is contained in:
parent
4febe7b119
commit
5c9361c199
@ -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() {
|
||||||
|
@ -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.
|
||||||
|
@ -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) << ".");
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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) {
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
@ -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(¤tWritePointer, &safeWritePointer);
|
result = dsWriteBuffer->GetCurrentPosition(¤tWritePointer, &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(¤tReadPointer, &safeReadPointer);
|
result = dsBuffer->GetCurrentPosition(¤tReadPointer, &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) {
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user