12 bar blues (first recording)

An experiment to fine-tune the sound of the 12 bar blues by @markknol by adding a low-pass filter to the synth and a delay filter to the loops.

Log in to post a comment.

// Forked from "12 bar blues" by markknol
// https://dittytoy.net/ditty/18766aafd1

const tick  = synth.def( (phase, env, tick) => 2 * (Math.random() - .5) * Math.exp(-tick * 200) * env.value, {attack: 0.0025, release: 0.01, amp: 4, env: adsr2});
const noise = synth.def( _ => Math.random() * 2 - 1, { amp: 4, env: adsr2 } );
const bluesGuitar= synth.def( 
    class {
            constructor(options) {
                this.phase = 0;
                this.hist0 = 0;
                this.hist1 = 0;
            }
            process(note, env, tick, options) {
                this.phase += midi_to_hz(note - env.progress * .1) * ditty.dt;
                
                let ret = 0;
                for (let i=1; i<=3; i++) {
                    ret += Math.sign(((this.phase * (2 * i)) % 2) - 1) / (i ** 2);
                }

                ret *= Math.exp(-env.progress);                

                // low pass filter, cutoff is based on env.value
                const alpha = env.value * .12;
                this.hist0 += alpha * (ret - this.hist0);
                this.hist1 += alpha * (this.hist0 - this.hist1);
                
                return this.hist1;
            }
    }, { env: adsr2 } );
    
const delay = filter.def(class {
    constructor(options) {
        this.index = 0;
        this.length = options.interval * ditty.sampleRate * 60 / ditty.bpm | 0;
        this.left  = new Float32Array(this.length);
        this.right = new Float32Array(this.length); 
    }

    process(input, options) {
        if (input) {
            const s = options.strength;
            this.index = (this.index + 1) % this.length;
            const hist = (this.index + 1) % this.length;
            
            this.left [this.index] = (1-s) * input[1] + s * this.right[hist];
            this.right[this.index] = (1-s) * input[0] + s * this.left [hist];
            
            return [ this.left[this.index], this.right[this.index] ];
        }
    }
}, { interval: 1/3, strength: 0.25 }); 

const lowpass = filter.def(class {
    constructor(options) {
        this.hist0 = [0, 0];
        this.hist1 = [0, 0];
    }

    process(input, options) {
        const alpha = options.cutoff;

        if (input) {
            for (let i = 0; i < 2; i++) {
                this.hist0[i] += alpha * (input[i] - this.hist0[i]);
                this.hist1[i] += alpha * (this.hist0[i] - this.hist1[i]);
            }

            return this.hist1;
        }
    }
}, {cutoff: 1});
    

const distortion  = filter.def(class {
    process(input, options) {
        const amp    = options.amp;
        const cutoff = options.cutoff;

        if (input) {
            return [clamp(input[0] * amp, -cutoff, cutoff), clamp(input[1] * amp, -cutoff, cutoff)];
        }
    }
}, {cutoff: .7, amp: 1});    
    
const nothing = {};

const progression = [
   chord(c3, chords.major), chord(f3, chords.major), chord(c3, chords.major), chord(c3, chords.dom7),
   chord(f3, chords.major), chord(f3, chords.dom7),  chord(c3, chords.major), chord(c3, chords.dom7),
   chord(g3, chords.major), chord(f3, chords.major), chord(c3, chords.major), chord(g3, chords.dom7),
];

loop(() => {
    progression.forEach(c => {
       sleep(playChord(c, 6/8*2));
       sleep(playChord(c, 6/8));
    });
},{name:'chords', amp: () => clamp01(ditty.time - 8 * 60 /  ditty.bpm)})
.connect( delay.create() );

loop(() => {
    progression.forEach(_ => {
        sleep(playChord(scale(a3, scales.minor_pentatonic, 3), 6/8*pick(0, 0.5, 1, 2, 4, 8), 6/8, 0.25));
    });
},{name:'melody', amp:  () => clamp01(ditty.time - 16 * 60 /  ditty.bpm) * 1.5})
.connect( delay.create( { interval: 2/3, strength: 0.1 }) )
.connect( distortion.create() )
.connect( delay.create() )
.connect( lowpass.create( { cutoff: .75 }) );

function playChord(c, t = 6/8, interv = 6/8/8, chance = 0.95) {
    [...c].sort(_ => -0.5 + Math.random()).forEach(n => (Math.random() < chance ? bluesGuitar.play(n, {amp: 0.5 + 0.25 * Math.random()}) : nothing, sleep(interv))); 
    t -= c.length * interv;
    return t;
}

function pick(...args) {
    return args[Math.random() * args.length | 0];
}

loop(() => {
    tick.play(c1);
    sleep(Math.sqrt(2));
},{name:'tick 1', amp: .25})
.connect( lowpass.create( { cutoff: .2 }) );

loop(() => {
    tick.play(c1);
    sleep(1.61803398875);
},{name:'tick 2', amp: 1})
.connect( lowpass.create( { cutoff: .1 }) );

loop(() => {
    noise.play(c1, { env: one, duration: 1e5, amp: () => .5 + .1 * Math.sin(ditty.time)});
    sleep(1e5);
},{name:'noise', amp: () => .2 * clamp01(ditty.time)})
.connect( lowpass.create( { cutoff: .15 }) );