Poing

I still don't understand how it could be, but Poing by the Rotterdam Termination Source topped the charts in the Netherlands in the summer of 1992.

youtu.be/lq6jzgmazk4

I use it to stress test the new debug features :)

Log in to post a comment.

ditty.bpm = 140;

const kick      = synth.def( (phase, env, tick, options) => Math.sin(phase * 2 * Math.PI * Math.exp(-env.progress)) * env.value, { attack: 0.05, release: 0.3, amp: 1.25, env: adsr2 });
const noise     = synth.def( (phase, env, tick, options) => (Math.random() - .5 + 2 * Math.sin(phase)) * (env.value ** 2), { attack: 0.0125, release: 0.45, amp: 1, env: adsr2 });
const poingLine = synth.def( _ => { 
    let x = ditty.tick % 1, base = .8; 
    const i = Math.floor(x = -Math.log(1-x)/Math.log(base));
    x = (1-Math.pow(base, i-x)) / (1-1/base);
    let bounce = -4*x*(x-1) / Math.pow(base*.8, i+1);
    debug.probe('poing', 2 * bounce - 1, 1, tick_to_second(2));
    return 0;
}, { env: one });

// My attempt to create the sound of a mouth harp 

// const poingOsc = synth.def(
//     class {
//         constructor(options) {
//             this.phase = 0;
//         }
//         process(note, env, tick, options) {
//             this.phase += midi_to_hz(note) * ditty.dt * ((env.value + 0.01) ** options.xenv);
//             const o   = .2 * Math.sin(this.phase);
//             let wave  = Math.sin(this.phase * Math.PI * 2 + o);
//             wave     += Math.sin(this.phase * Math.PI * 4.1 + o) / 2;            
//             wave     += Math.sin(this.phase * Math.PI * 7.9 + o) / 3;

//             return wave * (1 - ease01.cubeOut (env.progress));
//         }
//     }, { xenv: -0.4, amp: 1, attack: 0.1, release: 1, env: adsr2 }
// ); 


// Boing sound by srtuss. Much better!  
// https://dittytoy.net/ditty/4a954c58b5
//
// the boing is roughly a sawtooth with a root frequency of 96Hz, through a band-pass filter
// the center frequency of the filter sits at 1.2kHz for 90ms, then within 90ms sweeps to 2.9kHz were it stays until the end of the sound
const poingOsc = synth.def(class {
    constructor(opt) {
        this.p = 0;
        // crude state variable filter, 12dB per octave
        this.flt = {lp: 0, bp: 0, hp: 0};
        this.t = 0;
        this.svf = (s, inpt, kf, kq) => {
            var lp, hp, bp;
            lp = s.lp + kf * s.bp;
            hp = inpt - lp - kq * s.bp;
            bp = s.bp + kf * hp;
            s.lp = lp;
            s.hp = hp;
            s.bp = bp;
        };
    }
    process(note, env, tick, opt) {
        // create a sawtooth mixed with a slight square timbre, at a fixed frequency
        var v = (this.p % 1 + (this.p % 1 < .33 ? .1 : -.1) - .5) * env.value;
        this.p += 96 * ditty.dt;
        // add a white noise burst at the beginning
        v += (Math.random() - .5) * Math.exp(-this.t * 60) * .3;
        // feed it through the bandpass filter and sweep the filter's cutoff frequency
        this.svf(this.flt, v, lerp(.169, .41, clamp01((this.t - .09) / .09) ** 2), .08);
        this.t += ditty.dt;
        // mix the filter output and saturate it sligthly
        return Math.sin((this.flt.bp + this.flt.lp * .5 + this.flt.hp * .1) * 4 * env.value);
    }
}, {attack: 0, decay: .4, sustain: .2, release: 0.01, duration: .372, amp: .75});


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) {
            this.index = (this.index + 1) % this.length;
            const hist = (this.index + 1) % this.length;
            const s = options.strength;
            
            this.left [this.index] = input[1] + s * this.right[hist];
            this.right[this.index] = input[0] + s * this.left [hist];
            
            return [ this.left[this.index], this.right[this.index] ];
        }
    }
}, { interval: 1, strength: 0.15 }); 

loop( (i) => {
    poingOsc.play(c);
    kick.play(c);
    noise.play(c3);
    sleep(.5);
    if (i % 8 > 3) noise.play(c3);
    sleep(.5);
} , {sync: 1, amp: .75}).connect( delay.create() );


loop( () => {
    // bouncing ball ascii art in console
    const width = 80;
    const height = 3;
    const posX = width * 2 * Math.abs(ditty.time * .25 % 1 - .5) | 0;
    const posY = height * (ditty.tick % 1) ** 2 | 0;
    
    const b = posY > 1 ? '0' : 'o';
    for (let line=0; line<height; line++) {
        let str = '';
        for (let x=0; x<width; x++) {
            str += x == posX && line == posY ? b : ' ';
        }
        debug.log(line, str);
    }
}, {sync: 1/120});

loop( () => {
    // bouncing ball osciloscope
    poingLine.play(c, { duration: 1e5, amp: 0 });
}, { sync: 1e5 } );