|
|
|
@@ -230,6 +230,26 @@ void JackBoomerDriver::DisplayDeviceInfo() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
JackBoomerDriver::JackBoomerDriver(const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table) |
|
|
|
: JackAudioDriver(name, alias, engine, table), |
|
|
|
fInFD(-1), fOutFD(-1), fBits(0), |
|
|
|
fSampleFormat(0), fNperiods(0), fRWMode(0), fExcl(false), fIgnoreHW(true), |
|
|
|
fInputBufferSize(0), fOutputBufferSize(0), |
|
|
|
fInputBuffer(NULL), fOutputBuffer(NULL), |
|
|
|
fInputThread(&fInputHandler), fOutputThread(&fOutputHandler), |
|
|
|
fInputHandler(this), fOutputHandler(this) |
|
|
|
|
|
|
|
{ |
|
|
|
sem_init(&fReadSema, 0, 0); |
|
|
|
sem_init(&fWriteSema, 0, 0); |
|
|
|
} |
|
|
|
|
|
|
|
JackBoomerDriver::~JackBoomerDriver() |
|
|
|
{ |
|
|
|
sem_destroy(&fReadSema); |
|
|
|
sem_destroy(&fWriteSema); |
|
|
|
} |
|
|
|
|
|
|
|
int JackBoomerDriver::OpenInput() |
|
|
|
{ |
|
|
|
int flags = 0; |
|
|
|
@@ -488,15 +508,6 @@ int JackBoomerDriver::OpenAux() |
|
|
|
return -1; |
|
|
|
} |
|
|
|
|
|
|
|
// Prepare ringbuffers used for output |
|
|
|
if (fPlaybackChannels > 0) { |
|
|
|
fRingBuffer = new jack_ringbuffer_t*[fPlaybackChannels]; |
|
|
|
for (int i = 0; i < fPlaybackChannels; i++) { |
|
|
|
fRingBuffer[i] = jack_ringbuffer_create(fOutputBufferSize * 2); |
|
|
|
jack_ringbuffer_read_advance(fRingBuffer[i], fOutputBufferSize); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
DisplayDeviceInfo(); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
@@ -520,27 +531,25 @@ void JackBoomerDriver::CloseAux() |
|
|
|
if (fOutputBuffer) |
|
|
|
free(fOutputBuffer); |
|
|
|
fOutputBuffer = NULL; |
|
|
|
|
|
|
|
for (int i = 0; i < fPlaybackChannels; i++) { |
|
|
|
if (fRingBuffer[i]) |
|
|
|
jack_ringbuffer_free(fRingBuffer[i]); |
|
|
|
fRingBuffer[i] = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
delete [] fRingBuffer; |
|
|
|
fRingBuffer = NULL; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
int JackBoomerDriver::Start() |
|
|
|
{ |
|
|
|
jack_log("JackBoomerDriver::Start"); |
|
|
|
JackAudioDriver::Start(); |
|
|
|
|
|
|
|
// Start input thread only when needed |
|
|
|
if (fInFD > 0) { |
|
|
|
if (fInputThread.StartSync() < 0) { |
|
|
|
jack_error("Cannot start input thread"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// Start output thread only when needed |
|
|
|
if (fOutFD > 0) { |
|
|
|
if (fThread.StartSync() < 0) { |
|
|
|
jack_error("Cannot start thread"); |
|
|
|
if (fOutputThread.StartSync() < 0) { |
|
|
|
jack_error("Cannot start output thread"); |
|
|
|
return -1; |
|
|
|
} |
|
|
|
} |
|
|
|
@@ -550,20 +559,59 @@ int JackBoomerDriver::Start() |
|
|
|
|
|
|
|
int JackBoomerDriver::Stop() |
|
|
|
{ |
|
|
|
// Stop input thread only when needed |
|
|
|
if (fInFD > 0) { |
|
|
|
fInputThread.Kill(); |
|
|
|
} |
|
|
|
|
|
|
|
// Stop output thread only when needed |
|
|
|
if (fOutFD > 0) { |
|
|
|
return fThread.Kill(); |
|
|
|
} else { |
|
|
|
return 0; |
|
|
|
fOutputThread.Kill(); |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int JackBoomerDriver::Read() |
|
|
|
{ |
|
|
|
if (fInFD < 0) { |
|
|
|
/* |
|
|
|
// Keep begin cycle time |
|
|
|
JackDriver::CycleTakeBeginTime(); |
|
|
|
*/ |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
int JackBoomerDriver::Write() |
|
|
|
{ |
|
|
|
/* |
|
|
|
// Keep begin cycle time |
|
|
|
JackDriver::CycleTakeEndTime(); |
|
|
|
*/ |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
bool JackBoomerDriver::JackBoomerDriverInput::Init() |
|
|
|
{ |
|
|
|
if (fDriver->IsRealTime()) { |
|
|
|
jack_log("JackBoomerDriverInput::Init IsRealTime"); |
|
|
|
if (fDriver->fInputThread.AcquireRealTime(GetEngineControl()->fServerPriority) < 0) { |
|
|
|
jack_error("AcquireRealTime error"); |
|
|
|
} else { |
|
|
|
set_threaded_log_function(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool JackBoomerDriver::JackBoomerDriverInput::Execute() |
|
|
|
{ |
|
|
|
if (fDriver->fInFD < 0) { |
|
|
|
// Keep begin cycle time |
|
|
|
JackDriver::CycleTakeBeginTime(); |
|
|
|
return 0; |
|
|
|
fDriver->CycleTakeBeginTime(); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
#ifdef JACK_MONITOR |
|
|
|
@@ -571,21 +619,21 @@ int JackBoomerDriver::Read() |
|
|
|
#endif |
|
|
|
|
|
|
|
audio_errinfo ei_in; |
|
|
|
ssize_t count = ::read(fInFD, fInputBuffer, fInputBufferSize); |
|
|
|
ssize_t count = ::read(fDriver->fInFD, fDriver->fInputBuffer, fDriver->fInputBufferSize); |
|
|
|
|
|
|
|
#ifdef JACK_MONITOR |
|
|
|
if (count > 0 && count != (int)fInputBufferSize) |
|
|
|
jack_log("JackBoomerDriver::Read count = %ld", count / (fSampleSize * fCaptureChannels)); |
|
|
|
if (count > 0 && count != (int)fDriver->fInputBufferSize) |
|
|
|
jack_log("JackBoomerDriverInput::Execute count = %ld", count / (fDriver->fSampleSize * fDriver->fCaptureChannels)); |
|
|
|
gCycleTable.fTable[gCycleReadCount].fAfterRead = GetMicroSeconds(); |
|
|
|
#endif |
|
|
|
|
|
|
|
// XRun detection |
|
|
|
if (ioctl(fInFD, SNDCTL_DSP_GETERROR, &ei_in) == 0) { |
|
|
|
if (ioctl(fDriver->fInFD, SNDCTL_DSP_GETERROR, &ei_in) == 0) { |
|
|
|
|
|
|
|
if (ei_in.rec_overruns > 0 ) { |
|
|
|
jack_error("JackBoomerDriver::Read overruns"); |
|
|
|
jack_error("JackBoomerDriverInput::Execute overruns"); |
|
|
|
jack_time_t cur_time = GetMicroSeconds(); |
|
|
|
NotifyXRun(cur_time, float(cur_time - fBeginDateUst)); // Better this value than nothing... |
|
|
|
fDriver->NotifyXRun(cur_time, float(cur_time - fDriver->fBeginDateUst)); // Better this value than nothing... |
|
|
|
} |
|
|
|
|
|
|
|
if (ei_in.rec_errorcount > 0 && ei_in.rec_lasterror != 0) { |
|
|
|
@@ -594,18 +642,16 @@ int JackBoomerDriver::Read() |
|
|
|
} |
|
|
|
|
|
|
|
if (count < 0) { |
|
|
|
jack_log("JackBoomerDriver::Read error = %s", strerror(errno)); |
|
|
|
return -1; |
|
|
|
} else if (count < (int)fInputBufferSize) { |
|
|
|
jack_error("JackBoomerDriver::Read error bytes read = %ld", count); |
|
|
|
return -1; |
|
|
|
jack_log("JackBoomerDriverInput::Execute error = %s", strerror(errno)); |
|
|
|
} else if (count < (int)fDriver->fInputBufferSize) { |
|
|
|
jack_error("JackBoomerDriverInput::Execute error bytes read = %ld", count); |
|
|
|
} else { |
|
|
|
|
|
|
|
// Keep begin cycle time |
|
|
|
JackDriver::CycleTakeBeginTime(); |
|
|
|
for (int i = 0; i < fCaptureChannels; i++) { |
|
|
|
if (fGraphManager->GetConnectionsNum(fCapturePortList[i]) > 0) { |
|
|
|
CopyAndConvertIn(GetInputBuffer(i), fInputBuffer, fEngineControl->fBufferSize, i, fCaptureChannels, fBits); |
|
|
|
fDriver->CycleTakeBeginTime(); |
|
|
|
for (int i = 0; i < fDriver->fCaptureChannels; i++) { |
|
|
|
if (fDriver->fGraphManager->GetConnectionsNum(fDriver->fCapturePortList[i]) > 0) { |
|
|
|
CopyAndConvertIn(fDriver->GetInputBuffer(i), fDriver->fInputBuffer, fDriver->fEngineControl->fBufferSize, i, fDriver->fCaptureChannels, fDriver->fBits); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@@ -613,29 +659,17 @@ int JackBoomerDriver::Read() |
|
|
|
gCycleTable.fTable[gCycleReadCount].fAfterReadConvert = GetMicroSeconds(); |
|
|
|
gCycleReadCount = (gCycleReadCount == CYCLE_POINTS - 1) ? gCycleReadCount: gCycleReadCount + 1; |
|
|
|
#endif |
|
|
|
|
|
|
|
return 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int JackBoomerDriver::Write() |
|
|
|
{ |
|
|
|
for (int i = 0; i < fPlaybackChannels; i++) { |
|
|
|
if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) { |
|
|
|
if (jack_ringbuffer_write(fRingBuffer[i], (char*)GetOutputBuffer(i), fOutputBufferSize) < fOutputBufferSize) { |
|
|
|
jack_error("JackBoomerDriver::Write ringbuffer full"); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return 0; |
|
|
|
fDriver->SynchronizeRead(); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool JackBoomerDriver::Init() |
|
|
|
bool JackBoomerDriver::JackBoomerDriverOutput::Init() |
|
|
|
{ |
|
|
|
if (IsRealTime()) { |
|
|
|
jack_log("JackBoomerDriver::Init IsRealTime"); |
|
|
|
if (fThread.AcquireRealTime(GetEngineControl()->fServerPriority) < 0) { |
|
|
|
if (fDriver->IsRealTime()) { |
|
|
|
jack_log("JackBoomerDriverOutput::Init IsRealTime"); |
|
|
|
if (fDriver->fOutputThread.AcquireRealTime(GetEngineControl()->fServerPriority) < 0) { |
|
|
|
jack_error("AcquireRealTime error"); |
|
|
|
} else { |
|
|
|
set_threaded_log_function(); |
|
|
|
@@ -643,61 +677,40 @@ bool JackBoomerDriver::Init() |
|
|
|
} |
|
|
|
|
|
|
|
// Maybe necessary to write an empty output buffer first time : see http://manuals.opensound.com/developer/fulldup.c.html |
|
|
|
memset(fOutputBuffer, 0, fOutputBufferSize); |
|
|
|
memset(fDriver->fOutputBuffer, 0, fDriver->fOutputBufferSize); |
|
|
|
|
|
|
|
// Prefill ouput buffer |
|
|
|
if (fOutFD > 0) { |
|
|
|
for (int i = 0; i < fNperiods; i++) { |
|
|
|
ssize_t count = ::write(fOutFD, fOutputBuffer, fOutputBufferSize); |
|
|
|
if (count < (int)fOutputBufferSize) { |
|
|
|
if (fDriver->fOutFD > 0) { |
|
|
|
for (int i = 0; i < fDriver->fNperiods; i++) { |
|
|
|
ssize_t count = ::write(fDriver->fOutFD, fDriver->fOutputBuffer, fDriver->fOutputBufferSize); |
|
|
|
if (count < (int)fDriver->fOutputBufferSize) { |
|
|
|
jack_error("JackBoomerDriver::Write error bytes written = %ld", count); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
int delay; |
|
|
|
if (ioctl(fOutFD, SNDCTL_DSP_GETODELAY, &delay) == -1) { |
|
|
|
if (ioctl(fDriver->fOutFD, SNDCTL_DSP_GETODELAY, &delay) == -1) { |
|
|
|
jack_error("JackBoomerDriver::Write error get out delay : %s@%i, errno = %d", __FILE__, __LINE__, errno); |
|
|
|
} |
|
|
|
|
|
|
|
delay /= fSampleSize * fPlaybackChannels; |
|
|
|
delay /= fDriver->fSampleSize * fDriver->fPlaybackChannels; |
|
|
|
jack_info("JackBoomerDriver::Write output latency frames = %ld", delay); |
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
bool JackBoomerDriver::Execute() |
|
|
|
bool JackBoomerDriver::JackBoomerDriverOutput::Execute() |
|
|
|
{ |
|
|
|
memset(fOutputBuffer, 0, fOutputBufferSize); |
|
|
|
memset(fDriver->fOutputBuffer, 0, fDriver->fOutputBufferSize); |
|
|
|
|
|
|
|
#ifdef JACK_MONITOR |
|
|
|
gCycleTable.fTable[gCycleWriteCount].fBeforeWriteConvert = GetMicroSeconds(); |
|
|
|
#endif |
|
|
|
|
|
|
|
for (int i = 0; i < fPlaybackChannels; i++) { |
|
|
|
if (fGraphManager->GetConnectionsNum(fPlaybackPortList[i]) > 0) { |
|
|
|
|
|
|
|
jack_ringbuffer_data_t ring_buffer_data[2]; |
|
|
|
jack_ringbuffer_get_read_vector(fRingBuffer[i], ring_buffer_data); |
|
|
|
|
|
|
|
unsigned int available_frames = (ring_buffer_data[0].len + ring_buffer_data[1].len) / sizeof(float); |
|
|
|
jack_log("Output available = %ld", available_frames); |
|
|
|
|
|
|
|
unsigned int needed_bytes = fOutputBufferSize; |
|
|
|
float* output = (float*)fOutputBuffer; |
|
|
|
|
|
|
|
for (int j = 0; j < 2; j++) { |
|
|
|
unsigned int consumed_bytes = std::min(needed_bytes, ring_buffer_data[j].len); |
|
|
|
CopyAndConvertOut(output, (float*)ring_buffer_data[j].buf, consumed_bytes / sizeof(float), i, fPlaybackChannels, fBits); |
|
|
|
output += consumed_bytes / sizeof(float); |
|
|
|
needed_bytes -= consumed_bytes; |
|
|
|
} |
|
|
|
|
|
|
|
if (needed_bytes > 0) { |
|
|
|
jack_error("JackBoomerDriver::Execute missing bytes = %ld", needed_bytes); |
|
|
|
} |
|
|
|
|
|
|
|
jack_ringbuffer_read_advance(fRingBuffer[i], fOutputBufferSize - needed_bytes); |
|
|
|
for (int i = 0; i < fDriver->fPlaybackChannels; i++) { |
|
|
|
if (fDriver->fGraphManager->GetConnectionsNum(fDriver->fPlaybackPortList[i]) > 0) { |
|
|
|
CopyAndConvertOut(fDriver->fOutputBuffer, fDriver->GetOutputBuffer(i), fDriver->fEngineControl->fBufferSize, i, fDriver->fPlaybackChannels, fDriver->fBits); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@@ -705,23 +718,23 @@ bool JackBoomerDriver::Execute() |
|
|
|
gCycleTable.fTable[gCycleWriteCount].fBeforeWrite = GetMicroSeconds(); |
|
|
|
#endif |
|
|
|
|
|
|
|
ssize_t count = ::write(fOutFD, fOutputBuffer, fOutputBufferSize); |
|
|
|
ssize_t count = ::write(fDriver->fOutFD, fDriver->fOutputBuffer, fDriver->fOutputBufferSize); |
|
|
|
|
|
|
|
#ifdef JACK_MONITOR |
|
|
|
if (count > 0 && count != (int)fOutputBufferSize) |
|
|
|
jack_log("JackBoomerDriver::Execute count = %ld", count / (fSampleSize * fPlaybackChannels)); |
|
|
|
if (count > 0 && count != (int)fDriver->fOutputBufferSize) |
|
|
|
jack_log("JackBoomerDriverOutput::Execute count = %ld", count / (fDriver->fSampleSize * fDriver->fPlaybackChannels)); |
|
|
|
gCycleTable.fTable[gCycleWriteCount].fAfterWrite = GetMicroSeconds(); |
|
|
|
gCycleWriteCount = (gCycleWriteCount == CYCLE_POINTS - 1) ? gCycleWriteCount: gCycleWriteCount + 1; |
|
|
|
#endif |
|
|
|
|
|
|
|
// XRun detection |
|
|
|
audio_errinfo ei_out; |
|
|
|
if (ioctl(fOutFD, SNDCTL_DSP_GETERROR, &ei_out) == 0) { |
|
|
|
if (ioctl(fDriver->fOutFD, SNDCTL_DSP_GETERROR, &ei_out) == 0) { |
|
|
|
|
|
|
|
if (ei_out.play_underruns > 0) { |
|
|
|
jack_error("JackBoomerDriver::Execute underruns"); |
|
|
|
jack_error("JackBoomerDriverOutput::Execute underruns"); |
|
|
|
jack_time_t cur_time = GetMicroSeconds(); |
|
|
|
NotifyXRun(cur_time, float(cur_time - fBeginDateUst)); // Better this value than nothing... |
|
|
|
fDriver->NotifyXRun(cur_time, float(cur_time - fDriver->fBeginDateUst)); // Better this value than nothing... |
|
|
|
} |
|
|
|
|
|
|
|
if (ei_out.play_errorcount > 0 && ei_out.play_lasterror != 0) { |
|
|
|
@@ -730,14 +743,28 @@ bool JackBoomerDriver::Execute() |
|
|
|
} |
|
|
|
|
|
|
|
if (count < 0) { |
|
|
|
jack_log("JackBoomerDriver::Execute error = %s", strerror(errno)); |
|
|
|
} else if (count < (int)fOutputBufferSize) { |
|
|
|
jack_error("JackBoomerDriver::Execute error bytes written = %ld", count); |
|
|
|
jack_log("JackBoomerDriverOutput::Execute error = %s", strerror(errno)); |
|
|
|
} else if (count < (int)fDriver->fOutputBufferSize) { |
|
|
|
jack_error("JackBoomerDriverOutput::Execute error bytes written = %ld", count); |
|
|
|
} |
|
|
|
|
|
|
|
fDriver->SynchronizeWrite(); |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
void JackBoomerDriver::SynchronizeRead() |
|
|
|
{ |
|
|
|
sem_wait(&fWriteSema); |
|
|
|
Process(); |
|
|
|
sem_post(&fReadSema); |
|
|
|
} |
|
|
|
|
|
|
|
void JackBoomerDriver::SynchronizeWrite() |
|
|
|
{ |
|
|
|
sem_post(&fWriteSema); |
|
|
|
sem_wait(&fReadSema); |
|
|
|
} |
|
|
|
|
|
|
|
int JackBoomerDriver::SetBufferSize(jack_nframes_t buffer_size) |
|
|
|
{ |
|
|
|
CloseAux(); |
|
|
|
@@ -966,14 +993,13 @@ EXPORT Jack::JackDriverClientInterface* driver_initialize(Jack::JackLockedEngine |
|
|
|
} |
|
|
|
|
|
|
|
Jack::JackBoomerDriver* boomer_driver = new Jack::JackBoomerDriver("system", "boomer", engine, table); |
|
|
|
Jack::JackDriverClientInterface* threaded_driver = new Jack::JackThreadedDriver(boomer_driver); |
|
|
|
|
|
|
|
// Special open for Boomer driver... |
|
|
|
if (boomer_driver->Open(frames_per_interrupt, nperiods, srate, capture, playback, chan_in, chan_out, |
|
|
|
excl, monitor, capture_pcm_name, playback_pcm_name, systemic_input_latency, systemic_output_latency, bits, ignorehwbuf) == 0) { |
|
|
|
return threaded_driver; |
|
|
|
return boomer_driver; |
|
|
|
} else { |
|
|
|
delete threaded_driver; // Delete the decorated driver |
|
|
|
delete boomer_driver; // Delete the driver |
|
|
|
return NULL; |
|
|
|
} |
|
|
|
} |
|
|
|
|