A quick experiment to port the synth and filter code from SoundBox player-small.js to DittyToy.
SoundBox can be found here: sb.bitsnbites.eu/
code: github.com/mbitsnbites/soundbox (GPL v3)
reddit: reddit.com/r/soundboxmusic/
The song is created in SoundBox by @m_bitsnbites for the 4K demo SWAY pouet.net/prod.php?which=59203
Log in to post a comment.
// // A quick experiment to port the synth and filter code from SoundBox player-small.js to DittyToy. // // SoundBox can be found here: // http://sb.bitsnbites.eu/ // code: https://github.com/mbitsnbites/soundbox (GPL v3) // reddit: https://www.reddit.com/r/SoundBoxMusic/ // // The song is created in SoundBox by @m_bitsnbites for the 4K demo SWAY // https://www.pouet.net/prod.php?which=59203 // const mOscillators = [ (value) => Math.sin(value * (Math.PI * 2)), (value) => 2 * (value % 1) - 1, (value) => (value % 1) < 0.5 ? 1 : -1, (value) => { const v = (value % 1) * 4; return v < 2 ? v - 1 : 3 - v; } ]; const song = songData(), mLastRow = song.endPattern, mNumWords = song.rowLen * song.patternLen * (mLastRow + 1), rowLen = song.rowLen, patternLen = song.patternLen; const getnotefreq = (n) => 174.6 / ditty.sampleRate * (2 ** ((n - 128) / 12)); // // Soundbox Envelope // // Optimized because we know that evelope options won't change during playback // const sndboxenv = env.def( class { duration(options) { return options.attack + options.sustain + options.release; } value(tick, options) { if (tick < options.attack) { return tick / options.attack; } else if (tick >= options.attack + options.sustain) { const e = (tick - options.attack - options.sustain) / options.release; return (1 - e) * (3 ** (options.expDecay * e)); } return 1; } }, {attack: 0, sustain: 0, release: 1, expDecay: 0, name: 'sndboxenv'} ); // // Soundbox Synth // // Optimized because we know that synth options won't change during playback // const sndboxsynth = synth.def( class { constructor(options) { this.c1 = 0; this.c2 = 0; const instr = options.instr; this.osc1 = mOscillators[instr.i[0]]; this.o1vol = instr.i[1]; this.o1xenv = instr.i[3] / 32; this.osc2 = mOscillators[instr.i[4]]; this.o2vol = instr.i[5]; this.o2xenv = instr.i[8] / 32; this.noiseVol = instr.i[9]; // Calculate note frequencies for the oscillators this.o1t = getnotefreq(options.note + instr.i[2] - 128); this.o2t = getnotefreq(options.note + instr.i[6] - 128) * (1 + 0.0008 * instr.i[7]); } process(note, env, tick, options) { // Envelope const e = env.value; // Oscillator 1 this.c1 += this.o1t * (e ** this.o1xenv); let rsample = this.osc1(this.c1) * this.o1vol; // Oscillator 2 this.c2 += this.o2t * (e ** this.o2xenv); rsample += this.osc2(this.c2) * this.o2vol; // Noise oscillator if (this.noiseVol) { rsample += (2 * Math.random() - 1) * this.noiseVol; } return rsample * 80 * e; } }, {} ); // // Soundbox Filter // // Optimized: filter options won't change during playback // const sndboxfilter = filter.def(class { constructor(options) { this.low = 0; this.high = 0; this.band = 0; this.k = 0; this.len = mNumWords; this.leftBuffer = new Float32Array(this.len); this.rightBuffer = new Float32Array(this.len); // Put performance critical instrument properties in local variables const instr = options.instr; this.oscLFO = mOscillators[instr.i[16]], this.lfoAmt = instr.i[17] / 512, this.lfoFreq = (2 ** (instr.i[18] - 9)) / rowLen / 2, this.fxLFO = instr.i[19], this.fxFilter = instr.i[20], this.fxFreq = instr.i[21] * 43.23529 * 3.141592 / ditty.sampleRate, this.q = 1 - instr.i[22] / 255, this.dist = instr.i[23] * 1e-5, this.drive = instr.i[24] / 32, this.panAmt = instr.i[25] / 512, this.panFreq = 6.283184 * (2 ** (instr.i[26] - 9)) / rowLen / 2, this.dlyAmt = instr.i[27] / 255, this.dly = instr.i[28] * rowLen / 2 | 0; // Must be an even number } process(input, options) { // Dry mono-sample let rsample = input[0]; const k = this.k; let f = this.fxFreq; if (this.fxLFO) { f *= this.oscLFO(this.lfoFreq * k) * this.lfoAmt + 0.5; } f = 1.5 * Math.sin(f); this.low += f * this.band; this.high = this.q * (rsample - this.band) - this.low; this.band += f * this.high; rsample = this.fxFilter == 3 ? this.band : this.fxFilter == 1 ? this.high : this.low; // Distortion if (this.dist) { rsample *= this.dist; rsample = rsample < 1 ? rsample > -1 ? Math.sin(rsample * .5 * Math.PI) : -1 : 1; rsample /= this.dist; } // Drive rsample *= this.drive; // Panning let t = Math.sin(this.panFreq * k) * this.panAmt + 0.5; let lsample = rsample * (1 - t); rsample *= t; // Delay is always done, since it does not need sound input if (k >= this.dly) { // Left channel = left + right[-p] * t lsample += this.rightBuffer[(k - this.dly) % this.len] * this.dlyAmt; // Right channel = right + left[-p] * t rsample += this.leftBuffer[(k - this.dly) % this.len] * this.dlyAmt; } this.leftBuffer[k % this.len] = lsample; this.rightBuffer[k % this.len] = rsample; this.k = (this.k + 1) % mNumWords; lsample /= 255 * 40; rsample /= 255 * 40; return [lsample, rsample]; } }, {}); // // Create a separate loop for every SoundBox column // for (let mCurrentCol = 0; mCurrentCol < song.numChannels; mCurrentCol++) { const instr = song.songData[mCurrentCol]; // Clear effect state let low = 0, band = 0, high; let lsample, filterActive = false; loop( () => { // Patterns for (let p = 0; p <= mLastRow; ++p) { const cp = instr.p[p]; // Pattern rows for (let row = 0; row < patternLen; ++row) { // Execute effect command. const cmdNo = cp ? instr.c[cp - 1].f[row] : 0; if (cmdNo) { instr.i[cmdNo - 1] = instr.c[cp - 1].f[row + patternLen] || 0; } // Generate notes for this pattern row for (let col = 0; col < 4; ++col) { const n = cp ? instr.c[cp - 1].n[row + col * patternLen] : 0; if (n) { const attack = (instr.i[10] ** 2) * 8 / (ditty.sampleRate), sustain = (instr.i[11] ** 2) * 8 / (ditty.sampleRate), release = (instr.i[12] ** 2) * 8 / (ditty.sampleRate); sndboxsynth.play(n, { instr, attack, sustain, release, expDecay: -instr.i[13] / 16, env: sndboxenv }); } } sleep(.25); } } }) .connect( sndboxfilter.create( {instr} ) ); } // This music has been exported by SoundBox. You can use it with // http://sb.bitsnbites.eu/player-small.js in your own product. function songData() { return { songData: [{ i: [2, 100, 128, 0, 3, 201, 128, 0, 0, 0, 0, 6, 29, 0, 0, 0, 0, 194, 4, 1, 3, 25, 191, 115, 244, 147, 6, 84, 6], p: [5, 1, 2, 1, 1, 1, 1, 3, 4, 1, 2, 1, 2, 1, 2, , , 7, 8, 7, 8, 7, 8, 7, 8, 6, 9], c: [{ n: [123, 123, 135, 128, 123, 123, 135, 130, 126, 125, 126, 128, 123, 123, 135, 128, 123, 123, 135, 130, 126, 125, 126, 128, 123, 123, 135, 128, 123, 123, 135, 130], f: [22, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 48] }, { n: [123, 123, 135, 128, 123, 123, 135, 130, 126, 125, 126, 128, 123, 123, 135, 128, 123, 123, 135, 130, 126, 125, 126, 128, 123, , 99, , , , 99, , , , , , , , , , , , , , , , , , , , , , , , , , 111], f: [, , , , , , , , , , , , , , , , , , , , , , , , , , , 11, 13, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 31] }, { n: [111, , , , , , , , 123, , , , , , , , 111, , , , , , , , 123], f: [] }, { n: [114, , , , , , , , 126, , , , , , , , 114, , , , , , , , 114, 126, 111, 123, 114, 126, 118, 130], f: [13, , , , , , , , , , , , , , , , , , , , , , , , 13, , 13, , 13, , 13, 11, 29, , , , , , , , , , , , , , , , , , , , , , , , 32, , 41, , 29, , 25, 15] }, { n: [123, 123, 135, 128, 123, 123, 135, 130, 126, 125, 126, 128, 123, 123, 135, 128, 123, 123, 135, 130, 126, 125, 126, 128, 123, , 99, , , , 99, , , , , , , , , , , , , , , , , , , , , , , , , , 111], f: [13, 11, 22, 18, , , , , , , , , , , , , , , , , , , , , , , , , , , , 18, 29, , 25, 113, , , , , , , , , , , , , , , , , , , , , , , , , , , , 194] }, { n: [111, , , , , , , , 123, , , , , , , , 111, , , , , , , , 99], f: [] }, { n: [111, 123, , 123, 111, , 123, 111, 111, 123, , 123, 111, , 123, 111, 111, 123, , 123, 111, , 123, 111, 111, 123, , 123, 111, , 123, 111], f: [] }, { n: [114, 126, , 126, 114, , 126, 114, 114, 126, , 126, 114, , 126, 114, 114, 126, , 126, 114, , 126, 114, 116, 128, , 128, 116, , 114, 121], f: [] }, {n: [], f: []}] }, { i: [0, 255, 117, 64, 0, 255, 110, 0, 64, 0, 4, 6, 35, 0, 0, 0, 0, 0, 0, 0, 2, 14, 1, 1, 39, 76, 5, 0, 0], p: [, 1, 1, 1, 1, 1, 1, 2, , , , 1, 1, 1, 1, 1, 1, , , 1, 1, 1, 1, 1, 1], c: [{ n: [147, , , , , , 147, , , , 147, , , , , , 147, , , , , , 147, , , , 147, , , , 147], f: [] }, {n: [147], f: []}] }, { i: [0, 0, 140, 0, 0, 0, 140, 0, 0, 60, 4, 10, 68, 0, 0, 0, 0, 187, 5, 0, 1, 239, 135, 0, 32, 108, 5, 16, 4], p: [, 1, 1, 2, 3, 2, 3, 4, , , , 2, 3, 2, 3, 2, 3, , , 5, 5, 5, 5, 5, 5, 4], c: [{ n: [, , , , 147, , , , , , , , 148, , , , , , , , 147, , , , , , , , 147], f: [13, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 35] }, { n: [, , , , 147, , , 147, , , , , 148, , , , , , , , 147, , , 147, , , 147, , , , 147], f: [13, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 35] }, { n: [, , , , 147, , , 147, , , , , 148, , , , , , , , 147, , , 147, , , 147, , , 147, 147, 147], f: [13, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 35] }, { n: [147], f: [13, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 68] }, { n: [147, , , 147, , , 147, , 147, , , 147, , 147, , 147, 147, , , 147, , , 147, , 147, , , 147, , 147, , 147], f: [13, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 35] }] }, { i: [2, 192, 128, 0, 2, 192, 140, 18, 0, 0, 107, 115, 138, 0, 0, 0, 0, 136, 5, 1, 2, 8, 92, 21, 56, 148, 5, 85, 8], p: [3, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2], c: [{n: [111], f: []}, {n: [114], f: []}, { n: [111], f: [25, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 25, 5, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 56] }] }, { i: [3, 0, 127, 0, 3, 68, 127, 0, 64, 218, 4, 4, 40, 0, 0, 0, 1, 55, 4, 1, 2, 67, 115, 124, 190, 67, 6, 39, 1], p: [, , , 1, 2, 1, 2, 3, , , , 1, 2, 1, 2, 1, 2, , , 1, 4, 1, 4, 1, 4, 3], c: [{ n: [, , , , 147, , , , , , , , 147, , , , , , , , 147, , , , , , , , 147], f: [] }, { n: [, , , , 147, , , , , , , , 147, , , , , , , , 147, , , , , , , , 147, , 147, 147], f: [] }, {n: [147], f: []}, { n: [, , , , 147, , , , , , , , 147, , , , , , , , 147, , , , , , , , 147, , , 147], f: [] }] }, { i: [0, 91, 128, 0, 0, 95, 128, 12, 0, 0, 12, 0, 67, 0, 0, 0, 0, 0, 0, 0, 2, 255, 15, 0, 32, 83, 3, 134, 4], p: [, , , 1, 2, 3, 2, 1, 2, , , 1, 2, 3, 2, 1, 2, , , , , 4, 5, 4, 5], c: [{ n: [159, , 147, , 154, , 147, , 157, , 147, , 154, , 150, , 159, , 147, , 154, , 147, , 162, , 147, , 154, , 150, , 123], f: [5, 13, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 67] }, { n: [159, , 147, , 154, , 147, , 157, , 147, , 154, , 150, , 159, , 147, , 154, , 147, , 162, , 147, , 157, , 162, , 126], f: [] }, { n: [159, , 147, , 154, , 147, , 157, , 147, , 154, , 150, , 159, , 147, , 154, , 147, , 162, , 147, , 154, , 150, , 123], f: [5, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 3] }, { n: [159, , 162, , 164, , , , 159, , 162, 164, , , 162, , 159, , 162, , 164, , , , 159, , 162, 164, , , 162], f: [13, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 25] }, { n: [157, , 162, , 164, , , , 157, , 162, 164, , , 162, , 157, , 162, , 164, , , , 157, , 162, 164, , , 162], f: [, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 13, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 67] }] }, { i: [3, 146, 140, 0, 1, 224, 128, 3, 0, 0, 92, 0, 95, 0, 0, 0, 3, 179, 5, 1, 3, 37, 135, 63, 67, 150, 3, 157, 6], p: [, , , , , , , , , 1, 2, 3, , 1, 2, 1, 2, 3, , 4, 5, , , , , 3], c: [{ n: [123, , , , , , , , , , , , , , , , 130], f: [11, 25, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 92, 67] }, { n: [133, , , , , , , , , , , , , , , 138, 126, , , , , , , , , , , , 125], f: [11, , , , , , , , , , , , , , , , , , , , , , , , , , , , 11, , , , 95, , , , , , , , , , , , , , , , , , , , , , , , , , , , 29] }, { n: [123], f: [25, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 25, 52, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 67] }, { n: [123, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 138, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 116], f: [11, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 95] }, { n: [133, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 126, , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , 118], f: [] }] }, { i: [0, 255, 106, 64, 0, 255, 106, 0, 64, 0, 5, 7, 164, 0, 0, 0, 0, 0, 0, 0, 2, 255, 0, 2, 16, 83, 5, 53, 1], p: [, , , , , , , 1, , , , , , , , , , 1, , , , , , , , 1], c: [{n: [147], f: []}] },], rowLen: 5513, patternLen: 32, endPattern: 26, numChannels: 8 } }