You are probably already using Zero Delay Feedback filters, so let your customers know!

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

mystran wrote:I want to post my super-minimal mathematics version though:

1. start with the description of signal flow.. i'll use multiply by 1/s as notational symbol, but it's still just a description of signal flow.. for an SVF since it's simple enough.. and order doesn't matter, this is just a "list of connections"

Code: Select all

  lp = 1/s * bp
  bp = 1/s * hp
  hp = in - lp - bp/Q
See, you've already lost me. s? And a notational symbol of what? If that means anything other than "one divided by s" then I have no idea...

(This is the thing about you math guys. You don't realize how many completely arbitrary conventions you use that are completely opaque to those of us who had a math teacher in grade 8 who used to turn his hearing aid off and let the class go wild while he read books.)

But I know what Q is! At least.

Post

AdmiralQuality wrote:
mystran wrote:I want to post my super-minimal mathematics version though:

1. start with the description of signal flow.. i'll use multiply by 1/s as notational symbol, but it's still just a description of signal flow.. for an SVF since it's simple enough.. and order doesn't matter, this is just a "list of connections"

Code: Select all

  lp = 1/s * bp
  bp = 1/s * hp
  hp = in - lp - bp/Q
See, you've already lost me. s? And a notational symbol of what? If that means anything other than "one divided by s" then I have no idea...
Ooops.. did I really forget "for integrator" from there. Have to go back and fix that.

edit: Ok, so that's fixed.. the point is.. division by s actually means just that, except "s" is conventionally the parameter in the Laplace-transform domain, and then 1/s happens to be the transfer function for integration.

So the part about notational symbol was really a lie, but it's not that important really: there's tons of filter signal-flow diagrams all over the interwebz that have those boxes that say "1/s" and the point is you can just put one of those in the "list of connections" as if it divided something by s, and everything should work perfectly fine.

Anyway, does it still not make any sense? :P

Post

mystran wrote:Anyway, does it still not make any sense? :P
Very nice. Elimination of the unit delay is a powerful tool, and one doesn't always have to start at the beginning when there's papers over papers where people have already done the math - and all you need to do is "ZDF it" :)

I've prepared another (non-linear) example where the z^-1 code is identical with the ZDF code, it's up to a set of solvers to do whatever is desired. We're eventually going to write a little paper about it, and explain the whole iteration & convergence thing for dummies. Can't say when though.

Post

Is stability of convergence always guaranteed, or are there exceptional cases where the input might cause it to fail, excluding NaN?

Post

It behaves exactly as you'd expect. The only difference is that rather than allowing for one sample delay, the contribution of the current input is calculated directly and that approximation is used for feedback.

The history from beyond the current sample period comes from the memory elements which have already included the effect of feedback.

We have: [input] and [state]
We want: [output] so we can use that as feedback.
Since we don't yet have [feedback], we can't use it when we calculate [output] from [input] and [state].

So the only shortcoming of this is that there is still no way to calculate the state including both the input and feedback.

That is where recursive methods come in and you can run the algorithm for a few steps to produce an improved approximation.

So recursively we can calculate:

1) [output] = f([input], [state]), [feedback] = fb([output])
2) [output] = f([input], [feedback], [state]), [feedback] = fb([output])
...) [output] = f([input], [feedback], [state]), [feedback] = fb([output])

Until we're happy with [output] matching the equivalent component of [state].

There are many ways to do this.

Since the approximation is always going to be less than the actual state (since the contribution of feedback was not included) it is sure to be stable. When you start using recursive solutions and varying the number of steps, depending upon which method you use it is possible for it to become unstable.
Free plug-ins for Windows, MacOS and Linux. Xhip Synthesizer v8.0 and Xhip Effects Bundle v6.7.
The coder's credo: We believe our work is neither clever nor difficult; it is done because we thought it would be easy.
Work less; get more done.

Post

AdmiralQuality wrote:
andy-cytomic wrote:
AdmiralQuality wrote:As someone who's NOT using [the unspeakable term for whatever you want to call it], how about some of you geniuses share the algo with us, considering how it's all common knowledge and everything.

A few lines of code or pseudocode, please. Unpronounceable math symbols and opaque terminology not required or helpful. Nor is introduction of non-linearities. Just a nice straight linear IIR with negative feedback. For us simpletons. Please?
I've posted loads of these worked examples of how to apply basic circuit math that was first used in the 1970s to solve these systems of equations.

Here is an example of how passive and active idealised one pole low pass filters are identical, and showing how recently espoused up methods are equivalent to the long existing ones:

http://cytomic.com/files/dsp/OnePoleLinearLowPass.pdf

Here are the SVF and Sallen Key structures solved. The SVF one also shows how to manually solve these systems of linear equations (even non-linear ones get linearised) and also how to solve for the bell and shelving coefficients to match the RBJ shapes:

http://cytomic.com/files/dsp/SvfLinearT ... mised2.pdf
http://cytomic.com/files/dsp/SkfLinearT ... mised2.pdf
Thanks, those were exactly what I didn't want. ;)
Well, if this is about as basic as it gets, everything is very easy to follow, but you probably just skimmed it which won't be enough to understand what is going on.

I have derived everything from first principles in those pdfs apart from the tan warping of trapezoidal. Probably the most interesting example is the sallen key, which I've shown how to solve two ways, for nodal voltages or for capacitor voltages. Most state space and differential equations write things down from the point of view of the capacitors, which is the latter one. Note that I set the "gc" values to 1, but you could use real capacitor values (eg for trapezoidal 2 C / h which is 2 C samplerate - thanks to mystran for spotting the mistake here I previously wrote 2 C / samplerate), here if you want to have real currents, but most virtual components don't melt with too much current so it really doesn't matter here unless you wanted to build the physical circuit and double check you won't blow anything up.

One pole passive low pass:

Code: Select all

solve these equations for v1. v0 is input:
0 = ir1 - ic1

where:
ir1 = gr1 (v0 - v1)
ic1 = gc1 (v1 - 0) - ic1eq
gr = tan (pi*cutoff/samplerate)
gc1 = 1

update state (for trapezoidal):
ic1eq = 2 gc1 (v1 - 0) - ic1eq
One pole active low pass:

Code: Select all

solve these equations:
0 = ia1 - ic1

where:
ia1 = ga1 (gr v0 - gr v1)
ic1 = gc1 (v1 - 0) - ic1eq
ga1 = tan (pi*cutoff/samplerate)
gc1 = 1
gr = 1

update state (for trapezoidal):
ic1eq = 2 gc1 (v1 - 0) - ic1eq
Two Pole SVF (nodal voltages and cap voltages are the same since one side of the caps are to ground, but I've left the "- 0" to show this explicitly just in case you want to connect another voltage here instead):

Code: Select all

solve these equations for v1 and v2. v0 is input:
0 = ia1 - ic1
0 = ia2 - ic2

where:
ia1 = ga1 (v0 - k*v1 - v2)
ia2 = ga2 (v1)
ic1 = gc1 (v1 - 0) - ic1eq
ic2 = gc2 (v2 - 0) - ic2eq
ga1 = ga2 = tan (pi*cutoff/samplerate)
gc1 = gc2 = 1

update state (for trapezoidal):
ic1eq = 2 gc1 (v1 - 0) - ic1eq
ic2eq = 2 gc2 (v2 - 0) - ic2eq

Two pole Sallen Key (nodal voltages):

Code: Select all

solve these equations for v1 and v2. v0 is input:
0 = ia1 - ic1
0 = ia2 - ic2

where:
ia1 = ga1 (v0 - v1)
ia2 = ga2 (v1 - v2)
ic1 = gc1 (v1 - k v2) - ic1eq
ic2 = gc2 (v2 - 0) - ic2eq
ga1 = ga2 = tan (pi*cutoff/samplerate)
gc1 = gc2 = 1

update state (for trapezoidal):
ic1eq = 2 gc1 (v1 - k v2) - ic1eq
ic2eq = 2 gc2 (v2 - 0) - ic2eq
Two pole Sallen Key (cap voltages) this is how forward euler type voltage orientated people prefer to solve the Sallen Key:

Code: Select all

solve these equations for v1 and v2. v0 is input:
0 = ia1 - ic1
0 = ia2 - ic2

where:
ia1 = ga1 (v0 - (k v2 + v1))
ia2 = ga2 ((k v2 + v1) - v2)
ic1 = gc1 (v1) - ic1eq
ic2 = gc2 (v2) - ic2eq
ga1 = ga2 = tan (pi*cutoff/samplerate)
gc1 = gc2 = 1

update state (for trapezoidal):
ic1eq = 2 gc1 (v1) - ic1eq
ic2eq = 2 gc2 (v2) - ic2eq
Four pole cascade (nodal voltages and cap voltages are the same since one side of the caps are to ground, but I've left the "- 0" to show this explicitly just in case you want to connect another voltage here instead):

Code: Select all

solve these equations for v1, v2, v3, v4. v0 is input:
0 = ia1 - ic1
0 = ia2 - ic2
0 = ia3 - ic3
0 = ia4 - ic4

where:
ia1 = ga1 (v0 - v1 - k v4)
ia2 = ga2 (v1 - v2)
ia3 = ga3 (v2 - v3)
ia4 = ga4 (v3 - v4)
ic1 = gc1 (v1 - 0) - ic1eq
ic2 = gc2 (v2 - 0) - ic2eq
ic3 = gc3 (v3 - 0) - ic3eq
ic4 = gc4 (v4 - 0) - ic4eq
ga1 = ga2 = ga3 = ga4 = tan (pi*cutoff/samplerate)
gc1 = gc2 = gc3 = gc4 = 1

update state (for trapezoidal):
ic1eq = 2 gc1 (v1 - 0) - ic1eq
ic2eq = 2 gc2 (v2 - 0) - ic2eq
ic3eq = 2 gc3 (v3 - 0) - ic3eq
ic4eq = 2 gc4 (v4 - 0) - ic4eq
Last edited by andy-cytomic on Mon Dec 02, 2013 10:14 am, edited 3 times in total.
The Glue, The Drop - www.cytomic.com

Post

andy-cytomic wrote: Note that I set the "gc" values to 1, but you could use real capacitor values (2 C / samplerate) here if you want to have real currents
There's a mistake. It should read (2 * C * samplerate).

Seems counter intuitive, but higher samplerate means that the normalize cutoff needs to go down (to compensate) and so we need a larger capacitor. From one pole RC, we can see that this results in cutoff at 1/(R*2*C*samplerate) which should make perfect sense.

Also just a remainder that if you use real capacitor and resistor values, then you also need to adjust your tuning parameter to cancel that 1/(R*2*C*samplarate) if you still want to keep normalized tuning.

Post

AdmiralQuality wrote:See, you've already lost me. s?
1/s -> integrator in Laplace domain.

Post

mystran wrote:
andy-cytomic wrote: Note that I set the "gc" values to 1, but you could use real capacitor values (2 C / samplerate) here if you want to have real currents
There's a mistake. It should read (2 * C * samplerate).

Seems counter intuitive, but higher samplerate means that the normalize cutoff needs to go down (to compensate) and so we need a larger capacitor. From one pole RC, we can see that this results in cutoff at 1/(R*2*C*samplerate) which should make perfect sense.

Also just a remainder that if you use real capacitor and resistor values, then you also need to adjust your tuning parameter to cancel that 1/(R*2*C*samplarate) if you still want to keep normalized tuning.
Yes, sorry, I just wrote that off the top of me head, I meant to write 2 C / h, where h = 1/samplerate, I'll correct that now. This is why I post links to pdf files instead, it's right in all the pdfs!
The Glue, The Drop - www.cytomic.com

Post

EvilDragon wrote:
AdmiralQuality wrote:See, you've already lost me. s?
1/s -> integrator in Laplace domain.
But of course! Nothing arbitrary or opaque about that!

You guys don't know what pseudo-code is, do you? :P

Post

andy-cytomic wrote:
mystran wrote:
andy-cytomic wrote: Note that I set the "gc" values to 1, but you could use real capacitor values (2 C / samplerate) here if you want to have real currents
There's a mistake. It should read (2 * C * samplerate).

Seems counter intuitive, but higher samplerate means that the normalize cutoff needs to go down (to compensate) and so we need a larger capacitor. From one pole RC, we can see that this results in cutoff at 1/(R*2*C*samplerate) which should make perfect sense.

Also just a remainder that if you use real capacitor and resistor values, then you also need to adjust your tuning parameter to cancel that 1/(R*2*C*samplarate) if you still want to keep normalized tuning.
Yes, sorry, I just wrote that off the top of me head, I meant to write 2 C / h, where h = 1/samplerate, I'll correct that now. This is why I post links to pdf files instead, it's right in all the pdfs!
This is why I don't pay too much attention to you guys. Last time I tried to follow mystran's posts on this topic I gave up after pages of revisions kept coming in. I'm sure mystran is a very bright guy who knows all kinds of math stuff, but I don't get the impression he's very results oriented. He likes having a problem. I like having a solution.

Post

AdmiralQuality wrote:Last time I tried to follow mystran's posts on this topic I gave up after pages of revisions kept coming in.
Mystran's method is basically the same as Vadim's, write out the integrators, and then solve it with you math package of choice - that's it, the smart guys around here can probably do the equations by hand - I would whither and die without Maxima. I got mystran's example up and running (from scratch) in 20min. For the most part - writing out the integrators from a circuit diagram is the really hard part - especially if, like me, reading circuits does not come naturally at all. I've been staring at a Steiner-Parker filter schematic for a couple of days now, which is just a diode ladder (as far as I can tell), trying to figure out -> how the hell it is possible to just apply the input to the 'middle' of the ladder and 'magically' a highpass filter appears?

Post

Ichad.c wrote:
AdmiralQuality wrote:Last time I tried to follow mystran's posts on this topic I gave up after pages of revisions kept coming in.
Mystran's method is basically the same as Vadim's, write out the integrators, and then solve it with you math package of choice - that's it, the smart guys around here can probably do the equations by hand - I would whither and die without Maxima. I got mystran's example up and running (from scratch) in 20min. For the most part - writing out the integrators from a circuit diagram is the really hard part - especially if, like me, reading circuits does not come naturally at all. I've been staring at a Steiner-Parker filter schematic for a couple of days now, which is just a diode ladder (as far as I can tell), trying to figure out -> how the hell it is possible to just apply the input to the 'middle' of the ladder and 'magically' a highpass filter appears?
Again, this is all way overly complex. I'm not looking to model the exact behavior of diodes or any particular circuit. My existing filter design is like 5 lines of code. 4 LERPS between the sample value of "now" vs. "last time" and a negative feedback (of last time's output) applied to the start.

So are there TWO delays we want to eliminate here? The ones in the states of the individual poles, as well as the feedback? Is aciddose saying it's hard to do both at once without recursive methods? (Which I'd rather avoid, I don't want filters that constantly change their CPU load according to the values in the samples they're processing.)

Post

Code: Select all

//Four Pole Ladder, linear version
g=tan(PI*frequency/sampleRate);
g/=(1+g);
twoG = g*2;
//Filter states
s1=s2=s3=s4=0;
//Calculate Feedback
feedBack = (g^4*input+g^3*s1+g^2*s2+g*s3+s4)/(1+g^4*q);
//Lowpass 1
input -= feedBack*q;
out1 = (input)*g+s1;
s1 = (input-out1)*twoG+s1;
//Lowpass 2
out2 = out1*g+s2;
s2 = (out1-out2)*twoG+s2;
//Lowpass 3
out3 = out2*g+s3;
s3 = (out2-out3)*twoG+s3;
//Lowpass 4
out4 = out3*g+s4;
s4 = (out3-out4)*twoG+s4;

return out4;
A four pole lowpass for AQ to dismiss out of hand :D Anyway, could be full of mistakes, was typed on my iPad.

Edit: Yeah, I cocked up the state updates. That's what you get for working out code on a scrap of paper while watching TV and finishing off a nice scotch :hihi:

Should work now (hopefully).
Last edited by matt42 on Tue Dec 03, 2013 3:25 am, edited 1 time in total.

Post

Admiral, unfortunately I never took a few EE courses when I still remembered math. Maybe somebody would have to be real smart to figger it out self-taught, without having it pounded into one's dense skull by a grumpy perfessor.

However, here are some "very basic" pointers to the s-domain stuff, not that I understand it well enough to use.

http://en.wikipedia.org/wiki/S-domain
The parameter s is a complex number:

s = σ + iω , with real numbers σ and ω.
A complex number has real and imaginary components. The first squiggle in the above equation is sigma (the real component).

The second squiggle that looks like a 'w' is omega. The i in this case is the imaginary multiplier, the root of -1. 'i' * omega is the imaginary component.

In many AC electrical engineering calculations, they assume sigma equals zero, so it degrades down to this in many filter transfer function examples--

s = iω

Now here is another piece of the puzzle-- 'i' is considered by mathematicians to be the square root of -1. HOWEVER, 'j' is considered by electrical engineers to be the square root of -1. THEREFORE, in electrical engineer speak, you would see it written thisaway--

s = jω

Now, OMEGA is the Angular Frequency--

http://en.wikipedia.org/wiki/Angular_frequency
ω = (2 * pi) / T = 2 * pi * f

where:

ω is the angular frequency or angular speed (measured in radians per second),
T is the period (measured in seconds),
f is the ordinary frequency (measured in hertz) (sometimes symbolised with ν).
So in all of transfer function equations with the s variables scattered in them, you could substitute for s, for instance (-1^.5) * 2 * pi * f

In other words, you could plug in any particular frequency and solve the damn thing and get the phase and amplitude response AT THAT FREQUENCY.

Here is JOS being typically opaque explaining the thang--

https://ccrma.stanford.edu/~jos/filters ... lysis.html

So anyway, maybe that is a "starter" if you want to try to puzzle it out. I'm gonna try to puzzle it out further, one of these days. :)

Post Reply

Return to “DSP and Plugin Development”