The basic filter configuration we are dealing with is exactly the same as the Moog ladder, though I believe the non-linearities in the Xpander filter are different, but since we will be dealing with linear stuff only, that makes no difference whatsoever. As long as you keep your stage gains right, you can mess with whatever non-linearities you want.
We are going to proceed with the concept of adding together 5 outputs from the filter: one after the resonance feedback, and 4 after each 1-pole LP stage. I'm going to name these outputs as A,B,C,D and E. (We'll ignore any Xpander specific magic here, since I'm explaining the concept and not how to model an Xpander filter.)
The concept is simple (and hinted at in the service manual of the Oberheim thing, though it's somewhat obscured by analog implementation issue there): we work out the transfer function of each output, then solve for constants we need to multiply them to get a desired transfer function. We'll ignore the resonance from the feedback loop, as it happens that it makes no useful difference in the discussion, and there is nothing in the original Oberheim about it anyway (IIRC and AFAIK and whatever qualifiers).
Now a one-pole analog lowpass can be taken to have the transfer function 1/(1+s) which is about as much as we need to understand about Laplace transforms to be able to get this done. The other thing we need to understand is that if we put two lowpasses in a row, it happens to become multiplication in the transfer domain (this follows from the relationship of convolution and multiplication, and the fact that both of them are associative operations).
So, the outputs therefore get transfer functions as follows:
A = 1
B = 1/(1+s)
C = (1/(1+s))^2
D = (1/(1+s))^3
E = (1/(1+s))^4
Now to be able to combine the outputs in a useful way, we are going to assign coefficients [a,b,c,d,e] for them, giving us a totally generic (as far as the filter can provide) transfer function:
a*A + b*B + c*C + d*D + e*E
From that you obviously get a 4-pole LP by letting e=1 and rest of them 0. For two pole LP, you take just the C output, and so on.
Anyway, just different LPs aren't all that interesting, so using your favourite symbolic mathematics software (I usually use Maxima) or pencil and paper if you really feel like it, you'll want to expand and then simplify that to get something like:
Code: Select all
as^4 + (4a+b)s^3 + (6a+3b+c)s^2 + (4a+3b+2c+d)s + a+b+c+d+e
-----------------------------------------------------------
(1+s)^4
If your stages are inverting (like I think they were in the Xpander IIRC) then you need to negate every other output, which will give you all positive (or negative if you get it wrong way) coeffs later, but for trivial digital 1-pole lowpasses that's not necessary. Notice that this is sensitive to individual stage gains though (if your stages have equal gain and it starts self-oscillating at feedback of 4 you're at least close; if not, you can either modify the stages or the coeffs you solved).
Anyway, now that we have the general transfer function, we can solve the coefficients to get a particular transfer function. As an example, we'll take 4-pole hipass.
One can get 1-pole high-pass from a 1-pole lowpass, by taking the difference between the input and the output. This obviously (I hope it's obvious anyway) is no good for multi-pole high-pass though, so we'll multiply (or "put in a row" in the time domain sense) a couple of single-pole high-passes instead, giving us:
(1 - 1/(1+s))^4 or (s/(1+s))^4 in case you simplify first
Using your favourite symbolic mathematics software again, you might manage to simplify it to something like:
s^4 / (1+s)^4
ok, in this case it would be trivial to do it on paper as well, but in more complicated modes I'd just dump it to Maxima and be done with it
So now we have the ugly polynomial above, and then this simple polynomial, and we wanna make them the same:
Code: Select all
s^4 as^4+(4a+b)s^3+(6a+3b+c)s^2+(4a+3b+2c+d)s+a+b+c+d+e
------- = ---------------------------------------------------
(1+s)^4 (1+s)^4
Code: Select all
/ a = 1 a = 1
| 4a+ b = 0 b = -4
< 6a+3b+c = 0 => c = 6
| 4a+3b+2c+d = 0 d = -4
\ a+ b+ c+d+e = 0 e = 1
Same principle can be used for other modes, say 2+2 pole BP could be solved from:
(s/(1+s))^2 * (1/(1+s))^2 = two-pole highpass going into two-pole lowpass
For notch filter you need to do something more clever, and add hipass and lowpass responses together (at least 2-pole for each such that they happen to cancel each other near the cutoff).
You can also make more fancy modes, like notch going into lowpass, or whatever, and even if we're dealing with the analog transfer functions here, it translates "as-is" into the digital domain as long as you've got the indivial lowpass-stages translated properly.
Which ofcourse you won't have, because somehow you'll have to compensate for the fact that you have an extra delay in the feedback loop. This fortunately causes only minor problems (there seems to be some anomality with notch and resonance; the notch doesn't exactly cancel the resonance because the frequencies don't match exactly). I personally haven't bothered fixing it yet, though potential remedies would be structuring the one-poles such that correct phases are available inside them (no idea if this is workable), or by using all-passes (fractional delay filters should do the job?) for phase correction (more things I'll have to do before release... blah).
In any case, hope that was useful for someone, and please report any typos/grammar problems that my proof reading was unable to find.