The sound of connecting to the Internet before the 2000s.
Based on: windytan.com/2012/11…dialup-pictured.html
Log in to post a comment.
// Dial-up internet. Created by Reinder Nijhoff 2023 // https://dittytoy.net/ditty/91185791e3 // // The sound of connecting to the Internet before the 2000s. // // https://www.windytan.com/2012/11/the-sound-of-dialup-pictured.html // https://oona.windytan.com/posters/dialup-final.png // ditty.bpm = 60; // so one tick is one second // // Synth that emulates the sound of sending data // const data = synth.def(class { // https://en.wikipedia.org/wiki/Modem // https://en.wikipedia.org/wiki/Phase-shift_keying constructor(options) { this.freq = options.carrier * Math.PI * 2; this.phase = 0; this.amp = 1; this.bitTimeOut = 0; } process(note, env, tick, options) { this.bitTimeOut -= ditty.dt; if (this.bitTimeOut < 0) { const bits = Math.random() * 4 | 0; switch (options.cmd) { case 'ASK': // Amplitude-shift keying this.amp = (bits + 1) / 5; break; case 'FSK': // Frequency-shift keying const c = options.carrier; this.freq = bits < 2 || tick < 0.1 ? c[0] : c[1]; this.freq *= Math.PI * 2; break; case 'PSK': // Phase-shift keying this.phase += bits * Math.PI / 2; break; } this.bitTimeOut = 1 / options.baud; } return Math.sin( this.phase += this.freq * ditty.dt ) * this.amp; } }, { env: adsr, attack: 0.01, release: 0.01, carrier: 1920, baud: 2400, amp: 0.35, cmd: 'PSK' } ); // // Dial synth // const dial = synth.def(class { constructor(options) { // Multi-frequency signaling this.freq = []; this.time = 0; // https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-V.8bis-199608-S!!PDF-E&type=items const freqTable = { // https://en.wikipedia.org/wiki/Call-progress_tone 'dial': [350, 440], 'ring': [440, 480], // V.8 bis Events - https://www.rfc-editor.org/rfc/rfc4734.html 'ESiSeg' : [980], 'ESrSeg' : [1650], 'CRdSeg' : [1900], 'CReSeg' : [400], 'MRdSeg' : [1150], 'MReSeg' : [650], 'V8bISeg': [1375, 2002], 'V8bRSeg': [1529, 2225], 'ANSam': [2100], // disable echo suppression - should have phase reversals and amp modulation }; if (freqTable[options.cmd]) { this.freq = freqTable[options.cmd]; } else { // https://en.wikipedia.org/wiki/Dual-tone_multi-frequency_signaling const freqCol = [ 1209, 1336, 1477, 1633 ]; const freqRow = [ 697, 770, 852, 941 ]; const index = '123A456B789C*0#D'.indexOf(options.cmd); if (index >= 0) { this.freq = [freqCol[ index % 4], freqRow[ index / 4 | 0] ]; } } } process(note, env, tick, options) { this.time += ditty.dt; // amplitude modulation const mod = options.mod ? lerp(options.mod[1], options.mod[2], .5 + .5 * Math.sin(options.mod[0] * ditty.time)) : 1; // phase reversals if (options.phaserev > 0 && (ditty.time - ditty.dt) % options.phaserev > ditty.time % options.phaserev) { this.time += 0.5 / this.freq[0]; } return this.freq.reduce( (a, c) => a + Math.sin(c * this.time * Math.PI * 2), 0) * .5 * mod; } }, {env: adsr, attack: 0.01, release: 0.01, duration: 0.2, cmd: 'dial', mod: false, phaserev: 0}); const noise = synth.def( _ => Math.random()*2-1, {env: one}); // // Second order lowpass filter without resonance (mono) by @athibaul // https://dittytoy.net/ditty/49c3b85cc7 // const lpf = filter.def(class { constructor(options) { this.s0 = 0; this.s1 = 0; } process(input, options) { this.a0 = clamp01(2*Math.PI * ditty.dt * options.cutoff); this.s0 += this.a0 * (input[0] - this.s0); this.s1 += this.a0 * (this.s0 - this.s1); //return this.s1; // Uncomment this line to get a cryptic error message return [this.s1, this.s1]; } }, { cutoff: 2500 }); // // Helper function // function modem(cmd, dur, op = {}, slp = 0) { const options = Object.assign(op, { cmd, duration: dur + Math.random()*0.01 }); if (cmd === 'ASK' || cmd === 'FSK' || cmd === 'PSK') { data.play(0, options); } else { dial.play(0, options); } sleep(slp + dur + Math.random()*0.01); } // // Dial-up sequence // loop( () => { noise.play(0, {duration: 16, amp: 0.015}); sleep(1); modem('dial', 1.1, {}, -0.1 ); const number = '0205350535'; for (let i=0; i<number.length; i++) { modem( number[i], 0.1, {}, 0.1 ); } sleep(1); // phase 1 - netwerk interaction modem('V8bISeg', 0.4); modem('CReSeg', 0.1); modem('V8bRSeg', 0.4); modem('CRdSeg', 0.1); modem('ESrSeg', 0.1); modem('FSK', 0.7, { baud: 300, carrier: [980, 1180] }, 0.1); modem('FSK', 0.8, { baud: 300, carrier: [1650, 1850] }, -0.2); modem('FSK', 0.3, { baud: 300, carrier: [980, 1180] }, 0.35); modem('ANSam', 2, { mod: [15 * Math.PI * 2, 0.8, 1.2], phaserev: 0.450 }, -1); modem('FSK', 2, { baud: 300, carrier: [980, 1180] }, -1); modem('FSK', 1, { baud: 300, carrier: [1650, 1850] }); // phase 2 - probing / ranging modem('PSK', 0.1, { baud: 600, carrier: 1180 }, -0.1); modem('PSK', 0.1, { baud: 600, carrier: 1850 }); // frequencies by srtuss for (let i=0; i<25; i++) { if(i != 5 && i != 7 && i != 11 && i != 15) sine.play(hz_to_midi(150+150*i), { duration: 0.355, attack: 0.01, release: 0.01, amp: (0.4+Math.random()*.2)/25}); } sleep(.340); modem('PSK', 0.1, { baud: 600, carrier: 1850 }); modem('PSK', 0.1, { baud: 600, carrier: 1180 }); for (let i=0; i<25; i++) { if(i != 5 && i != 7 && i != 11 && i != 15) sine.play(hz_to_midi(150+150*i), { duration: 0.355, attack: 0.01, release: 0.01, amp: (0.4+Math.random()*.2)/25}); } sleep(.340); modem('PSK', 0.1, { baud: 600, carrier: 1850 }); modem('PSK', 0.1, { baud: 600, carrier: 1180 }); sleep(.1); // phase 3 - equalizer and echo canceller training modem('PSK', 4, { baud: 600, carrier: 1920, amp: 0.3 }, -2); modem('PSK', 2, { baud: 1200, carrier: 1829, amp: 0.3 }); }, { sync: 16, amp: (tick) => clamp01(tick % 16) - clamp01((tick-1) % 16 - 15), name: 'modem'}).connect(lpf.create());