Commit d55eddb4 authored by sletz's avatar sletz
Browse files

Use Torben Hohn PI controler code for adapters (in progress).

git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@3417 0c269be4-1314-0410-8aa9-9f06e86f4224
parent 8badda0a
......@@ -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;
}
};
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment