@@ -67,8 +67,8 @@ namespace Jack | |||
fprintf(file, "set xlabel \"audio cycles\"\n"); | |||
fprintf(file, "set ylabel \"frames\"\n"); | |||
fprintf(file, "plot "); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 2 title \"Ringbuffer error\" with lines,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 3 title \"Ringbuffer error with timing correction\" with lines"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 2 title \"Ringbuffer error\" with dots,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 3 title \"Ringbuffer error with timing correction\" with dots"); | |||
fprintf(file, "\n unset multiplot\n"); | |||
fprintf(file, "set output 'AdapterTiming1.svg\n"); | |||
@@ -81,8 +81,8 @@ namespace Jack | |||
fprintf(file, "set xlabel \"audio cycles\"\n"); | |||
fprintf(file, "set ylabel \"frames\"\n"); | |||
fprintf(file, "plot "); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 2 title \"Consumer interrupt period\" with lines,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 3 title \"Producer interrupt period\" with lines\n"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 2 title \"Consumer interrupt period\" with dots,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 3 title \"Producer interrupt period\" with dots\n"); | |||
fprintf(file, "unset multiplot\n"); | |||
fprintf(file, "unset output\n"); | |||
@@ -97,8 +97,8 @@ namespace Jack | |||
fprintf(file, "set xlabel \"audio cycles\"\n"); | |||
fprintf(file, "set ylabel \"resampling ratio\"\n"); | |||
fprintf(file, "plot "); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with lines,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with lines"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with dots,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with dots"); | |||
fprintf(file, "\n unset multiplot\n"); | |||
fprintf(file, "set output 'AdapterTiming2.svg\n"); | |||
@@ -111,8 +111,8 @@ namespace Jack | |||
fprintf(file, "set xlabel \"audio cycles\"\n"); | |||
fprintf(file, "set ylabel \"resampling ratio\"\n"); | |||
fprintf(file, "plot "); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with lines,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with lines\n"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 4 title \"Ratio 1\" with dots,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 5 title \"Ratio 2\" with dots\n"); | |||
fprintf(file, "unset multiplot\n"); | |||
fprintf(file, "unset output\n"); | |||
@@ -127,8 +127,8 @@ namespace Jack | |||
fprintf(file, "set xlabel \"audio cycles\"\n"); | |||
fprintf(file, "set ylabel \"frames\"\n"); | |||
fprintf(file, "plot "); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with lines,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with lines"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with dots,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with dots"); | |||
fprintf(file, "\n unset multiplot\n"); | |||
fprintf(file, "set output 'AdapterTiming3.svg\n"); | |||
@@ -141,8 +141,8 @@ namespace Jack | |||
fprintf(file, "set xlabel \"audio cycles\"\n"); | |||
fprintf(file, "set ylabel \"frames\"\n"); | |||
fprintf(file, "plot "); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with lines,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with lines\n"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 6 title \"Frames position in consumer ringbuffer\" with dots,"); | |||
fprintf(file, "\"JackAudioAdapter.log\" using 7 title \"Frames position in producer ringbuffer\" with dots\n"); | |||
fprintf(file, "unset multiplot\n"); | |||
fprintf(file, "unset output\n"); | |||
@@ -173,9 +173,11 @@ namespace Jack | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); | |||
fPIControllerCapture[i]->Reset(); | |||
} | |||
for (int i = 0; i < fPlaybackChannels; i++) { | |||
fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); | |||
fPIControllerPlayback[i]->Reset(); | |||
} | |||
} | |||
@@ -191,9 +193,11 @@ namespace Jack | |||
#else | |||
void JackAudioAdapterInterface::Create() | |||
{ | |||
//ringbuffers | |||
fCaptureRingBuffer = new JackResampler*[fCaptureChannels]; | |||
fPIControllerCapture = new JackPIController*[fCaptureChannels]; | |||
fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels]; | |||
fPIControllerPlayback = new JackPIController*[fPlaybackChannels]; | |||
if (fAdaptative) { | |||
AdaptRingBufferSize(); | |||
@@ -205,13 +209,22 @@ namespace Jack | |||
jack_info("Fixed ringbuffer size = %d frames", fRingbufferCurSize); | |||
} | |||
if (fResampleRatioPerChannel){ | |||
jack_info("Resample ratio tracked independently for each capture and playback channel"); | |||
} else { | |||
jack_info("Shared resample ratio tracking for all capture and playback channels"); | |||
} | |||
for (int i = 0; i < fCaptureChannels; i++ ) { | |||
fCaptureRingBuffer[i] = new JackLibSampleRateResampler(fQuality); | |||
fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); | |||
fPIControllerCapture[i] = new JackPIController(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
} | |||
for (int i = 0; i < fPlaybackChannels; i++ ) { | |||
fPlaybackRingBuffer[i] = new JackLibSampleRateResampler(fQuality); | |||
fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); | |||
fPIControllerPlayback[i] = new JackPIController(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
} | |||
if (fCaptureChannels > 0) { | |||
@@ -227,40 +240,79 @@ namespace Jack | |||
{ | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
delete(fCaptureRingBuffer[i]); | |||
delete(fPIControllerCapture[i]); | |||
} | |||
for (int i = 0; i < fPlaybackChannels; i++) { | |||
delete (fPlaybackRingBuffer[i]); | |||
delete(fPIControllerPlayback[i]); | |||
} | |||
delete[] fCaptureRingBuffer; | |||
delete [] fPIControllerCapture; | |||
delete[] fPlaybackRingBuffer; | |||
delete [] fPIControllerPlayback; | |||
} | |||
double JackAudioAdapterInterface::GetSharedRatio(int delta_frames) | |||
{ | |||
if (0 < fCaptureChannels) { | |||
return fPIControllerCapture[0]->GetRatio(fCaptureRingBuffer[0]->GetReadBalance() - delta_frames); | |||
} | |||
else if (0 < fPlaybackChannels) { | |||
return fPIControllerPlayback[0]->GetRatio(fPlaybackRingBuffer[0]->GetReadBalance() - delta_frames); | |||
} | |||
return 1; | |||
} | |||
int JackAudioAdapterInterface::PushAndPull(float** inputBuffer, float** outputBuffer, unsigned int frames) | |||
{ | |||
// Most of the time we can use a shared ratio calculated on a single input or output channel ringbuffer and apply this ratio also to all other channels. | |||
// In some situations we see that there is a drift between the channels coming from or going to the same device. | |||
// On those devices we need to activate the fResampleRatioPerChannel option so that every channel will calculate and keep track of its own resample ratio | |||
// in order to keep his ringbuffer in a good balance which is half used for reading and half used for writing. | |||
// Not using this option when it is needed will result in buffer errors followed by resets for all the buffers. | |||
// cfr. https://github.com/jackaudio/jack2/issues/534 | |||
bool failure = false; | |||
fRunning = true; | |||
// Finer estimation of the position in the ringbuffer | |||
int delta_frames = (fPullAndPushTime > 0) ? (int)((float(long(GetMicroSeconds() - fPullAndPushTime)) * float(fAdaptedSampleRate)) / 1000000.f) : 0; | |||
double ratio = 1; | |||
// TODO : done like this just to avoid crash when input only or output only... | |||
if (fCaptureChannels > 0) { | |||
ratio = fPIControler.GetRatio(fCaptureRingBuffer[0]->GetError() - delta_frames); | |||
} else if (fPlaybackChannels > 0) { | |||
ratio = fPIControler.GetRatio(fPlaybackRingBuffer[0]->GetError() - delta_frames); | |||
double ratioC[fCaptureChannels]; | |||
double ratioP[fPlaybackChannels]; | |||
if(!fResampleRatioPerChannel) { | |||
double shared_ratio = GetSharedRatio(delta_frames); | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
ratioC[i] = shared_ratio; | |||
} | |||
for (int i = 0; i < fPlaybackChannels; i++) { | |||
ratioP[i] = shared_ratio; | |||
} | |||
#ifdef JACK_MONITOR | |||
if (fCaptureRingBuffer && fCaptureRingBuffer[0] != NULL) { | |||
fTable.Write(fCaptureRingBuffer[0]->GetReadBalance(), fCaptureRingBuffer[0]->GetReadBalance() - delta_frames, shared_ratio, 1/shared_ratio, fCaptureRingBuffer[0]->ReadSpace(), fCaptureRingBuffer[0]->ReadSpace()); | |||
} | |||
#endif // JACK_MONITOR | |||
} | |||
else { | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
ratioC[i] = fPIControllerCapture[i]->GetRatio(fCaptureRingBuffer[i]->GetReadBalance() - delta_frames); | |||
} | |||
for (int i = 0; i < fPlaybackChannels; i++) { | |||
ratioP[i] = fPIControllerPlayback[i]->GetRatio(fPlaybackRingBuffer[i]->GetWriteBalance() + delta_frames); | |||
} | |||
#ifdef JACK_MONITOR | |||
if (fCaptureRingBuffer && fCaptureRingBuffer[0] != NULL) { | |||
fTable.Write(fCaptureRingBuffer[0]->GetReadBalance(), fCaptureRingBuffer[0]->GetReadBalance() - delta_frames, ratioC[0], 1/ratioC[0], fCaptureRingBuffer[0]->ReadSpace(), fCaptureRingBuffer[0]->ReadSpace()); | |||
} | |||
#endif // JACK_MONITOR | |||
} | |||
#ifdef JACK_MONITOR | |||
if (fCaptureRingBuffer && fCaptureRingBuffer[0] != NULL) | |||
fTable.Write(fCaptureRingBuffer[0]->GetError(), fCaptureRingBuffer[0]->GetError() - delta_frames, ratio, 1/ratio, fCaptureRingBuffer[0]->ReadSpace(), fCaptureRingBuffer[0]->ReadSpace()); | |||
#endif | |||
// Push/pull from ringbuffer | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
fCaptureRingBuffer[i]->SetRatio(ratio); | |||
fCaptureRingBuffer[i]->SetRatio(ratioC[i]); | |||
if (inputBuffer[i]) { | |||
if (fCaptureRingBuffer[i]->WriteResample(inputBuffer[i], frames) < frames) { | |||
failure = true; | |||
@@ -269,7 +321,7 @@ namespace Jack | |||
} | |||
for (int i = 0; i < fPlaybackChannels; i++) { | |||
fPlaybackRingBuffer[i]->SetRatio(1/ratio); | |||
fPlaybackRingBuffer[i]->SetRatio(1 / ratioP[i]); | |||
if (outputBuffer[i]) { | |||
if (fPlaybackRingBuffer[i]->ReadResample(outputBuffer[i], frames) < frames) { | |||
failure = true; | |||
@@ -347,14 +399,32 @@ namespace Jack | |||
int JackAudioAdapterInterface::SetHostSampleRate(jack_nframes_t sample_rate) | |||
{ | |||
fHostSampleRate = sample_rate; | |||
fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
if (NULL != fPIControllerCapture) { | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
fPIControllerCapture[i]->Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
} | |||
} | |||
if (NULL != fPIControllerPlayback) { | |||
for (int i = 0; i < fPlaybackChannels; i++) { | |||
fPIControllerPlayback[i]->Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
} | |||
} | |||
return 0; | |||
} | |||
int JackAudioAdapterInterface::SetAdaptedSampleRate(jack_nframes_t sample_rate) | |||
{ | |||
fAdaptedSampleRate = sample_rate; | |||
fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
if (NULL != fPIControllerCapture) { | |||
for (int i = 0; i < fCaptureChannels; i++) { | |||
fPIControllerCapture[i]->Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
} | |||
} | |||
if (NULL != fPIControllerPlayback) { | |||
for (int i = 0; i < fPlaybackChannels; i++ ) { | |||
fPIControllerPlayback[i]->Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
} | |||
} | |||
return 0; | |||
} | |||
@@ -82,9 +82,9 @@ namespace Jack | |||
jack_nframes_t fAdaptedBufferSize; | |||
jack_nframes_t fAdaptedSampleRate; | |||
//PI controller | |||
JackPIControler fPIControler; | |||
//PI controler | |||
JackPIController** fPIControllerCapture; | |||
JackPIController** fPIControllerPlayback; | |||
JackResampler** fCaptureRingBuffer; | |||
JackResampler** fPlaybackRingBuffer; | |||
@@ -94,6 +94,7 @@ namespace Jack | |||
bool fRunning; | |||
bool fAdaptative; | |||
bool fResampleRatioPerChannel; | |||
void ResetRingBuffers(); | |||
void AdaptRingBufferSize(); | |||
@@ -108,13 +109,14 @@ namespace Jack | |||
fHostSampleRate(sample_rate), | |||
fAdaptedBufferSize(buffer_size), | |||
fAdaptedSampleRate(sample_rate), | |||
fPIControler(sample_rate / sample_rate, 256), | |||
fPIControllerCapture(NULL), fPIControllerPlayback(NULL), | |||
fCaptureRingBuffer(NULL), fPlaybackRingBuffer(NULL), | |||
fQuality(0), | |||
fRingbufferCurSize(ring_buffer_size), | |||
fPullAndPushTime(0), | |||
fRunning(false), | |||
fAdaptative(true) | |||
fAdaptative(true), | |||
fResampleRatioPerChannel(false) | |||
{} | |||
JackAudioAdapterInterface(jack_nframes_t host_buffer_size, | |||
@@ -128,12 +130,14 @@ namespace Jack | |||
fHostSampleRate(host_sample_rate), | |||
fAdaptedBufferSize(adapted_buffer_size), | |||
fAdaptedSampleRate(adapted_sample_rate), | |||
fPIControler(host_sample_rate / host_sample_rate, 256), | |||
fPIControllerCapture(NULL), fPIControllerPlayback(NULL), | |||
fCaptureRingBuffer(NULL), fPlaybackRingBuffer(NULL), | |||
fQuality(0), | |||
fRingbufferCurSize(ring_buffer_size), | |||
fPullAndPushTime(0), | |||
fRunning(false), | |||
fAdaptative(true) | |||
fAdaptative(true), | |||
fResampleRatioPerChannel(false) | |||
{} | |||
virtual ~JackAudioAdapterInterface() | |||
@@ -168,6 +172,7 @@ namespace Jack | |||
virtual int GetInputLatency(int port_index) { return 0; } | |||
virtual int GetOutputLatency(int port_index) { return 0; } | |||
double GetSharedRatio(int delta_frames); | |||
int PushAndPull(jack_default_audio_sample_t** inputBuffer, jack_default_audio_sample_t** outputBuffer, unsigned int frames); | |||
int PullAndPush(jack_default_audio_sample_t** inputBuffer, jack_default_audio_sample_t** outputBuffer, unsigned int frames); | |||
@@ -220,102 +220,44 @@ namespace Jack | |||
#endif | |||
/* | |||
Torben Hohn PI controller from JACK1 | |||
Torben Hohn PI controler from JACK1 | |||
*/ | |||
struct JackPIControler { | |||
struct JackPIController { | |||
double resample_mean; | |||
double static_resample_factor; | |||
double* offset_array; | |||
double* window_array; | |||
int offset_differential_index; | |||
double offset_integral; | |||
double catch_factor; | |||
double catch_factor2; | |||
double pclamp; | |||
double controlquant; | |||
int smooth_size; | |||
double hann(double x) | |||
{ | |||
return 0.5 * (1.0 - cos(2 * M_PI * x)); | |||
} | |||
JackPIControler(double resample_factor, int fir_size) | |||
JackPIController(double resample_factor) | |||
{ | |||
resample_mean = resample_factor; | |||
static_resample_factor = resample_factor; | |||
offset_array = new double[fir_size]; | |||
window_array = new double[fir_size]; | |||
offset_differential_index = 0; | |||
offset_integral = 0.0; | |||
smooth_size = fir_size; | |||
for (int i = 0; i < fir_size; i++) { | |||
offset_array[i] = 0.0; | |||
window_array[i] = hann(double(i) / (double(fir_size) - 1.0)); | |||
} | |||
// These values could be configurable | |||
catch_factor = 100000; | |||
catch_factor2 = 10000; | |||
pclamp = 15.0; | |||
controlquant = 10000.0; | |||
} | |||
~JackPIControler() | |||
~JackPIController() | |||
{ | |||
delete[] offset_array; | |||
delete[] window_array; | |||
} | |||
void Init(double resample_factor) | |||
{ | |||
resample_mean = resample_factor; | |||
static_resample_factor = resample_factor; | |||
} | |||
/* | |||
double GetRatio(int fill_level) | |||
void Reset() | |||
{ | |||
double offset = fill_level; | |||
// Save offset. | |||
offset_array[(offset_differential_index++) % smooth_size] = offset; | |||
// Build the mean of the windowed offset array basically fir lowpassing. | |||
double smooth_offset = 0.0; | |||
for (int i = 0; i < smooth_size; i++) { | |||
smooth_offset += offset_array[(i + offset_differential_index - 1) % smooth_size] * window_array[i]; | |||
} | |||
smooth_offset /= double(smooth_size); | |||
// This is the integral of the smoothed_offset | |||
offset_integral += smooth_offset; | |||
// Clamp offset : the smooth offset still contains unwanted noise which would go straight onto the resample coeff. | |||
// It only used in the P component and the I component is used for the fine tuning anyways. | |||
if (fabs(smooth_offset) < pclamp) | |||
smooth_offset = 0.0; | |||
// Ok, now this is the PI controller. | |||
// u(t) = K * (e(t) + 1/T \int e(t') dt') | |||
// Kp = 1/catch_factor and T = catch_factor2 Ki = Kp/T | |||
double current_resample_factor | |||
= static_resample_factor - smooth_offset / catch_factor - offset_integral / catch_factor / catch_factor2; | |||
// Now quantize this value around resample_mean, so that the noise which is in the integral component doesn't hurt. | |||
current_resample_factor = floor((current_resample_factor - resample_mean) * controlquant + 0.5) / controlquant + resample_mean; | |||
// Calculate resample_mean so we can init ourselves to saner values. | |||
resample_mean = 0.9999 * resample_mean + 0.0001 * current_resample_factor; | |||
return current_resample_factor; | |||
offset_integral = 0.0; | |||
} | |||
*/ | |||
double GetRatio(int error) | |||
{ | |||
@@ -329,20 +271,6 @@ namespace Jack | |||
// Kp = 1/catch_factor and T = catch_factor2 Ki = Kp/T | |||
return static_resample_factor - smooth_offset/catch_factor - offset_integral/catch_factor/catch_factor2; | |||
} | |||
void OurOfBounds() | |||
{ | |||
int i; | |||
// Set the resample_rate... we need to adjust the offset integral, to do this. | |||
// first look at the PI controller, this code is just a special case, which should never execute once | |||
// everything is swung in. | |||
offset_integral = - (resample_mean - static_resample_factor) * catch_factor * catch_factor2; | |||
// Also clear the array. we are beginning a new control cycle. | |||
for (i = 0; i < smooth_size; i++) { | |||
offset_array[i] = 0.0; | |||
} | |||
} | |||
}; | |||
} | |||
@@ -995,9 +995,11 @@ struct JackNetAdapter : public JackAudioAdapterInterface { | |||
if (fCaptureChannels > 0) { | |||
fCaptureRingBuffer = new JackResampler*[fCaptureChannels]; | |||
fPIControllerCapture = new JackPIController*[fCaptureChannels]; | |||
} | |||
if (fPlaybackChannels > 0) { | |||
fPlaybackRingBuffer = new JackResampler*[fPlaybackChannels]; | |||
fPIControllerPlayback = new JackPIController*[fPlaybackChannels]; | |||
} | |||
if (fAdaptative) { | |||
@@ -1013,10 +1015,12 @@ struct JackNetAdapter : public JackAudioAdapterInterface { | |||
for (int i = 0; i < fCaptureChannels; i++ ) { | |||
fCaptureRingBuffer[i] = new JackResampler(); | |||
fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); | |||
fPIControllerCapture[i] = new JackPIController(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
} | |||
for (int i = 0; i < fPlaybackChannels; i++ ) { | |||
fPlaybackRingBuffer[i] = new JackResampler(); | |||
fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); | |||
fPIControllerPlayback[i] = new JackPIController(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
} | |||
if (fCaptureChannels > 0) { | |||
@@ -1036,9 +1040,11 @@ struct JackNetAdapter : public JackAudioAdapterInterface { | |||
{ | |||
for (int i = 0; i < fCaptureChannels; i++ ) { | |||
fCaptureRingBuffer[i]->Reset(fRingbufferCurSize); | |||
fPIControllerCapture[i]->Reset(); | |||
} | |||
for (int i = 0; i < fPlaybackChannels; i++ ) { | |||
fPlaybackRingBuffer[i]->Reset(fRingbufferCurSize); | |||
fPIControllerPlayback[i]->Reset(); | |||
} | |||
} | |||
@@ -124,6 +124,9 @@ namespace Jack | |||
fRingbufferCurSize = param->value.ui; | |||
fAdaptative = false; | |||
break; | |||
case 'R': | |||
fResampleRatioPerChannel = true; | |||
break; | |||
} | |||
} | |||
@@ -462,6 +465,10 @@ extern "C" | |||
value.i = false; | |||
jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect netadapter to system ports", NULL); | |||
value.i = false; | |||
jack_driver_descriptor_add_parameter(desc, &filler, "multi-ratios", 'R', JackDriverParamBool, &value, NULL, "Calculate resample ratio per channel" | |||
, "Calculate resample ratio per channel in case of repeated ringbuffer errors because of clock drift between the channels from the same device."); | |||
return desc; | |||
} | |||
@@ -65,10 +65,14 @@ class JackRingBuffer | |||
virtual unsigned int ReadSpace(); | |||
virtual unsigned int WriteSpace(); | |||
unsigned int GetError() | |||
unsigned int GetReadBalance() | |||
{ | |||
return (jack_ringbuffer_read_space(fRingBuffer) / sizeof(float)) - (fRingBufferSize / 2); | |||
} | |||
unsigned int GetWriteBalance() | |||
{ | |||
return (jack_ringbuffer_write_space(fRingBuffer) / sizeof(float)) - (fRingBufferSize / 2); | |||
} | |||
}; | |||
@@ -86,6 +86,9 @@ namespace Jack | |||
fRingbufferCurSize = param->value.ui; | |||
fAdaptative = false; | |||
break; | |||
case 'R': | |||
fResampleRatioPerChannel = true; | |||
break; | |||
} | |||
} | |||
@@ -228,6 +231,9 @@ extern "C" | |||
value.ui = 32768; | |||
jack_driver_descriptor_add_parameter(desc, &filler, "ring-buffer", 'g', JackDriverParamUInt, &value, NULL, "Fixed ringbuffer size", "Fixed ringbuffer size (if not set => automatic adaptative)"); | |||
value.i = false; | |||
jack_driver_descriptor_add_parameter(desc, &filler, "multi-ratios", 'R', JackDriverParamBool, &value, NULL, "Calculate resample ratio per channel" | |||
, "Calculate resample ratio per channel in case of repeated ringbuffer errors because of clock drift between the channels from the same device."); | |||
return desc; | |||
} | |||
@@ -409,6 +409,10 @@ JackCoreAudioAdapter::JackCoreAudioAdapter(jack_nframes_t buffer_size, jack_nfra | |||
case 's': | |||
fClockDriftCompensate = true; | |||
break; | |||
case 'R': | |||
fResampleRatioPerChannel = true; | |||
break; | |||
} | |||
} | |||
@@ -1745,6 +1749,10 @@ extern "C" | |||
value.i = false; | |||
jack_driver_descriptor_add_parameter(desc, &filler, "auto-connect", 'c', JackDriverParamBool, &value, NULL, "Auto connect audioadapter to system ports", NULL); | |||
value.i = false; | |||
jack_driver_descriptor_add_parameter(desc, &filler, "multi-ratios", 'R', JackDriverParamBool, &value, NULL, "Calculate resample ratio per channel" | |||
, "Calculate resample ratio per channel in case of repeated ringbuffer errors because of clock drift between the channels from the same device."); | |||
return desc; | |||
} | |||
@@ -187,6 +187,10 @@ JackOSSAdapter::JackOSSAdapter(jack_nframes_t buffer_size, jack_nframes_t sample | |||
fAdaptative = false; | |||
break; | |||
case 'R': | |||
fResampleRatioPerChannel = true; | |||
break; | |||
} | |||
} | |||
@@ -674,6 +678,10 @@ extern "C" | |||
value.i = 32768; | |||
jack_driver_descriptor_add_parameter(desc, &filler, "ring-buffer", 'g', JackDriverParamInt, &value, NULL, "Fixed ringbuffer size", "Fixed ringbuffer size (if not set => automatic adaptative)"); | |||
value.i = false; | |||
jack_driver_descriptor_add_parameter(desc, &filler, "multi-ratios", 'R', JackDriverParamBool, &value, NULL, "Calculate resample ratio per channel" | |||
, "Calculate resample ratio per channel in case of repeated ringbuffer errors because of clock drift between the channels from the same device."); | |||
return desc; | |||
} | |||
@@ -92,6 +92,9 @@ namespace Jack | |||
fRingbufferCurSize = param->value.ui; | |||
fAdaptative = false; | |||
break; | |||
case 'R': | |||
fResampleRatioPerChannel = true; | |||
break; | |||
} | |||
} | |||
@@ -235,6 +238,10 @@ extern "C" | |||
value.ui = 32768; | |||
jack_driver_descriptor_add_parameter(desc, &filler, "ring-buffer", 'g', JackDriverParamUInt, &value, NULL, "Fixed ringbuffer size", "Fixed ringbuffer size (if not set => automatic adaptative)"); | |||
value.i = false; | |||
jack_driver_descriptor_add_parameter(desc, &filler, "multi-ratios", 'R', JackDriverParamBool, &value, NULL, "Calculate resample ratio per channel" | |||
, "Calculate resample ratio per channel in case of repeated ringbuffer errors because of clock drift between the channels from the same device."); | |||
return desc; | |||
} | |||