gen~ functions for lo, hi, and peak shelving EQ

Ernest's icon

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);
Ernest's icon

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.


vichug's icon

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 ?

Ernest's icon

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?