There's actually no "circuit modelling" even required here, we can do it all purely in terms of abstract signals and integrators ... and even though Andy writes in terms of circuit quantities, I think that mostly just makes things more confusing for linear filters... but that's a matter of opinion I guess. I'll post the "purely in terms of signals" version here so you can compare equivalence to Andy's version to perhaps gain more insight.That sounds indeed desirable. How would the coefficient-computation and sample-processing have to be modified to achieve that? I really don't know enough about circuit modeling to understand how I would "take the highpass directly from the solver" etc.
Anyway, regarding HP the idea is that you take the ODE with 2-dimensions and add a third algebraic dimension for the HP node (ie. feedback summing opamp's output) turning the thing into DAE (which makes little difference in terms of solving; it's still just Ax=b, except now you only integrate some of the values; I'm not going to write that part out right now, but perhaps another day) ... and then if you solve for this new variable first, this thing (which I've posted many times) falls out:
Code:
float hp = (in - (g+r)*z1 - z2) / (1 + g*(g+r)); float bp = z1 + g*hp; float lp = z2 + g*bp; // state variable update z1 = 2*bp - z1; // equivalent to: z1 += 2*g*hp z2 = 2*lp - z2; // equivalent to: z2 += 2*g*bp float out = a2*hp + a1*bp + a0*lp;
For emphasis, this isn't really different from Andy's filter, you can obtain one from the other simply by rearranging the terms, but I like this form because it's sort of clear in terms of what is going on: we solve the feedback sum (hp node) and then integrate twice.
The "analog prototype response" for this thing is (a2*s^2+a1*s+a0)/(s^2+s/Q+1), so if we have any analog prototype (such as those listed in RBJ Cookbook) where the denominator is already in the form 1/(s^2+s/Q+1) then basically all you have to do is take the scalar multiplies from numerator as a2,a1,a0 and you're set.
If we have a peaking filter, then RBJ gives (s^2+A/Q*s+1)/(s^2+s/(A*Q)+1) so deminator is wrong, so what do we do?!? PANIC. Fortunately we can fix this by substituting Q'=AQ and we get (s^2+A^2/Q'*s+1)/(s^2+s/Q'+1) and now it's in the correct form again when we use r=1/Q'=1/(A*Q).
What about shelves though? We could panic again, but given RBJ lowshelf A*(s^2+sqrt(A)/Q*s+A)/(As^2+sqrt(A)/Q*s+1) we can also do a similar trick, but this with "s" where we substitute s' = s/sqrt(A) so that the denominator multiplies into the desired form and this time we then set g=tan(pi*f/fs)/sqrtA() to scale the cutoff (doing the scaling after tan() pre-warp keeps the pre-warp frequency as-is, which is what RBJ also does).
Symbolically the results are exactly the same whether you do it this way or go through the direct form coefficients, but if you're doing things numerically then the direct form coefficient solve is a bit less stable, so it's helpful to start directly from the analog prototype if possible.
Statistics: Posted by mystran — Fri Nov 08, 2024 5:47 pm