Antiderivative anti-aliasing

Implementing a basic anti-aliasing strategy from a 2016 DAFx paper. Listen for the aliasing artifacts (low frequencies produced when the filter is fed with high frequencies) : this technique aims to reduce them. Better results could be obtained with a higher-order kernel, or by oversampling.

Log in to post a comment.

// Antiderivative anti-aliasing, as proposed in the 2016 DAFx paper:
//
// Parker, J. D., Zavalishin, V., & Le Bivic, E. (2016, September).
// Reducing the aliasing of nonlinear waveshaping using continuous-time convolution.
// In Proc. Int. Conf. Digital Audio Effects (DAFx-16), Brno, Czech Republic (pp. 137-144).
//
// http://dafx.de/paper-archive/2016/dafxpapers/20-DAFx-16_paper_41-PN.pdf

function abs(x) {
    return x >= 0 ? x : -x;
}

// Hard clipping function
function clip11(x) {
    return x < -1 ? -1 : x > 1 ? 1 : x;
}
// Antiderivative of hard clipping function
function clip11_antider(x) {
    return x < -1 ? -x-0.5 : x > 1 ? x-0.5 : 0.5*x*x;
}
// Soft clipping function
function softclip(x) {
    return x < -1 ? -1 : x > 1 ? 1 : 1.5*(1 - x*x/3)*x;
}
function softclip_antider(x) {
    const x2 = x*x;
    return x < -1 ? -x-(3/8) : x > 1 ? x-(3/8) : (3/4)*x2 - (1/8)*x2*x2;
}

const hardClipper = filter.def(
    (input,options) => [options.clipf(input[0]) * options.outgain, options.clipf(input[1]) * options.outgain],
    {clipf: () => clip11, outgain:0.1}
);

const hardClipperADAA = filter.def(class {
    constructor(options) {
        this.x_nm1 = 0;
        this.F_xnm1 = 0;
    }
    process(input, options) {
        let x_n = input[0];
        if(abs(x_n - this.x_nm1) > 1e-3) {
            const F_xn = options.clipF(x_n);
            const outv = (F_xn - this.F_xnm1) / (x_n - this.x_nm1) * options.outgain;
            this.x_nm1 = x_n;
            this.F_xnm1 = F_xn;
            return [outv,outv];
        } else {
            const outv = options.clipf(0.5*(x_n + this.x_nm1)) * options.outgain;
            return [outv,outv];
        }
    }
}, {
    clipf: () => clip11,
    clipF: () => clip11_antider,
    outgain: 0.1
});

loop( (i) => {
    sine.play((tick) => hz_to_midi(20000*tick/5), {env:one, duration:5, amp:3});
    sleep(7);
    sleep(10);
    sleep(7);
    sleep(10);
}, {name:"Hard clipping (naive)"}).connect(hardClipper.create());

loop( (i) => {
    sleep(7);
    sine.play((tick) => hz_to_midi(20000*tick/5), {env:one, duration:5, amp:3});
    sleep(10);
    sleep(7);
    sleep(10);
}, {name:"Hard clipping with ADAA"}).connect(hardClipperADAA.create());


loop( (i) => {
    sleep(7);
    sleep(10);
    sine.play((tick) => hz_to_midi(20000*tick/5), {env:one, duration:5, amp:2});
    sleep(7);
    sleep(10);
}, {name:"Soft clipping (naive)"}).connect(hardClipper.create({clipf: () => softclip}));


loop( (i) => {
    sleep(7);
    sleep(10);
    sleep(7);
    sine.play((tick) => hz_to_midi(20000*tick/5), {env:one, duration:5, amp:2});
    sleep(10);
}, {name:"Soft clipping with ADAA"}).connect(hardClipperADAA.create({clipf: () => softclip, clipF: () => softclip_antider}));