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 | Fernando Lopez-Lezcano | ||||
Romain Moret | Romain Moret | ||||
Florian Faber | Florian Faber | ||||
Michael Voigt | |||||
Michael Voigt | |||||
Torben Hohn | |||||
--------------------------- | --------------------------- | ||||
Jackdmp changes log | 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> | 2009-03-05 Stephane Letz <letz@grame.fr> | ||||
* Support for BIG_ENDIAN machines in NetJack2 for transport data. | * Support for BIG_ENDIAN machines in NetJack2 for transport data. | ||||
@@ -53,6 +53,7 @@ namespace Jack | |||||
} | } | ||||
fclose(file); | fclose(file); | ||||
/* No used for now | |||||
// Adapter timing 1 | // Adapter timing 1 | ||||
file = fopen("AdapterTiming1.plot", "w"); | file = fopen("AdapterTiming1.plot", "w"); | ||||
fprintf(file, "set multiplot\n"); | fprintf(file, "set multiplot\n"); | ||||
@@ -83,6 +84,7 @@ namespace Jack | |||||
fprintf(file, buffer); | fprintf(file, buffer); | ||||
fclose(file); | fclose(file); | ||||
*/ | |||||
// Adapter timing 2 | // Adapter timing 2 | ||||
file = fopen("AdapterTiming2.plot", "w"); | file = fopen("AdapterTiming2.plot", "w"); | ||||
@@ -157,30 +159,7 @@ namespace Jack | |||||
fPlaybackRingBuffer[i]->Reset(); | 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(); | ResetRingBuffers(); | ||||
fRunning = false; | fRunning = false; | ||||
@@ -216,25 +195,31 @@ namespace Jack | |||||
int JackAudioAdapterInterface::PushAndPull(float** inputBuffer, float** outputBuffer, unsigned int inNumberFrames) | int JackAudioAdapterInterface::PushAndPull(float** inputBuffer, float** outputBuffer, unsigned int inNumberFrames) | ||||
{ | { | ||||
bool failure = false; | 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 | // Push/pull from ringbuffer | ||||
for (int i = 0; i < fCaptureChannels; i++) { | for (int i = 0; i < fCaptureChannels; i++) { | ||||
fCaptureRingBuffer[i]->SetRatio(time1, time2); | |||||
fCaptureRingBuffer[i]->SetRatio(ratio); | |||||
if (fCaptureRingBuffer[i]->WriteResample(inputBuffer[i], inNumberFrames) < inNumberFrames) | if (fCaptureRingBuffer[i]->WriteResample(inputBuffer[i], inNumberFrames) < inNumberFrames) | ||||
failure = true; | failure = true; | ||||
} | } | ||||
for (int i = 0; i < fPlaybackChannels; i++) { | 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) | if (fPlaybackRingBuffer[i]->ReadResample(outputBuffer[i], inNumberFrames) < inNumberFrames) | ||||
failure = true; | failure = true; | ||||
} | } | ||||
#ifdef JACK_MONITOR | #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 | #endif | ||||
// Reset all ringbuffers in case of failure | // Reset all ringbuffers in case of failure | ||||
@@ -250,8 +235,8 @@ namespace Jack | |||||
int JackAudioAdapterInterface::PullAndPush(float** inputBuffer, float** outputBuffer, unsigned int inNumberFrames) | int JackAudioAdapterInterface::PullAndPush(float** inputBuffer, float** outputBuffer, unsigned int inNumberFrames) | ||||
{ | { | ||||
bool failure = false; | bool failure = false; | ||||
fHostDLL.IncFrame(GetMicroSeconds()); | |||||
fPullAndPushTime = GetMicroSeconds(); | |||||
// Push/pull from ringbuffer | // Push/pull from ringbuffer | ||||
for (int i = 0; i < fCaptureChannels; i++) { | for (int i = 0; i < fCaptureChannels; i++) { | ||||
if (fCaptureRingBuffer[i]->Read(inputBuffer[i], inNumberFrames) < inNumberFrames) | if (fCaptureRingBuffer[i]->Read(inputBuffer[i], inNumberFrames) < inNumberFrames) | ||||
@@ -82,20 +82,19 @@ namespace Jack | |||||
jack_nframes_t fAdaptedBufferSize; | jack_nframes_t fAdaptedBufferSize; | ||||
jack_nframes_t fAdaptedSampleRate; | jack_nframes_t fAdaptedSampleRate; | ||||
//delay locked loop | |||||
JackAtomicDelayLockedLoop fHostDLL; | |||||
JackAtomicDelayLockedLoop fAdaptedDLL; | |||||
//PI controler | |||||
JackPIControler fPIControler; | |||||
JackResampler** fCaptureRingBuffer; | JackResampler** fCaptureRingBuffer; | ||||
JackResampler** fPlaybackRingBuffer; | JackResampler** fPlaybackRingBuffer; | ||||
unsigned int fQuality; | unsigned int fQuality; | ||||
unsigned int fRingbufferSize; | unsigned int fRingbufferSize; | ||||
jack_time_t fPullAndPushTime; | |||||
bool fRunning; | bool fRunning; | ||||
void ResetRingBuffers(); | void ResetRingBuffers(); | ||||
void ResampleFactor ( jack_time_t& frame1, jack_time_t& frame2 ); | |||||
public: | public: | ||||
@@ -106,10 +105,10 @@ namespace Jack | |||||
fHostSampleRate ( sample_rate ), | fHostSampleRate ( sample_rate ), | ||||
fAdaptedBufferSize ( buffer_size), | fAdaptedBufferSize ( buffer_size), | ||||
fAdaptedSampleRate ( sample_rate ), | fAdaptedSampleRate ( sample_rate ), | ||||
fHostDLL ( buffer_size, sample_rate ), | |||||
fAdaptedDLL ( buffer_size, sample_rate ), | |||||
fPIControler(sample_rate / sample_rate, 256), | |||||
fCaptureRingBuffer(NULL), fPlaybackRingBuffer(NULL), | fCaptureRingBuffer(NULL), fPlaybackRingBuffer(NULL), | ||||
fQuality(0), fRingbufferSize(DEFAULT_RB_SIZE), | fQuality(0), fRingbufferSize(DEFAULT_RB_SIZE), | ||||
fPullAndPushTime(0), | |||||
fRunning(false) | fRunning(false) | ||||
{} | {} | ||||
@@ -139,14 +138,12 @@ namespace Jack | |||||
virtual int SetHostBufferSize ( jack_nframes_t buffer_size ) | virtual int SetHostBufferSize ( jack_nframes_t buffer_size ) | ||||
{ | { | ||||
fHostBufferSize = buffer_size; | fHostBufferSize = buffer_size; | ||||
fHostDLL.Init ( fHostBufferSize, fHostSampleRate ); | |||||
return 0; | return 0; | ||||
} | } | ||||
virtual int SetAdaptedBufferSize ( jack_nframes_t buffer_size ) | virtual int SetAdaptedBufferSize ( jack_nframes_t buffer_size ) | ||||
{ | { | ||||
fAdaptedBufferSize = buffer_size; | fAdaptedBufferSize = buffer_size; | ||||
fAdaptedDLL.Init ( fAdaptedBufferSize, fAdaptedSampleRate ); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -160,14 +157,14 @@ namespace Jack | |||||
virtual int SetHostSampleRate ( jack_nframes_t sample_rate ) | virtual int SetHostSampleRate ( jack_nframes_t sample_rate ) | ||||
{ | { | ||||
fHostSampleRate = sample_rate; | fHostSampleRate = sample_rate; | ||||
fHostDLL.Init ( fHostBufferSize, fHostSampleRate ); | |||||
fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||||
return 0; | return 0; | ||||
} | } | ||||
virtual int SetAdaptedSampleRate ( jack_nframes_t sample_rate ) | virtual int SetAdaptedSampleRate ( jack_nframes_t sample_rate ) | ||||
{ | { | ||||
fAdaptedSampleRate = sample_rate; | fAdaptedSampleRate = sample_rate; | ||||
fAdaptedDLL.Init ( fAdaptedBufferSize, fAdaptedSampleRate ); | |||||
fPIControler.Init(double(fHostSampleRate) / double(fAdaptedSampleRate)); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -23,6 +23,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
#include "jack.h" | #include "jack.h" | ||||
#include "JackAtomicState.h" | #include "JackAtomicState.h" | ||||
#include <math.h> | #include <math.h> | ||||
#include <stdlib.h> | |||||
namespace Jack | namespace Jack | ||||
{ | { | ||||
@@ -201,6 +202,117 @@ namespace Jack | |||||
return res; | 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() | JackLibSampleRateResampler::JackLibSampleRateResampler() | ||||
:JackResampler(),fRatio(1) | |||||
:JackResampler() | |||||
{ | { | ||||
int error; | int error; | ||||
fResampler = src_new(SRC_LINEAR, 1, &error); | fResampler = src_new(SRC_LINEAR, 1, &error); | ||||
@@ -32,7 +32,7 @@ JackLibSampleRateResampler::JackLibSampleRateResampler() | |||||
} | } | ||||
JackLibSampleRateResampler::JackLibSampleRateResampler(unsigned int quality, unsigned int ringbuffer_size) | JackLibSampleRateResampler::JackLibSampleRateResampler(unsigned int quality, unsigned int ringbuffer_size) | ||||
:JackResampler(ringbuffer_size),fRatio(1) | |||||
:JackResampler(ringbuffer_size) | |||||
{ | { | ||||
switch (quality) { | switch (quality) { | ||||
case 0: | case 0: | ||||
@@ -26,11 +26,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
namespace Jack | 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/). | \brief Resampler using "libsamplerate" (http://www.mega-nerd.com/SRC/). | ||||
*/ | */ | ||||
@@ -41,7 +36,6 @@ class JackLibSampleRateResampler : public JackResampler | |||||
private: | private: | ||||
SRC_STATE* fResampler; | SRC_STATE* fResampler; | ||||
double fRatio; | |||||
public: | public: | ||||
@@ -51,12 +45,6 @@ class JackLibSampleRateResampler : public JackResampler | |||||
unsigned int ReadResample(float* buffer, unsigned int frames); | unsigned int ReadResample(float* buffer, unsigned int frames); | ||||
unsigned int WriteResample(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(); | void Reset(); | ||||
@@ -23,13 +23,15 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
namespace Jack | 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); | fRingBuffer = jack_ringbuffer_create(sizeof(float) * fRingBufferSize); | ||||
jack_ringbuffer_read_advance(fRingBuffer, (sizeof(float) * fRingBufferSize) / 2); | jack_ringbuffer_read_advance(fRingBuffer, (sizeof(float) * fRingBufferSize) / 2); | ||||
@@ -26,8 +26,13 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |||||
namespace Jack | 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. | \brief Base class for Resampler. | ||||
*/ | */ | ||||
@@ -38,8 +43,7 @@ class JackResampler | |||||
protected: | protected: | ||||
jack_ringbuffer_t* fRingBuffer; | jack_ringbuffer_t* fRingBuffer; | ||||
unsigned int fNum; | |||||
unsigned int fDenom; | |||||
double fRatio; | |||||
unsigned int fRingBufferSize; | unsigned int fRingBufferSize; | ||||
public: | public: | ||||
@@ -58,16 +62,20 @@ class JackResampler | |||||
virtual unsigned int ReadSpace(); | virtual unsigned int ReadSpace(); | ||||
virtual unsigned int WriteSpace(); | 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; | |||||
} | } | ||||
}; | }; | ||||