I'm very new to the DSP world, and honestly I'm not even sure if this is the right place for this question.

Anyway, I found this piece of code on github, it's a kind of filter (I think), and I would like to know if this can be identified as a commonly used or well known concept. Does it have a name I can use to google? Where do I start to learn more about this, like how does one come up with this code.

The (relevant) code:

This is calculated once:

// Set the loop bandwidth to bw Hz.
w = 6.28f * 20 * bw * bsize / fsamp;
w0 = 1.0 - exp (-w);
w = 6.28f * bw * ratio / fsamp;
w1 = w * 1.6;
w2 = w * bsize / 1.6;

z1 = 0.0
z2 = 0.0
z3 = 0.0

This is calculated every loop:

// Run loop filter and set resample ratio.
z1 += _w0 * (w1 * err - z1);
z2 += _w0 * (z1 - z2);
z3 += _w2 * z2;

rcorr = 1 - z2 - z3;

In these snippets err is the input of the filter and rcorr is the output. bsize, ratio and fsamp are some constants.

For completeness, this code is from here on github.

  • 1
    $\begingroup$ I think you're asking a proxy question whose answer won't help you much (the answer is: yes, these are known patterns, even if this is all expressed a bit in a confusing manner; terms to learn are control loop, control theory basics, loop filter on the control theory side of this, recursive filter, infinite impulse response and stability on the filter side; our students take several courses of math before they can get the amount of control theory and system theory thrown at them without nausea). What is the reason you want to know this? Is there a problem we can help you solve? $\endgroup$ – Marcus Müller Jan 6 at 16:03
  • $\begingroup$ @MarcusMüller I had a problem with controlling a resampler factor. I was running into oscillation problems. Knowing I wouldn't be the first, I searched and found this project that solves the problem using the above code. I could just copy it and it would probably work. However I would prefer to understand how it actually works instead of just copying it :) $\endgroup$ – Roel Gerrits Jan 6 at 16:23
  • 1
    $\begingroup$ The filter itself seems like a fairly straight-forward continuous three-pole approximately discretized using Euler integrators. It arguably looks a bit adhoc, but the very high-level version of what it does is smooth the error so it's corrected "slowly" over time. Beyond that though, I kinda agree with Marcus Müller that you need some basic theory of filters and control loops for a more detailed answer to really be helpful and at that point you might not need the answer anymore. $\endgroup$ – myzz Jan 6 at 21:21

It's part of a feedback loop -- presumably setting the sample rate in some real-time audio thingie, since I see keywords in the code like 'resample' and 'jack' and 'PLAY'.

// Run loop filter and set resample ratio.

So, this tells you what it does!

Elsewhere in the code, err is computed as the timing error between the input stream and the output stream. So, this is a feedback loop.

_z1 += _w0 * (_w1 * err - _z1);
_z2 += _w0 * (_z1 - _z2);

Low-pass filter the error with a pair of first-order low-pass filters. (_z1 is err filtered, _z2 is _z1 filtered). Note that for this to work, _z1 and _z2 need to be persistent. In C/C++, this means that they should be at least at file scope -- and in my humble opinion, this means they should have longer, descriptive names, even to the point of declareing a struct called "filter_state" -- it never hurts to hand your reader a clue-by-four.

I'm not going to dredge through all the loop analysis, but the cutoff frequencies of these filters should be significantly higher than the target loop bandwidth -- and the loop bandwidth is determined, in part, by the gain of the timing error detection process. So you can't just slap this into your code willy-nilly.

_z3 += _w2 * _z2;

This is an integrator.

_rcorr = 1 - _z2 - _z3;

if (_rcorr > 1.05) _rcorr = 1.05;
if (_rcorr < 0.95) _rcorr = 0.95;

This does the final calculation of the command variable to the resampler. Note, however, that we have a controller with an integrator, and we're limiting the output of the integrator, but we're not limiting the state of the integrator. If you implement this code, search on "integrator anti-windup" for why this is a mistake, and why _z3 should be limited in magnitude, somewhere between $\pm 0$ and $\pm 0.1$, depending on your desired recovery characteristics from a large excursion.

_resamp.set_rratio (_rcorr);

This is actually setting the resample ratio, which in turn affects the value of err in the next iteration of the loop.

  • $\begingroup$ Thanks MarcusMüller myzz and TimWescott. Altough I still don't fully understand how it works, it's a bit less magical now. I will have to get a little bit more into the theory I guess :) $\endgroup$ – Roel Gerrits Jan 9 at 13:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.