From 8c9235528fdbdc58623e53d9e7125322a933e647 Mon Sep 17 00:00:00 2001 From: uraura00 Date: Thu, 31 Mar 2022 16:42:07 +0200 Subject: [PATCH] Possibility to calculate resampe ratio per channel in adapter to avoid delay buildup --- common/JackAudioAdapterInterface.cpp | 126 ++++++++++++++++----- common/JackAudioAdapterInterface.h | 19 ++-- common/JackFilters.h | 84 +------------- common/JackNetAPI.cpp | 6 + common/JackNetAdapter.cpp | 7 ++ common/JackResampler.h | 6 +- linux/alsa/JackAlsaAdapter.cpp | 6 + macosx/coreaudio/JackCoreAudioAdapter.mm | 8 ++ solaris/oss/JackOSSAdapter.cpp | 8 ++ windows/portaudio/JackPortAudioAdapter.cpp | 7 ++ 10 files changed, 163 insertions(+), 114 deletions(-) diff --git a/common/JackAudioAdapterInterface.cpp b/common/JackAudioAdapterInterface.cpp index ee90edd4a..29b2a4d49 100644 --- a/common/JackAudioAdapterInterface.cpp +++ b/common/JackAudioAdapterInterface.cpp @@ -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; } diff --git a/common/JackAudioAdapterInterface.h b/common/JackAudioAdapterInterface.h index 36be35aa8..2f9a71e22 100644 --- a/common/JackAudioAdapterInterface.h +++ b/common/JackAudioAdapterInterface.h @@ -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); diff --git a/common/JackFilters.h b/common/JackFilters.h index 70611d3cb..9535a2f5f 100644 --- a/common/JackFilters.h +++ b/common/JackFilters.h @@ -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; - } - } - }; } diff --git a/common/JackNetAPI.cpp b/common/JackNetAPI.cpp index 09962ca71..42ce0708d 100644 --- a/common/JackNetAPI.cpp +++ b/common/JackNetAPI.cpp @@ -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(); } } diff --git a/common/JackNetAdapter.cpp b/common/JackNetAdapter.cpp index dc254a15f..7e28af72d 100644 --- a/common/JackNetAdapter.cpp +++ b/common/JackNetAdapter.cpp @@ -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; } diff --git a/common/JackResampler.h b/common/JackResampler.h index 74c75a26c..fa9902cf4 100644 --- a/common/JackResampler.h +++ b/common/JackResampler.h @@ -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); + } }; diff --git a/linux/alsa/JackAlsaAdapter.cpp b/linux/alsa/JackAlsaAdapter.cpp index 25686c08f..2a56dc546 100644 --- a/linux/alsa/JackAlsaAdapter.cpp +++ b/linux/alsa/JackAlsaAdapter.cpp @@ -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; } diff --git a/macosx/coreaudio/JackCoreAudioAdapter.mm b/macosx/coreaudio/JackCoreAudioAdapter.mm index 2ea8cfbd8..5448c407a 100644 --- a/macosx/coreaudio/JackCoreAudioAdapter.mm +++ b/macosx/coreaudio/JackCoreAudioAdapter.mm @@ -409,6 +409,10 @@ static void printError(OSStatus err) case 's': fClockDriftCompensate = true; break; + + case 'R': + fResampleRatioPerChannel = true; + break; } } @@ -1745,6 +1749,10 @@ static CFStringRef GetDeviceName(AudioDeviceID id) 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; } diff --git a/solaris/oss/JackOSSAdapter.cpp b/solaris/oss/JackOSSAdapter.cpp index 552f00107..6afd81dd8 100644 --- a/solaris/oss/JackOSSAdapter.cpp +++ b/solaris/oss/JackOSSAdapter.cpp @@ -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; } diff --git a/windows/portaudio/JackPortAudioAdapter.cpp b/windows/portaudio/JackPortAudioAdapter.cpp index 09013588a..72e27e7ac 100644 --- a/windows/portaudio/JackPortAudioAdapter.cpp +++ b/windows/portaudio/JackPortAudioAdapter.cpp @@ -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; }