Amplitude decay

Comparison between a linear decay and an exponential decay of amplitude.
Notice how the linear decay sounds more saturated, then seemingly ceasing more abruptly. The difference is most noticeable with headphones.

Log in to post a comment.

// A demonstration of how different types of amplitude decay sound.
// I suggested making the Decay and Release phase of the Ditty ADSR an exponential curve by default.
//
// Synthesizers commonly model a "fading" quantity as an exponential curve, since our ears' perception of volume is logarithmic.
// In other words, the greater the amplitude of sound is, the larger a change in amplitude would be necessary for it to be noticable.
//
// Why not simply make all ADSR stages linear and map it to a logarithmic scale afterwards?
// In most cases a linear Attack is still desireable because it "fades in" the sound. Furthermore, if the envelope value is also used
// in other calculations, you have the unexpected shift in the sustain level.
//
// In the analog world, an ADSR generator would commonly produce the exponential stage by RC discharge, while using a current source
// to charge the capacitor linearly.

input.decayType = 0; // min=0, max=1, step=1 (linear, exponential)

const bell = synth.def(class {
    constructor(opt) {
        this.p1 = 0;
        this.p2 = 0;
        this.fm = 3;
        this.time = 0;
    }
    process(note, env, tick, opt) {
        // generate sound
        var o1 = Math.sin(this.p1 * Math.PI * 2);
        var o2 = Math.sin(this.p2 * Math.PI * 2);
        this.p1 += midi_to_hz(note) * ditty.dt * 8;
        this.p2 += midi_to_hz(note) * ditty.dt * (1 + o1 * this.fm);
        this.fm *= .9995;
        
        // generate decay envelope
        var decayLinear = Math.max(1 - this.time * 2, 0);
        var decayExponential = Math.exp(-Math.min(this.time, 1) * 7) - Math.exp(-1 * 7);
        
        var amplitude = input.decayType ? decayExponential : decayLinear;
        
        this.time += ditty.dt;
        return o2 * amplitude;
    }
});

const ns = scale(c4, scales['harmonic_minor'], 3);

loop( () => {
    bell.play(ns.choose(), { attack: 0.001, release: .0, pan: Math.random() * 2 - 1, amp: .5, duration: 2 });
    sleep(Math.random() * .05);
    bell.play(ns.choose(), { attack: 0.001, release: .0, pan: Math.random() * 2 - 1, amp: .5, duration: 2 });
    sleep( .8 );
}, { name: 'bell' });