```

```

### 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 :)

```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 } );```