From 933ef02df06968e67ff4b415838c117816e4cac7 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 25 Sep 2022 16:36:59 +0200 Subject: [PATCH 1/4] really simple reverb --- packages/webaudio/reverb.mjs | 19 +++++++++++++++++++ packages/webaudio/webaudio.mjs | 25 ++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 packages/webaudio/reverb.mjs diff --git a/packages/webaudio/reverb.mjs b/packages/webaudio/reverb.mjs new file mode 100644 index 00000000..4757932d --- /dev/null +++ b/packages/webaudio/reverb.mjs @@ -0,0 +1,19 @@ +if (typeof AudioContext !== 'undefined') { + AudioContext.prototype.impulseResponse = function (duration) { + const length = this.sampleRate * duration; + const impulse = this.createBuffer(2, length, this.sampleRate); + const IR = impulse.getChannelData(0); + for (let i = 0; i < length; i++) IR[i] = (2 * Math.random() - 1) * Math.pow(1 - i / length, duration); + return impulse; + }; + + AudioContext.prototype.createReverb = function (duration) { + const convolver = this.createConvolver(); + convolver.setDuration = (d) => { + convolver.buffer = this.impulseResponse(d); + }; + this.duration = duration; + convolver.setDuration(duration); + return convolver; + }; +} diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 1412f613..2e7de7be 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -8,6 +8,7 @@ This program is free software: you can redistribute it and/or modify it under th import * as strudel from '@strudel.cycles/core'; import { fromMidi, toMidi } from '@strudel.cycles/core'; import './feedbackdelay.mjs'; +import './reverb.mjs'; import { loadBuffer, reverseBuffer } from './sampler.mjs'; const { Pattern } = strudel; import './vowel.mjs'; @@ -207,6 +208,20 @@ function getDelay(orbit, delaytime, delayfeedback, t) { return delays[orbit]; } +let reverbs = {}; +function getReverb(orbit, duration = 2) { + if (!reverbs[orbit]) { + const ac = getAudioContext(); + const reverb = ac.createReverb(duration); + reverb.connect(getDestination()); + reverbs[orbit] = reverb; + } + if (reverbs[orbit].duration !== duration) { + reverbs[orbit].setDuration(duration); + } + return reverbs[orbit]; +} + function effectSend(input, effect, wet) { const send = gainNode(wet); input.connect(send); @@ -260,6 +275,8 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => { cut, loop, orbit = 1, + room, + size = 2, } = hap.value; const { velocity = 1 } = hap.context; gain *= velocity; // legacy fix for velocity @@ -386,12 +403,18 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => { const delyNode = getDelay(orbit, delaytime, delayfeedback, t); delaySend = effectSend(post, delyNode, delay); } + // reverb + let reverbSend; + if (room > 0 && size > 0) { + const reverbNode = getReverb(orbit, size); + reverbSend = effectSend(post, reverbNode, room); + } // connect chain elements together chain.slice(1).reduce((last, current) => last.connect(current), chain[0]); // disconnect all nodes when source node has ended: - chain[0].onended = () => chain.concat([delaySend]).forEach((n) => n?.disconnect()); + chain[0].onended = () => chain.concat([delaySend, reverbSend]).forEach((n) => n?.disconnect()); } catch (e) { console.warn('.out error:', e); } From 0e7a0b5754f9426ac4cff7d8704f8e882669dd06 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 25 Sep 2022 16:37:10 +0200 Subject: [PATCH 2/4] more tunes --- repl/src/tunes.mjs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/repl/src/tunes.mjs b/repl/src/tunes.mjs index 5d7dda2c..639fc042 100644 --- a/repl/src/tunes.mjs +++ b/repl/src/tunes.mjs @@ -897,3 +897,33 @@ stack( x? ~ ~ x ~ x@3\`), roots.struct("x [~ x?0.2] x [~ x?] | x!4 | x@2 ~ ~ ~ x x x").transpose("0 7") ).slow(2).pianoroll().note().piano().out();`; + +export const chop = `samples({ p: 'https://cdn.freesound.org/previews/648/648433_11943129-lq.mp3' }) + +s("p") + .loopAt(32,1) + .chop(128) + .jux(rev) + .shape(.4) + .decay(.1) + .sustain(.6) + .out()`; + +export const delay = `stack( + s("bd ") + .delay("<0 .5>") + .delaytime(".16 | .33") + .delayfeedback(".6 | .8") + ).sometimes(x=>x.speed("-1")).out()`; + +export const orbit = `stack( + s("bd ") + .delay(.5) + .delaytime(.33) + .delayfeedback(.6), + s("hh*2") + .delay(.8) + .delaytime(.08) + .delayfeedback(.7) + .orbit(2) + ).sometimes(x=>x.speed("-1")).out()`; From c5342d3eb0ec203383a3c0302c019608e60ad88b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 25 Sep 2022 16:40:50 +0200 Subject: [PATCH 3/4] snaps --- .../test/__snapshots__/tunes.test.mjs.snap | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/repl/src/test/__snapshots__/tunes.test.mjs.snap b/repl/src/test/__snapshots__/tunes.test.mjs.snap index dba92c73..9092749e 100644 --- a/repl/src/test/__snapshots__/tunes.test.mjs.snap +++ b/repl/src/test/__snapshots__/tunes.test.mjs.snap @@ -1743,6 +1743,19 @@ exports[`renders tunes > tune: caverave 1`] = ` ] `; +exports[`renders tunes > tune: chop 1`] = ` +[ + "0/1 -> 1/4: {\\"s\\":\\"p\\",\\"speed\\":0.03125,\\"unit\\":\\"c\\",\\"begin\\":0,\\"end\\":0.0078125,\\"pan\\":0,\\"shape\\":0.4,\\"decay\\":0.1,\\"sustain\\":0.6}", + "1/4 -> 1/2: {\\"s\\":\\"p\\",\\"speed\\":0.03125,\\"unit\\":\\"c\\",\\"begin\\":0.0078125,\\"end\\":0.015625,\\"pan\\":0,\\"shape\\":0.4,\\"decay\\":0.1,\\"sustain\\":0.6}", + "1/2 -> 3/4: {\\"s\\":\\"p\\",\\"speed\\":0.03125,\\"unit\\":\\"c\\",\\"begin\\":0.015625,\\"end\\":0.0234375,\\"pan\\":0,\\"shape\\":0.4,\\"decay\\":0.1,\\"sustain\\":0.6}", + "3/4 -> 1/1: {\\"s\\":\\"p\\",\\"speed\\":0.03125,\\"unit\\":\\"c\\",\\"begin\\":0.0234375,\\"end\\":0.03125,\\"pan\\":0,\\"shape\\":0.4,\\"decay\\":0.1,\\"sustain\\":0.6}", + "3/4 -> 1/1: {\\"s\\":\\"p\\",\\"speed\\":0.03125,\\"unit\\":\\"c\\",\\"begin\\":0,\\"end\\":0.0078125,\\"pan\\":1,\\"shape\\":0.4,\\"decay\\":0.1,\\"sustain\\":0.6}", + "1/2 -> 3/4: {\\"s\\":\\"p\\",\\"speed\\":0.03125,\\"unit\\":\\"c\\",\\"begin\\":0.0078125,\\"end\\":0.015625,\\"pan\\":1,\\"shape\\":0.4,\\"decay\\":0.1,\\"sustain\\":0.6}", + "1/4 -> 1/2: {\\"s\\":\\"p\\",\\"speed\\":0.03125,\\"unit\\":\\"c\\",\\"begin\\":0.015625,\\"end\\":0.0234375,\\"pan\\":1,\\"shape\\":0.4,\\"decay\\":0.1,\\"sustain\\":0.6}", + "0/1 -> 1/4: {\\"s\\":\\"p\\",\\"speed\\":0.03125,\\"unit\\":\\"c\\",\\"begin\\":0.0234375,\\"end\\":0.03125,\\"pan\\":1,\\"shape\\":0.4,\\"decay\\":0.1,\\"sustain\\":0.6}", +] +`; + exports[`renders tunes > tune: customTrigger 1`] = ` [ "0/1 -> 1/8: {\\"freq\\":55.33,\\"s\\":\\"sawtooth\\"}", @@ -1771,6 +1784,13 @@ exports[`renders tunes > tune: customTrigger 1`] = ` ] `; +exports[`renders tunes > tune: delay 1`] = ` +[ + "0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"delay\\":0,\\"delaytime\\":0.16,\\"delayfeedback\\":0.8,\\"speed\\":-1}", + "1/2 -> 1/1: {\\"s\\":\\"sd\\",\\"delay\\":0,\\"delaytime\\":0.16,\\"delayfeedback\\":0.8,\\"speed\\":-1}", +] +`; + exports[`renders tunes > tune: echoPiano 1`] = ` [ "0/1 -> 1/2: {\\"note\\":\\"D3\\",\\"clip\\":1,\\"s\\":\\"piano\\",\\"release\\":0.1,\\"pan\\":0.4814814814814815}", @@ -13380,6 +13400,15 @@ exports[`renders tunes > tune: meltingsubmarine 1`] = ` ] `; +exports[`renders tunes > tune: orbit 1`] = ` +[ + "0/1 -> 1/2: {\\"s\\":\\"bd\\",\\"delay\\":0.5,\\"delaytime\\":0.33,\\"delayfeedback\\":0.6,\\"speed\\":-1}", + "1/2 -> 1/1: {\\"s\\":\\"sd\\",\\"delay\\":0.5,\\"delaytime\\":0.33,\\"delayfeedback\\":0.6,\\"speed\\":-1}", + "0/1 -> 1/2: {\\"s\\":\\"hh\\",\\"delay\\":0.8,\\"delaytime\\":0.08,\\"delayfeedback\\":0.7,\\"orbit\\":2,\\"speed\\":-1}", + "1/2 -> 1/1: {\\"s\\":\\"hh\\",\\"delay\\":0.8,\\"delaytime\\":0.08,\\"delayfeedback\\":0.7,\\"orbit\\":2,\\"speed\\":-1}", +] +`; + exports[`renders tunes > tune: outroMusic 1`] = ` [ "0/1 -> 3/1: {\\"n\\":\\"B3\\",\\"s\\":\\"0040_FluidR3_GM_sf2_file\\",\\"attack\\":0.05,\\"decay\\":0.1,\\"sustain\\":0.7,\\"cutoff\\":1111.7252990603447,\\"gain\\":0.3}", From 5018669ded708387e3f4787971ee18f33b09f3e5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 25 Sep 2022 20:08:02 +0200 Subject: [PATCH 4/4] roomsize --- packages/core/controls.mjs | 5 +++++ packages/webaudio/reverb.mjs | 3 ++- packages/webaudio/webaudio.mjs | 8 +++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index c5c19644..6e34c3d0 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -581,6 +581,11 @@ const generic_params = [ 'size', 'a pattern of numbers from 0 to 1. Sets the perceptual size (reverb time) of the `room` to be used in reverb.', ], + [ + 'f', + 'roomsize', + 'a pattern of numbers from 0 to 1. Sets the perceptual size (reverb time) of the `room` to be used in reverb.', + ], // ['f', 'sagogo', ''], // ['f', 'sclap', ''], // ['f', 'sclaves', ''], diff --git a/packages/webaudio/reverb.mjs b/packages/webaudio/reverb.mjs index 4757932d..10e6dcd1 100644 --- a/packages/webaudio/reverb.mjs +++ b/packages/webaudio/reverb.mjs @@ -11,8 +11,9 @@ if (typeof AudioContext !== 'undefined') { const convolver = this.createConvolver(); convolver.setDuration = (d) => { convolver.buffer = this.impulseResponse(d); + convolver.duration = duration; + return convolver; }; - this.duration = duration; convolver.setDuration(duration); return convolver; }; diff --git a/packages/webaudio/webaudio.mjs b/packages/webaudio/webaudio.mjs index 2e7de7be..41c6f748 100644 --- a/packages/webaudio/webaudio.mjs +++ b/packages/webaudio/webaudio.mjs @@ -217,7 +217,8 @@ function getReverb(orbit, duration = 2) { reverbs[orbit] = reverb; } if (reverbs[orbit].duration !== duration) { - reverbs[orbit].setDuration(duration); + reverbs[orbit] = reverbs[orbit].setDuration(duration); + reverbs[orbit].duration = duration; } return reverbs[orbit]; } @@ -277,6 +278,7 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => { orbit = 1, room, size = 2, + roomsize = size, } = hap.value; const { velocity = 1 } = hap.context; gain *= velocity; // legacy fix for velocity @@ -405,8 +407,8 @@ export const webaudioOutput = async (hap, deadline, hapDuration) => { } // reverb let reverbSend; - if (room > 0 && size > 0) { - const reverbNode = getReverb(orbit, size); + if (room > 0 && roomsize > 0) { + const reverbNode = getReverb(orbit, roomsize); reverbSend = effectSend(post, reverbNode, room); }