gen~ functions for lo, hi, and peak shelving EQ
Hi, some people asked for this, so here are gen~ methods for single- and dual-pole lo, hi, and peak shelving EQs. The next post has a demo patch.
shelf1core(input, a1, b0, b1){
// required for 1pole EQ
History z0, z1;
output = a1 * z1 + b0 * input + b1 * z0;
z1 = output;
z0 = input;
return output;
}
loshelf12(src1, src2, pitch, db){
// pitch = midi, 0 to 127
// db = shelf, -12 to 12
History a1, b0, b1;
if((change(pitch)!=0)||(change(db) !=0)){
b = pow(1.059253692626953, db);
b2 = b * b;
f1 = tan(clip(mtof(pitch) *b,
samplerate /24576,
samplerate /2.125) *pi /samplerate);
f2 = 1 + f1;
a1 = (1 - f1) / f2;
a2 = (f1 / f2) * (b2 -1);
b0 = a2 +1;
b1 = a2 - a1;
}
out_1 = shelf1core(src1, a1, b0, b1);
out_2 = shelf1core(src2, a1, b0, b1);
return out_1, out_2;
}
hishelf12(src1, src2, pitch, db){
// pitch = midi, 0 to 127
// db = shelf, -12 to 12
History b, b2, f1, f2, a1, a2, b0, b1;
if((change(pitch)!=0)||(change(db) !=0)){
b = pow(1.059253692626953, db *.5);
b2 = b * b;
f1 = tan(clip(mtof(pitch) *b,
samplerate /24576,
samplerate /2.125) *pi /samplerate);
f2 = 1 + f1;
a1 = (1 - f1)/ f2;
a2 = (b2 -1) / f2;
b0 = a2 +1;
b1 = neg(a2 + a1);
}
out_1 = shelf1core(src1, a1, b0, b1);
out_2 = shelf1core(src2, a1, b0, b1);
return out_1, out_2;
}
shelf2corelo(input, a0, a1, b1, w){
// IIR with LP FIR, required for 2pole lo eq
History z1;
x = a0 * (input - a1 * z1);
output = w * (x + z1);
z1 = x;
return output * b1 + input;
}
loshelf22(src1, src2, p, db){
// p (default=60, min=10, max=110); //pitch, MIDI
// db(default= 0, min=-24, max=24); //dB, cut/boost
History b0, f, b1, w, a0, a1;
if(change(p)!=0 || change(db) !=0){
b0 = pow(1.059253692626953, db *.5);
f = mtof(p) / b0;
b1 = b0 *b0 -1;
w = tan(f * pi / samplerate);
a0 = 1 / (w +1);
a1 = w -1;
}
Left = shelf2corelo(src1, a0, a1, b1, w);
Right = shelf2corelo(src2, a0, a1, b1, w);
return Left, Right;
}
shelf2corehi(input, a0, a1, b1){
// IIR with HP FIR, required for 2pole hi eq
History z1;
x = a0 * (input - a1 * z1);
output = x - z1;
z1 = x;
output = output * b1 + input;
return output;
}
hishelf22(src1, src2, p, db){
// Stereo
// p (default=60, min=10, max=110); //pitch, MIDI
// db(default= 0, min=-12, max=12); //dB, cut/boost
History b0, f, b1, w, a0, a1;
if(change(p)!=0 || change(db) !=0){
b0 = pow(1.059253692626953, db *.25);
f = min(mtof(p) * b0, samplerate);
b1 = b0 *b0 -1;
w = tan(max(f, 0) * pi / samplerate);
a0 = 1 / (w +1);
a1 = w -1;
}
Left = shelf2corehi(src1, a0, a1, b1);
Right = shelf2corehi(src2, a0, a1, b1);
return Left, Right;
}
peakeq1core(input, w, d2, a0, a1, a2){
// required for peak eq
History z1, z2, z3, z4;
output = (input - z2) * w;
z2 = z1;
z1 = input;
output = a0 * (output -(a1 *z3) -(a2 *z4));
z4 = z3;
z3 = output;
output = input + output * d2;
return output;
}
peakeq1(input, p, db, bw){
// p (default=60, min=10, max=110); //pitch, MIDI
// db(default= 0, min=-24, max=24); //dB, cut/boost
// bw(default=1, min=.1, max=10); //bw, octs at mid slope
History w, b0, d0, d, d2, wq, w2, a0, a1, a2;
if(change(p)!=0 || change(db) !=0 || change(bw) !=0 ){
w = qtan(mtof(p) *pi /samplerate); // FIR coeff
b0 = pow(1.059253692626953, db *.5);// IIR coeff
b = (b0 *b0) -1;
d0 = pow(1.414213538169861, bw);
d = clip((d0 *d0 -1)/(d0 *b0), .01, 100);
d2 = d * b;
wq = d * w;
w2 = w * w;
a0 = 1 / (1 + w2 + wq);
a1 = (w2 - 1) *2;
a2 = 1 + w2 -wq;
}
return peakeq1core(input, w, d2, a0, a1, a2);
}
peakeq12(src1, src2, p, db, bw){
// p (default=60, min=10, max=110); //pitch, MIDI
// db(default= 0, min=-24, max=24); //dB, cut/boost
// bw(default=1, min=.1, max=10); //bw, octs at mid slope
History w, b0, d0, d, d2, wq, w2, a0, a1, a2;
if(change(p)!=0 || change(db) !=0 || change(bw) !=0 ){
w = tan(mtof(p) *pi /samplerate); // FIR coeff
b0 = pow(1.059253692626953, db *.5);// IIR coeff
b = (b0 *b0) -1;
d0 = pow(1.414213538169861, bw);
d = clip((d0 *d0 -1)/(d0 *b0), .01, 100);
d2 = d * b;
wq = d * w;
w2 = w * w;
a0 = 1 / (1 + w2 + wq);
a1 = (w2 - 1) *2;
a2 = 1 + w2 -wq;
}
Left = peakeq1core(src1, w, d2, a0, a1, a2);
Right = peakeq1core(src2, w, d2, a0, a1, a2);
return Left, Right;
}
eqsel(src1, src2, lop, midp, hip, log, midg, hig, bw, mode){
// lop, midp, hip = pitch, MIDI
// log, hig, midg = gain, db, +/- 12db
// bw = peach bandwithc, oct, 0.1 to 10
// mode = 0 is off, 1 is 1pole, 2 is 2pole
x = src1;
y = src2;
if (mode==1){
x, y = peakeq12(x, y, midp, midg, bw);
x, y = loshelf12(x, y, lop, log);
x, y = hishelf12(x, y, hip, hig);
}else if(mode==2){
x, y = peakeq12(x, y, midp, midg, bw);
x, y = loshelf22(x, y, lop, log);
x, y = hishelf22(x, y, hip, hig);
}
return x, y;
}
//---------------------------------------------------------------------
Param lop, midp, hip, log, midg, hig, bw, mode;
// lop, midp, hip = pitch, MIDI
// log, hig, midg = gain, db, +/- 12db
// bw = peach bandwithc, oct, 0.1 to 10
// mode = 0 is off, 1 is 1pole, 2 is 2pole
out1, out2 = eqsel(in1, in2, lop, midp, hip, log, midg, hig, bw, mode);
A synthax-highlighted gen~ listing and simple demo for the above code is now available at the top of the synthcore page here: https://yofiel.com/audio/synthcore.php
The synthcore library is several thousand lines, so it may take your device a couple of seconds to complete color syntax highlighting before this page is displayed and locally cached.
Hey Ernest, thanks a lot for sharing your work, it's a really great resource.
Slightly offtopic : when using _SYNTH_FULL.maxpat (included in the synthcore library zip) with a midi controller (no problem with the kslider), any note-off message sent will trigger as if it were a note-on message. So if you have several keys pressed, and you release one of those keys, that key will be played again. I've tried a little to fix that myself, without success :p any hint at how this could be resolved ?
Sorry to say I cant even open that patch any more, it has deprecated objects in it, so I' ll delete it from the distribution, instead there is a new design here that does polyphonic voice allocation in gen:
https://yofiel.com/audio/husserl2.php
Does that one work for you?