git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@3417 0c269be4-1314-0410-8aa9-9f06e86f4224tags/1.9.2
| @@ -17,12 +17,17 @@ Nedko Arnaudov | |||
| Fernando Lopez-Lezcano | |||
| Romain Moret | |||
| Florian Faber | |||
| Michael Voigt | |||
| Michael Voigt | |||
| Torben Hohn | |||
| --------------------------- | |||
| Jackdmp changes log | |||
| --------------------------- | |||
| 2009-03-09 Stephane Letz <letz@grame.fr> | |||
| * Use Torben Hohn PI controler code for adapters (in progress). | |||
| 2009-03-05 Stephane Letz <letz@grame.fr> | |||
| * Support for BIG_ENDIAN machines in NetJack2 for transport data. | |||
| @@ -53,6 +53,7 @@ namespace Jack | |||
| } | |||
| fclose(file); | |||
| /* No used for now | |||
| // Adapter timing 1 | |||
| file = fopen("AdapterTiming1.plot", "w"); | |||
| fprintf(file, "set multiplot\n"); | |||
| @@ -83,6 +84,7 @@ namespace Jack | |||
| fprintf(file, buffer); | |||
| fclose(file); | |||
| */ | |||
| // Adapter timing 2 | |||
| file = fopen("AdapterTiming2.plot", "w"); | |||
| @@ -157,30 +159,7 @@ namespace Jack | |||
| fPlaybackRingBuffer[i]->Reset(); | |||
| } | |||
| void JackAudioAdapterInterface::ResampleFactor ( jack_time_t& frame1, jack_time_t& frame2 ) | |||
| { | |||
| jack_time_t time = GetMicroSeconds(); | |||
| if (!fRunning) { | |||
| // Init DLL | |||
| fRunning = true; | |||
| fHostDLL.Init(time); | |||
| fAdaptedDLL.Init(time); | |||
| frame1 = 1; | |||
| frame2 = 1; | |||
| } else { | |||
| // DLL | |||
| fAdaptedDLL.IncFrame(time); | |||
| jack_nframes_t time1 = fHostDLL.Time2Frames(time); | |||
| jack_nframes_t time2 = fAdaptedDLL.Time2Frames(time); | |||
| frame1 = time1; | |||
| frame2 = time2; | |||
| jack_log("JackAudioAdapterInterface::ResampleFactor time1 = %ld time2 = %ld src_ratio_input = %f src_ratio_output = %f", | |||
| long(time1), long(time2), double(time1) / double(time2), double(time2) / double(time1)); | |||
| } | |||
| } | |||
| void JackAudioAdapterInterface::Reset() | |||
| void JackAudioAdapterInterface::Reset() | |||
| { | |||
| ResetRingBuffers(); | |||
| fRunning = false; | |||
| @@ -216,25 +195,31 @@ namespace Jack | |||
| int JackAudioAdapterInterface::PushAndPull(float** inputBuffer, float** outputBuffer, unsigned int inNumberFrames) | |||
| { | |||
| bool failure = false; | |||
| jack_time_t time1, time2; | |||
| ResampleFactor(time1, time2); | |||
| fRunning = true; | |||
| /* | |||
| Finer estimation of the position in the ringbuffer ?? | |||
| int delta_frames = (int)(float(long(GetMicroSeconds() - fPullAndPushTime)) * float(fAdaptedSampleRate)) / 1000000.f; | |||
| double ratio = fPIControler.GetRatio(fCaptureRingBuffer[0]->GetOffset() - delta_frames); | |||
| */ | |||
| double ratio = fPIControler.GetRatio(fCaptureRingBuffer[0]->GetOffset()); | |||
| // Push/pull from ringbuffer | |||
| for (int i = 0; i < fCaptureChannels; i++) { | |||
| fCaptureRingBuffer[i]->SetRatio(time1, time2); | |||
| fCaptureRingBuffer[i]->SetRatio(ratio); | |||
| if (fCaptureRingBuffer[i]->WriteResample(inputBuffer[i], inNumberFrames) < inNumberFrames) | |||
| failure = true; | |||
| } | |||
| for (int i = 0; i < fPlaybackChannels; i++) { | |||
| fPlaybackRingBuffer[i]->SetRatio(time2, time1); | |||
| fPlaybackRingBuffer[i]->SetRatio(1 / ratio); | |||
| if (fPlaybackRingBuffer[i]->ReadResample(outputBuffer[i], inNumberFrames) < inNumberFrames) | |||
| failure = true; | |||
| } | |||
| #ifdef JACK_MONITOR | |||
| fTable.Write(time1, time2, double(time1) / double(time2), double(time2) / double(time1), | |||
| fCaptureRingBuffer[0]->ReadSpace(), fPlaybackRingBuffer[0]->WriteSpace()); | |||
| fTable.Write(0, 0, ratio, 1/ratio, fCaptureRingBuffer[0]->ReadSpace(), fPlaybackRingBuffer[0]->WriteSpace()); | |||
| #endif | |||
| // Reset all ringbuffers in case of failure | |||
| @@ -250,8 +235,8 @@ namespace Jack | |||
| int JackAudioAdapterInterface::PullAndPush(float** inputBuffer, float** outputBuffer, unsigned int inNumberFrames) | |||
| { | |||
| bool failure = false; | |||
| fHostDLL.IncFrame(GetMicroSeconds()); | |||
| fPullAndPushTime = GetMicroSeconds(); | |||
| // Push/pull from ringbuffer | |||
| for (int i = 0; i < fCaptureChannels; i++) { | |||
| if (fCaptureRingBuffer[i]->Read(inputBuffer[i], inNumberFrames) < inNumberFrames) | |||
| @@ -82,20 +82,19 @@ namespace Jack | |||
| jack_nframes_t fAdaptedBufferSize; | |||
| jack_nframes_t fAdaptedSampleRate; | |||
| //delay locked loop | |||
| JackAtomicDelayLockedLoop fHostDLL; | |||
| JackAtomicDelayLockedLoop fAdaptedDLL; | |||
| //PI controler | |||
| JackPIControler fPIControler; | |||
| JackResampler** fCaptureRingBuffer; | |||
| JackResampler** fPlaybackRingBuffer; | |||
| unsigned int fQuality; | |||
| unsigned int fRingbufferSize; | |||
| jack_time_t fPullAndPushTime; | |||
| bool fRunning; | |||
| void ResetRingBuffers(); | |||
| void ResampleFactor ( jack_time_t& frame1, jack_time_t& frame2 ); | |||
| public: | |||
| @@ -106,10 +105,10 @@ namespace Jack | |||
| fHostSampleRate ( sample_rate ), | |||
| fAdaptedBufferSize ( buffer_size), | |||
| fAdaptedSampleRate ( sample_rate ), | |||
| fHostDLL ( buffer_size, sample_rate ), | |||
| fAdaptedDLL ( buffer_size, sample_rate ), | |||
| fPIControler(sample_rate / sample_rate, 256), | |||
| fCaptureRingBuffer(NULL), fPlaybackRingBuffer(NULL), | |||
| fQuality(0), fRingbufferSize(DEFAULT_RB_SIZE), | |||
| fPullAndPushTime(0), | |||
| fRunning(false) | |||
| {} | |||
| @@ -139,14 +138,12 @@ namespace Jack | |||
| virtual int SetHostBufferSize ( jack_nframes_t buffer_size ) | |||
| { | |||
| fHostBufferSize = buffer_size; | |||
| fHostDLL.Init ( fHostBufferSize, fHostSampleRate ); | |||
| return 0; | |||
| } | |||
| virtual int SetAdaptedBufferSize ( jack_nframes_t buffer_size ) | |||
| { | |||
| fAdaptedBufferSize = buffer_size; | |||
| fAdaptedDLL.Init ( fAdaptedBufferSize, fAdaptedSampleRate ); | |||
| return 0; | |||
| } | |||
| @@ -160,14 +157,14 @@ namespace Jack | |||
| virtual int SetHostSampleRate ( jack_nframes_t sample_rate ) | |||
| { | |||
| fHostSampleRate = sample_rate; | |||
| fHostDLL.Init ( fHostBufferSize, fHostSampleRate ); | |||
| fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
| return 0; | |||
| } | |||
| virtual int SetAdaptedSampleRate ( jack_nframes_t sample_rate ) | |||
| { | |||
| fAdaptedSampleRate = sample_rate; | |||
| fAdaptedDLL.Init ( fAdaptedBufferSize, fAdaptedSampleRate ); | |||
| fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||
| return 0; | |||
| } | |||
| @@ -23,6 +23,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| #include "jack.h" | |||
| #include "JackAtomicState.h" | |||
| #include <math.h> | |||
| #include <stdlib.h> | |||
| namespace Jack | |||
| { | |||
| @@ -201,6 +202,117 @@ namespace Jack | |||
| return res; | |||
| } | |||
| }; | |||
| /* | |||
| Torben Hohn PI controler from JACK1 | |||
| */ | |||
| struct JackPIControler { | |||
| 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) | |||
| { | |||
| 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() | |||
| { | |||
| 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) | |||
| { | |||
| 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 straigth 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 doesnt 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; | |||
| } | |||
| 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; | |||
| } | |||
| } | |||
| }; | |||
| } | |||
| @@ -23,7 +23,7 @@ namespace Jack | |||
| { | |||
| JackLibSampleRateResampler::JackLibSampleRateResampler() | |||
| :JackResampler(),fRatio(1) | |||
| :JackResampler() | |||
| { | |||
| int error; | |||
| fResampler = src_new(SRC_LINEAR, 1, &error); | |||
| @@ -32,7 +32,7 @@ JackLibSampleRateResampler::JackLibSampleRateResampler() | |||
| } | |||
| JackLibSampleRateResampler::JackLibSampleRateResampler(unsigned int quality, unsigned int ringbuffer_size) | |||
| :JackResampler(ringbuffer_size),fRatio(1) | |||
| :JackResampler(ringbuffer_size) | |||
| { | |||
| switch (quality) { | |||
| case 0: | |||
| @@ -26,11 +26,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| namespace Jack | |||
| { | |||
| inline float Range(float min, float max, float val) | |||
| { | |||
| return (val < min) ? min : ((val > max) ? max : val); | |||
| } | |||
| /*! | |||
| \brief Resampler using "libsamplerate" (http://www.mega-nerd.com/SRC/). | |||
| */ | |||
| @@ -41,7 +36,6 @@ class JackLibSampleRateResampler : public JackResampler | |||
| private: | |||
| SRC_STATE* fResampler; | |||
| double fRatio; | |||
| public: | |||
| @@ -51,12 +45,6 @@ class JackLibSampleRateResampler : public JackResampler | |||
| unsigned int ReadResample(float* buffer, unsigned int frames); | |||
| unsigned int WriteResample(float* buffer, unsigned int frames); | |||
| void SetRatio(unsigned int num, unsigned int denom) | |||
| { | |||
| JackResampler::SetRatio(num, denom); | |||
| fRatio = Range(0.25, 4.0, (double(num) / double(denom))); | |||
| } | |||
| void Reset(); | |||
| @@ -23,13 +23,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| namespace Jack | |||
| { | |||
| JackResampler::JackResampler():fNum(1),fDenom(1) | |||
| JackResampler::JackResampler() | |||
| :fRatio(1),fRingBufferSize(DEFAULT_RB_SIZE) | |||
| { | |||
| fRingBuffer = jack_ringbuffer_create(sizeof(float) * DEFAULT_RB_SIZE); | |||
| jack_ringbuffer_read_advance(fRingBuffer, (sizeof(float) * DEFAULT_RB_SIZE) / 2); | |||
| fRingBuffer = jack_ringbuffer_create(sizeof(float) * fRingBufferSize); | |||
| jack_ringbuffer_read_advance(fRingBuffer, (sizeof(float) * fRingBufferSize) / 2); | |||
| } | |||
| JackResampler::JackResampler(unsigned int ringbuffer_size):fNum(1),fDenom(1),fRingBufferSize(ringbuffer_size) | |||
| JackResampler::JackResampler(unsigned int ringbuffer_size) | |||
| :fRatio(1),fRingBufferSize(ringbuffer_size) | |||
| { | |||
| fRingBuffer = jack_ringbuffer_create(sizeof(float) * fRingBufferSize); | |||
| jack_ringbuffer_read_advance(fRingBuffer, (sizeof(float) * fRingBufferSize) / 2); | |||
| @@ -26,8 +26,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||
| namespace Jack | |||
| { | |||
| #define DEFAULT_RB_SIZE 16384 * 2 | |||
| #define DEFAULT_RB_SIZE 16384 | |||
| inline float Range(float min, float max, float val) | |||
| { | |||
| return (val < min) ? min : ((val > max) ? max : val); | |||
| } | |||
| /*! | |||
| \brief Base class for Resampler. | |||
| */ | |||
| @@ -38,8 +43,7 @@ class JackResampler | |||
| protected: | |||
| jack_ringbuffer_t* fRingBuffer; | |||
| unsigned int fNum; | |||
| unsigned int fDenom; | |||
| double fRatio; | |||
| unsigned int fRingBufferSize; | |||
| public: | |||
| @@ -58,16 +62,20 @@ class JackResampler | |||
| virtual unsigned int ReadSpace(); | |||
| virtual unsigned int WriteSpace(); | |||
| unsigned int GetOffset() | |||
| { | |||
| return (jack_ringbuffer_read_space(fRingBuffer) / sizeof(float)) - (fRingBufferSize / 2); | |||
| } | |||
| virtual void SetRatio(unsigned int num, unsigned int denom) | |||
| void SetRatio(double ratio) | |||
| { | |||
| fNum = num; | |||
| fDenom = denom; | |||
| fRatio = Range(0.25, 4.0, ratio); | |||
| } | |||
| virtual void GetRatio(unsigned int& num, unsigned int& denom) | |||
| double GetRatio() | |||
| { | |||
| num = fNum; | |||
| denom = fDenom; | |||
| return fRatio; | |||
| } | |||
| }; | |||