This works perfectly thanks so much for your help with this. I tried to understand how to derive these filters myself but found it very difficult. Hopefully I’ll learn how to do it at some point. I’m going to explain how I implemented all of these filters for anyone else who finds themselves in a similar situation. Basically I’m implementing all of these filters in Faust and then exporting the Faust code to a JUCE project. All of the filters I implemented allow you to set a cutoff frequency above Nyquist (SampleRate/2), including the first order highpass I implemented.
Note that for each filter detailed below, the code calculates the coefficients which are then arguments to the tf21 function. This function is a direct-form second-order biquad filter. The a1,a2 coefficients describe where the poles are in the z-domain, and the b0,b1,b2 coefficients describe where the zeros are in the z-domain.
Here is the matched second order high pass filter. The coefficients are taken from this Vicanek paper:
biquad_highpass_matched(fc,Q) = tf21(b0,b1,b2,a1,a2) with {
w0 = (2*ma.PI*fc)/ma.SR; //converts freq in HZ to radians per sample
q = 1/(2*Q); //Explained just after Equation 6
a1coef = -2*ma.E^(-q*w0);
a11 = a1coef*cos(sqrt(1-q^2)*w0); //Equation 12 of paper
a12 = a1coef*cosh(sqrt(q^2-1)*w0); //Equation 12 of paper
a1 = ba.if(q <= 1, a11, a12); //Equation 12 of paper
a2 = ma.E^(-2*q*w0); //Equation 12 of paper
f0 = fc/(ma.SR/2); //converts freq in HZ to half-cycles per sample
r1= (1-a1+a2)/sqrt((1-f0^2)^2 + f0^2/Q^2); //Equation 48 of paper
b0 = r1/4; //Equation 49 of paper
b2 = b0; //Equation 49 of paper
b1 = -2*b0; //Equation 49 of paper
};
Here is the matched high shelf filter. The coefficients are taken from this Vicanek paper:
biquad_matched_shelf(fc,G) = tf21(b0,b1,b2,a1,a2) with {
f_ny = fc/(ma.SR/2); //converts freq in HZ to half-cycles per sample
f_m = 0.9; // Equation 12 of paper
phi = 1- cos(ma.PI*f_m); // Equation 4 of paper with f_m as argument
alpha = 2/(ma.PI^2) * (1/(f_m^2)+ 1/(G*f_ny^2)) - 1/phi; // Equation 12 of paper
beta = 2/(ma.PI^2) * (1/(f_m^2)+ G/(f_ny^2)) - 1/phi; // Equation 12 of paper
a1 = -alpha/(1+alpha+sqrt(1+2*alpha)); // Equation 10 of paper
b = -beta/(1+beta+sqrt(1+2*beta)); // Equation 10 of paper
a2 = 0; //inferred
b0 = (1+a1)/(1+b); // Equation 11 of paper
b1 = b*b0; // Equation 11 of paper
b2 = 0; //inferred
};
Finally, here is the matched first order high pass that @pflugshaupt explained:
biquad_highpass_1st_order(fc) = tf21(b0,b1,b2,a1,a2) with {
nf = fc/ma.SR;
a1 = -1 * ma.E^(-nf * 2 * ma.PI);
gain_nyq = sqrt(0.25 / (0.25 + nf * nf));
b0 = 0.5 * gain_nyq * (1 - a1);
b1 = -b0;
a2 = 0;
b2 = 0;
};