From 0ad0046a769db952c8e68f097a730374e2b94a1e Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sat, 30 Sep 2023 22:04:22 +0200 Subject: [PATCH 01/14] Replacing old reverb by better convolution --- packages/superdough/reverb.mjs | 34 ++--- packages/superdough/reverbGen.mjs | 209 +++++++++++++++++++++++++++++ packages/superdough/superdough.mjs | 9 +- 3 files changed, 236 insertions(+), 16 deletions(-) create mode 100644 packages/superdough/reverbGen.mjs diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index e6d31f6aa..4d5f655f4 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -1,23 +1,27 @@ -if (typeof AudioContext !== 'undefined') { - AudioContext.prototype.impulseResponse = function (duration, channels = 1) { - const length = this.sampleRate * duration; - const impulse = this.createBuffer(channels, 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; - }; +import reverbGen from './reverbGen.mjs'; - AudioContext.prototype.createReverb = function (duration) { +if (typeof AudioContext !== 'undefined') { + AudioContext.prototype.generateReverb = reverbGen.generateReverb; + AudioContext.prototype.createReverb = function(duration, audioContext) { const convolver = this.createConvolver(); convolver.setDuration = (d) => { - convolver.buffer = this.impulseResponse(d); - convolver.duration = duration; - return convolver; + this.generateReverb( + { + audioContext, + sampleRate: 44100, + numChannels: 2, + decayTime: d, + fadeInTime: d, + lpFreqStart: 2000, + lpFreqEnd: 15000, + }, + (buffer) => { + convolver.buffer = buffer; + } + ); + convolver.duration = d; }; convolver.setDuration(duration); return convolver; }; } - -// TODO: make the reverb more exciting -// check out https://blog.gskinner.com/archives/2019/02/reverb-web-audio-api.html diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs new file mode 100644 index 000000000..1d05ee823 --- /dev/null +++ b/packages/superdough/reverbGen.mjs @@ -0,0 +1,209 @@ +// Copyright 2014 Alan deLespinasse +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +"use strict"; + + +var reverbGen = {}; + +/** Generates a reverb impulse response. + + @param {!Object} params TODO: Document the properties. + @param {!function(!AudioBuffer)} callback Function to call when + the impulse response has been generated. The impulse response + is passed to this function as its parameter. May be called + immediately within the current execution context, or later. */ +reverbGen.generateReverb = function(params, callback) { + var audioContext = params.audioContext || new AudioContext(); + var sampleRate = params.sampleRate || 44100; + var numChannels = params.numChannels || 2; + // params.decayTime is the -60dB fade time. We let it go 50% longer to get to -90dB. + var totalTime = params.decayTime * 1.5; + var decaySampleFrames = Math.round(params.decayTime * sampleRate); + var numSampleFrames = Math.round(totalTime * sampleRate); + var fadeInSampleFrames = Math.round((params.fadeInTime || 0) * sampleRate); + // 60dB is a factor of 1 million in power, or 1000 in amplitude. + var decayBase = Math.pow(1 / 1000, 1 / decaySampleFrames); + var reverbIR = audioContext.createBuffer(numChannels, numSampleFrames, sampleRate); + for (var i = 0; i < numChannels; i++) { + var chan = reverbIR.getChannelData(i); + for (var j = 0; j < numSampleFrames; j++) { + chan[j] = randomSample() * Math.pow(decayBase, j); + } + for (var j = 0; j < fadeInSampleFrames; j++) { + chan[j] *= (j / fadeInSampleFrames); + } + } + + applyGradualLowpass(reverbIR, params.lpFreqStart || 0, params.lpFreqEnd || 0, params.decayTime, callback); +}; + +/** Creates a canvas element showing a graph of the given data. + + @param {!Float32Array} data An array of numbers, or a Float32Array. + @param {number} width Width in pixels of the canvas. + @param {number} height Height in pixels of the canvas. + @param {number} min Minimum value of data for the graph (lower edge). + @param {number} max Maximum value of data in the graph (upper edge). + @return {!CanvasElement} The generated canvas element. */ +reverbGen.generateGraph = function(data, width, height, min, max) { + var canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + var gc = canvas.getContext('2d'); + gc.fillStyle = '#000'; + gc.fillRect(0, 0, canvas.width, canvas.height); + gc.fillStyle = '#fff'; + var xscale = width / data.length; + var yscale = height / (max - min); + for (var i = 0; i < data.length; i++) { + gc.fillRect(i * xscale, height - (data[i] - min) * yscale, 1, 1); + } + return canvas; +} + +/** Saves an AudioBuffer as a 16-bit WAV file on the client's host + file system. Normalizes it to peak at +-32767, and optionally + truncates it if there's a lot of "silence" at the end. + + @param {!AudioBuffer} buffer The buffer to save. + @param {string} name Name of file to create. + @param {number?} opt_minTail Defines what counts as "silence" for + auto-truncating the buffer. If there is a point past which every + value of every channel is less than opt_minTail, then the buffer + is truncated at that point. This is expressed as an integer, + applying to the post-normalized and integer-converted + buffer. The default is 0, meaning don't truncate. */ +reverbGen.saveWavFile = function(buffer, name, opt_minTail) { + var bitsPerSample = 16; + var bytesPerSample = 2; + var sampleRate = buffer.sampleRate; + var numChannels = buffer.numberOfChannels; + var channels = getAllChannelData(buffer); + var numSampleFrames = channels[0].length; + var scale = 32767; + // Find normalization constant. + var max = 0; + for (var i = 0; i < numChannels; i++) { + for (var j = 0; j < numSampleFrames; j++) { + max = Math.max(max, Math.abs(channels[i][j])); + } + } + if (max) { + scale = 32767 / max; + } + // Find truncation point. + if (opt_minTail) { + var truncateAt = 0; + for (var i = 0; i < numChannels; i++) { + for (var j = 0; j < numSampleFrames; j++) { + var absSample = Math.abs(Math.round(scale * channels[i][j])); + if (absSample > opt_minTail) { + truncateAt = j; + } + } + } + numSampleFrames = truncateAt + 1; + } + var sampleDataBytes = bytesPerSample * numChannels * numSampleFrames; + var fileBytes = sampleDataBytes + 44; + var arrayBuffer = new ArrayBuffer(fileBytes); + var dataView = new DataView(arrayBuffer); + dataView.setUint32(0, 1179011410, true); // "RIFF" + dataView.setUint32(4, fileBytes - 8, true); // file length + dataView.setUint32(8, 1163280727, true); // "WAVE" + dataView.setUint32(12, 544501094, true); // "fmt " + dataView.setUint32(16, 16, true) // fmt chunk length + dataView.setUint16(20, 1, true); // PCM format + dataView.setUint16(22, numChannels, true); // NumChannels + dataView.setUint32(24, sampleRate, true); // SampleRate + var bytesPerSampleFrame = numChannels * bytesPerSample; + dataView.setUint32(28, sampleRate * bytesPerSampleFrame, true); // ByteRate + dataView.setUint16(32, bytesPerSampleFrame, true); // BlockAlign + dataView.setUint16(34, bitsPerSample, true); // BitsPerSample + dataView.setUint32(36, 1635017060, true); // "data" + dataView.setUint32(40, sampleDataBytes, true); + for (var j = 0; j < numSampleFrames; j++) { + for (var i = 0; i < numChannels; i++) { + dataView.setInt16(44 + j * bytesPerSampleFrame + i * bytesPerSample, + Math.round(scale * channels[i][j]), true); + } + } + var blob = new Blob([arrayBuffer], { 'type': 'audio/wav' }); + var url = window.URL.createObjectURL(blob); + var linkEl = document.createElement('a'); + linkEl.href = url; + linkEl.download = name; + linkEl.style.display = 'none'; + document.body.appendChild(linkEl); + linkEl.click(); +}; + +/** Applies a constantly changing lowpass filter to the given sound. + + @private + @param {!AudioBuffer} input + @param {number} lpFreqStart + @param {number} lpFreqEnd + @param {number} lpFreqEndAt + @param {!function(!AudioBuffer)} callback May be called + immediately within the current execution context, or later.*/ +var applyGradualLowpass = function(input, lpFreqStart, lpFreqEnd, lpFreqEndAt, callback) { + if (lpFreqStart == 0) { + callback(input); + return; + } + var channelData = getAllChannelData(input); + var context = new OfflineAudioContext(input.numberOfChannels, channelData[0].length, input.sampleRate); + var player = context.createBufferSource(); + player.buffer = input; + var filter = context.createBiquadFilter(); + + lpFreqStart = Math.min(lpFreqStart, input.sampleRate / 2); + lpFreqEnd = Math.min(lpFreqEnd, input.sampleRate / 2); + + filter.type = "lowpass"; + filter.Q.value = 0.0001; + filter.frequency.setValueAtTime(lpFreqStart, 0); + filter.frequency.linearRampToValueAtTime(lpFreqEnd, lpFreqEndAt); + + player.connect(filter); + filter.connect(context.destination); + player.start(); + context.oncomplete = function(event) { + callback(event.renderedBuffer); + }; + context.startRendering(); + + window.filterNode = filter; +}; + +/** @private + @param {!AudioBuffer} buffer + @return {!Array.} An array containing the Float32Array of each channel's samples. */ +var getAllChannelData = function(buffer) { + var channels = []; + for (var i = 0; i < buffer.numberOfChannels; i++) { + channels[i] = buffer.getChannelData(i); + } + return channels; +}; + +/** @private + @return {number} A random number from -1 to 1. */ +var randomSample = function() { + return Math.random() * 2 - 1; +}; + +export default reverbGen; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 289e8d97a..9010c85db 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -107,17 +107,24 @@ function getDelay(orbit, delaytime, delayfeedback, t) { } let reverbs = {}; + function getReverb(orbit, duration = 2) { + + // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb(duration); + const reverb = ac.createReverb(duration, getAudioContext()); reverb.connect(getDestination()); + console.log(reverb) reverbs[orbit] = reverb; } + + // Update the reverb duration if needed after instanciation if (reverbs[orbit].duration !== duration) { reverbs[orbit] = reverbs[orbit].setDuration(duration); reverbs[orbit].duration = duration; } + return reverbs[orbit]; } From abff27970746fecb4620d54a272abc5b67972d0c Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 1 Oct 2023 11:52:24 +0200 Subject: [PATCH 02/14] Document reverb controls --- packages/core/controls.mjs | 39 ++++++++++++++++++++++++++++-- packages/superdough/reverb.mjs | 14 ++++++++--- packages/superdough/superdough.mjs | 11 ++++++++- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 6cac6e540..216b70012 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -970,6 +970,41 @@ const generic_params = [ * */ [['room', 'size']], + /** + * Reverb lowpass starting frequency (in hertz). + * + * @name revlp + * @param {number} level between 0 and 20000hz + * @example + * s("bd sd").room(0.5).revlp(10000) + * @example + * s("bd sd").room(0.5).revlp(5000) + */ + ['revlp'], + /** + * Reverb lowpass frequency at -60dB (in hertz). + * + * @name revdim + * @param {number} level between 0 and 20000hz + * @example + * s("bd sd").room(0.5).revlp(10000).revdim(8000) + * @example + * s("bd sd").room(0.5).revlp(5000).revdim(400) + * + */ + ['revdim'], + /** + * Reverb fade time (in seconds). + * + * @name fade + * @param {number} seconds for the reverb to fade + * @example + * s("bd sd").room(0.5).revlp(10000).fade(0.5) + * @example + * s("bd sd").room(0.5).revlp(5000).fade(4) + * + */ + ['fade'], /** * Sets the room size of the reverb, see {@link room}. * @@ -1162,7 +1197,7 @@ const generic_params = [ ]; // TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 -controls.createParam = function (names) { +controls.createParam = function(names) { const name = Array.isArray(names) ? names[0] : names; var withVal; @@ -1186,7 +1221,7 @@ controls.createParam = function (names) { const func = (...pats) => sequence(...pats).withValue(withVal); - const setter = function (...pats) { + const setter = function(...pats) { if (!pats.length) { return this.fmap(withVal); } diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 4d5f655f4..505d0ac27 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -2,7 +2,13 @@ import reverbGen from './reverbGen.mjs'; if (typeof AudioContext !== 'undefined') { AudioContext.prototype.generateReverb = reverbGen.generateReverb; - AudioContext.prototype.createReverb = function(duration, audioContext) { + AudioContext.prototype.createReverb = function( + duration, + audioContext, + fade, + revlp, + revdim + ) { const convolver = this.createConvolver(); convolver.setDuration = (d) => { this.generateReverb( @@ -11,9 +17,9 @@ if (typeof AudioContext !== 'undefined') { sampleRate: 44100, numChannels: 2, decayTime: d, - fadeInTime: d, - lpFreqStart: 2000, - lpFreqEnd: 15000, + fadeInTime: fade, + lpFreqStart: revlp, + lpFreqEnd: revdim, }, (buffer) => { convolver.buffer = buffer; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 9010c85db..33c108f2b 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -113,7 +113,13 @@ function getReverb(orbit, duration = 2) { // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb(duration, getAudioContext()); + const reverb = ac.createReverb( + duration, + getAudioContext(), + fade, + revlp, + revdim, + ); reverb.connect(getDestination()); console.log(reverb) reverbs[orbit] = reverb; @@ -222,6 +228,9 @@ export const superdough = async (value, deadline, hapDuration) => { delaytime = 0.25, orbit = 1, room, + fade = 0.1, + revlp = 15000, + revdim = 1000, size = 2, velocity = 1, analyze, // analyser wet From e600b91a8569685444bb1f719f9c6ed4b8910b9b Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 1 Oct 2023 12:02:17 +0200 Subject: [PATCH 03/14] bugfixes for parameter passing --- packages/superdough/reverb.mjs | 4 ++-- packages/superdough/superdough.mjs | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 505d0ac27..4c5bc1d18 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -3,14 +3,14 @@ import reverbGen from './reverbGen.mjs'; if (typeof AudioContext !== 'undefined') { AudioContext.prototype.generateReverb = reverbGen.generateReverb; AudioContext.prototype.createReverb = function( - duration, audioContext, + duration, fade, revlp, revdim ) { const convolver = this.createConvolver(); - convolver.setDuration = (d) => { + convolver.setDuration = (d, fade, revlp, revdim) => { this.generateReverb( { audioContext, diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 33c108f2b..607c649de 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -108,29 +108,27 @@ function getDelay(orbit, delaytime, delayfeedback, t) { let reverbs = {}; -function getReverb(orbit, duration = 2) { +function getReverb(orbit, duration = 2, fade, revlp, revdim) { // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); const reverb = ac.createReverb( - duration, getAudioContext(), + duration, fade, revlp, - revdim, ); reverb.connect(getDestination()); console.log(reverb) reverbs[orbit] = reverb; } - // Update the reverb duration if needed after instanciation if (reverbs[orbit].duration !== duration) { - reverbs[orbit] = reverbs[orbit].setDuration(duration); + reverbs[orbit] = reverbs[orbit].setDuration( + duration, fade, revlp, revdim); reverbs[orbit].duration = duration; } - return reverbs[orbit]; } @@ -370,7 +368,7 @@ export const superdough = async (value, deadline, hapDuration) => { // reverb let reverbSend; if (room > 0 && size > 0) { - const reverbNode = getReverb(orbit, size); + const reverbNode = getReverb(orbit, size, fade, revlp, revdim); reverbSend = effectSend(post, reverbNode, room); } From 2d07eeb518e9b200bae4e38b818808ef2a388849 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 1 Oct 2023 14:44:34 +0200 Subject: [PATCH 04/14] Connecting all parameters to convolution generator --- packages/superdough/reverb.mjs | 5 ++++- packages/superdough/superdough.mjs | 20 ++++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 4c5bc1d18..54019fc2a 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -26,8 +26,11 @@ if (typeof AudioContext !== 'undefined') { } ); convolver.duration = d; + convolver.fade = fade; + convolver.revlp = revlp; + convolver.revdim = revdim; }; - convolver.setDuration(duration); + convolver.setDuration(duration, fade, revlp, revdim); return convolver; }; } diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 607c649de..0d08c8ad2 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -118,17 +118,29 @@ function getReverb(orbit, duration = 2, fade, revlp, revdim) { duration, fade, revlp, + revdim, ); reverb.connect(getDestination()); - console.log(reverb) reverbs[orbit] = reverb; + console.log(reverbs[orbit]); } - // Update the reverb duration if needed after instanciation - if (reverbs[orbit].duration !== duration) { + + if ( + reverbs[orbit].duration !== duration || + reverbs[orbit].fade !== fade || + reverbs[orbit].revlp !== revlp || + reverbs[orbit].revdim !== revdim + ) { reverbs[orbit] = reverbs[orbit].setDuration( - duration, fade, revlp, revdim); + duration, fade, revlp, revdim + ); reverbs[orbit].duration = duration; + reverbs[orbit].fade = fade; + reverbs[orbit].revlp = revlp; + reverbs[orbit].revdim = revdim; + } + return reverbs[orbit]; } From 1909caf769d83e89fbc6ace74b464f25179fe20d Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Sun, 1 Oct 2023 14:50:29 +0200 Subject: [PATCH 05/14] Connecting new reverb documentation --- website/src/pages/learn/effects.mdx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index f77ab4c47..062a1947a 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -203,4 +203,16 @@ global effects use the same chain for all events of the same orbit: +## fade + + + +## revlp + + + +## revdim + + + Next, we'll look at strudel's support for [Csound](/learn/csound). From 6ca99e33aba268442e519760e1488d9e87460ab5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 23:20:05 +0200 Subject: [PATCH 06/14] codeformat --- packages/core/controls.mjs | 4 +-- packages/superdough/reverb.mjs | 10 ++------ packages/superdough/reverbGen.mjs | 40 ++++++++++++++---------------- packages/superdough/superdough.mjs | 15 ++--------- 4 files changed, 24 insertions(+), 45 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 216b70012..e72f006db 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1197,7 +1197,7 @@ const generic_params = [ ]; // TODO: slice / splice https://www.youtube.com/watch?v=hKhPdO0RKDQ&list=PL2lW1zNIIwj3bDkh-Y3LUGDuRcoUigoDs&index=13 -controls.createParam = function(names) { +controls.createParam = function (names) { const name = Array.isArray(names) ? names[0] : names; var withVal; @@ -1221,7 +1221,7 @@ controls.createParam = function(names) { const func = (...pats) => sequence(...pats).withValue(withVal); - const setter = function(...pats) { + const setter = function (...pats) { if (!pats.length) { return this.fmap(withVal); } diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 54019fc2a..7151e7fc3 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -2,13 +2,7 @@ import reverbGen from './reverbGen.mjs'; if (typeof AudioContext !== 'undefined') { AudioContext.prototype.generateReverb = reverbGen.generateReverb; - AudioContext.prototype.createReverb = function( - audioContext, - duration, - fade, - revlp, - revdim - ) { + AudioContext.prototype.createReverb = function (audioContext, duration, fade, revlp, revdim) { const convolver = this.createConvolver(); convolver.setDuration = (d, fade, revlp, revdim) => { this.generateReverb( @@ -23,7 +17,7 @@ if (typeof AudioContext !== 'undefined') { }, (buffer) => { convolver.buffer = buffer; - } + }, ); convolver.duration = d; convolver.fade = fade; diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs index 1d05ee823..cac5d24a1 100644 --- a/packages/superdough/reverbGen.mjs +++ b/packages/superdough/reverbGen.mjs @@ -12,9 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -"use strict"; - - var reverbGen = {}; /** Generates a reverb impulse response. @@ -24,7 +21,7 @@ var reverbGen = {}; the impulse response has been generated. The impulse response is passed to this function as its parameter. May be called immediately within the current execution context, or later. */ -reverbGen.generateReverb = function(params, callback) { +reverbGen.generateReverb = function (params, callback) { var audioContext = params.audioContext || new AudioContext(); var sampleRate = params.sampleRate || 44100; var numChannels = params.numChannels || 2; @@ -42,7 +39,7 @@ reverbGen.generateReverb = function(params, callback) { chan[j] = randomSample() * Math.pow(decayBase, j); } for (var j = 0; j < fadeInSampleFrames; j++) { - chan[j] *= (j / fadeInSampleFrames); + chan[j] *= j / fadeInSampleFrames; } } @@ -57,7 +54,7 @@ reverbGen.generateReverb = function(params, callback) { @param {number} min Minimum value of data for the graph (lower edge). @param {number} max Maximum value of data in the graph (upper edge). @return {!CanvasElement} The generated canvas element. */ -reverbGen.generateGraph = function(data, width, height, min, max) { +reverbGen.generateGraph = function (data, width, height, min, max) { var canvas = document.createElement('canvas'); canvas.width = width; canvas.height = height; @@ -71,7 +68,7 @@ reverbGen.generateGraph = function(data, width, height, min, max) { gc.fillRect(i * xscale, height - (data[i] - min) * yscale, 1, 1); } return canvas; -} +}; /** Saves an AudioBuffer as a 16-bit WAV file on the client's host file system. Normalizes it to peak at +-32767, and optionally @@ -85,7 +82,7 @@ reverbGen.generateGraph = function(data, width, height, min, max) { is truncated at that point. This is expressed as an integer, applying to the post-normalized and integer-converted buffer. The default is 0, meaning don't truncate. */ -reverbGen.saveWavFile = function(buffer, name, opt_minTail) { +reverbGen.saveWavFile = function (buffer, name, opt_minTail) { var bitsPerSample = 16; var bytesPerSample = 2; var sampleRate = buffer.sampleRate; @@ -124,23 +121,22 @@ reverbGen.saveWavFile = function(buffer, name, opt_minTail) { dataView.setUint32(4, fileBytes - 8, true); // file length dataView.setUint32(8, 1163280727, true); // "WAVE" dataView.setUint32(12, 544501094, true); // "fmt " - dataView.setUint32(16, 16, true) // fmt chunk length - dataView.setUint16(20, 1, true); // PCM format + dataView.setUint32(16, 16, true); // fmt chunk length + dataView.setUint16(20, 1, true); // PCM format dataView.setUint16(22, numChannels, true); // NumChannels - dataView.setUint32(24, sampleRate, true); // SampleRate + dataView.setUint32(24, sampleRate, true); // SampleRate var bytesPerSampleFrame = numChannels * bytesPerSample; dataView.setUint32(28, sampleRate * bytesPerSampleFrame, true); // ByteRate - dataView.setUint16(32, bytesPerSampleFrame, true); // BlockAlign - dataView.setUint16(34, bitsPerSample, true); // BitsPerSample - dataView.setUint32(36, 1635017060, true); // "data" + dataView.setUint16(32, bytesPerSampleFrame, true); // BlockAlign + dataView.setUint16(34, bitsPerSample, true); // BitsPerSample + dataView.setUint32(36, 1635017060, true); // "data" dataView.setUint32(40, sampleDataBytes, true); for (var j = 0; j < numSampleFrames; j++) { for (var i = 0; i < numChannels; i++) { - dataView.setInt16(44 + j * bytesPerSampleFrame + i * bytesPerSample, - Math.round(scale * channels[i][j]), true); + dataView.setInt16(44 + j * bytesPerSampleFrame + i * bytesPerSample, Math.round(scale * channels[i][j]), true); } } - var blob = new Blob([arrayBuffer], { 'type': 'audio/wav' }); + var blob = new Blob([arrayBuffer], { type: 'audio/wav' }); var url = window.URL.createObjectURL(blob); var linkEl = document.createElement('a'); linkEl.href = url; @@ -159,7 +155,7 @@ reverbGen.saveWavFile = function(buffer, name, opt_minTail) { @param {number} lpFreqEndAt @param {!function(!AudioBuffer)} callback May be called immediately within the current execution context, or later.*/ -var applyGradualLowpass = function(input, lpFreqStart, lpFreqEnd, lpFreqEndAt, callback) { +var applyGradualLowpass = function (input, lpFreqStart, lpFreqEnd, lpFreqEndAt, callback) { if (lpFreqStart == 0) { callback(input); return; @@ -173,7 +169,7 @@ var applyGradualLowpass = function(input, lpFreqStart, lpFreqEnd, lpFreqEndAt, c lpFreqStart = Math.min(lpFreqStart, input.sampleRate / 2); lpFreqEnd = Math.min(lpFreqEnd, input.sampleRate / 2); - filter.type = "lowpass"; + filter.type = 'lowpass'; filter.Q.value = 0.0001; filter.frequency.setValueAtTime(lpFreqStart, 0); filter.frequency.linearRampToValueAtTime(lpFreqEnd, lpFreqEndAt); @@ -181,7 +177,7 @@ var applyGradualLowpass = function(input, lpFreqStart, lpFreqEnd, lpFreqEndAt, c player.connect(filter); filter.connect(context.destination); player.start(); - context.oncomplete = function(event) { + context.oncomplete = function (event) { callback(event.renderedBuffer); }; context.startRendering(); @@ -192,7 +188,7 @@ var applyGradualLowpass = function(input, lpFreqStart, lpFreqEnd, lpFreqEndAt, c /** @private @param {!AudioBuffer} buffer @return {!Array.} An array containing the Float32Array of each channel's samples. */ -var getAllChannelData = function(buffer) { +var getAllChannelData = function (buffer) { var channels = []; for (var i = 0; i < buffer.numberOfChannels; i++) { channels[i] = buffer.getChannelData(i); @@ -202,7 +198,7 @@ var getAllChannelData = function(buffer) { /** @private @return {number} A random number from -1 to 1. */ -var randomSample = function() { +var randomSample = function () { return Math.random() * 2 - 1; }; diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 0d08c8ad2..39e05e79a 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -109,20 +109,12 @@ function getDelay(orbit, delaytime, delayfeedback, t) { let reverbs = {}; function getReverb(orbit, duration = 2, fade, revlp, revdim) { - // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb( - getAudioContext(), - duration, - fade, - revlp, - revdim, - ); + const reverb = ac.createReverb(getAudioContext(), duration, fade, revlp, revdim); reverb.connect(getDestination()); reverbs[orbit] = reverb; - console.log(reverbs[orbit]); } if ( @@ -131,14 +123,11 @@ function getReverb(orbit, duration = 2, fade, revlp, revdim) { reverbs[orbit].revlp !== revlp || reverbs[orbit].revdim !== revdim ) { - reverbs[orbit] = reverbs[orbit].setDuration( - duration, fade, revlp, revdim - ); + reverbs[orbit] = reverbs[orbit].setDuration(duration, fade, revlp, revdim); reverbs[orbit].duration = duration; reverbs[orbit].fade = fade; reverbs[orbit].revlp = revlp; reverbs[orbit].revdim = revdim; - } return reverbs[orbit]; From 275796c241f3740e71bc1366e501a4855d7cbc8d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 23:21:02 +0200 Subject: [PATCH 07/14] fix: reverbs[orbit] is undefined --- packages/superdough/superdough.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 39e05e79a..28f4c261e 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -123,7 +123,7 @@ function getReverb(orbit, duration = 2, fade, revlp, revdim) { reverbs[orbit].revlp !== revlp || reverbs[orbit].revdim !== revdim ) { - reverbs[orbit] = reverbs[orbit].setDuration(duration, fade, revlp, revdim); + reverbs[orbit].setDuration(duration, fade, revlp, revdim); reverbs[orbit].duration = duration; reverbs[orbit].fade = fade; reverbs[orbit].revlp = revlp; From dfdd9e02ca43f7117fe995cd9baba57ed7bed561 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sun, 1 Oct 2023 23:22:32 +0200 Subject: [PATCH 08/14] eslint ignore reverbGen + snapshot --- .eslintignore | 3 +- test/__snapshots__/examples.test.mjs.snap | 78 +++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/.eslintignore b/.eslintignore index 13e635f3f..58d3643dc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -18,4 +18,5 @@ vite.config.js **/*.json **/dev-dist **/dist -/src-tauri/target/**/* \ No newline at end of file +/src-tauri/target/**/* +reverbGen.mjs \ No newline at end of file diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index e026f9c43..616fff125 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1773,6 +1773,32 @@ exports[`runs examples > example "every" example index 0 1`] = ` ] `; +exports[`runs examples > example "fade" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", +] +`; + +exports[`runs examples > example "fade" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 fade:4 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 fade:4 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 fade:4 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 fade:4 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 fade:4 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 fade:4 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 fade:4 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 fade:4 ]", +] +`; + exports[`runs examples > example "fast" example index 0 1`] = ` [ "[ 0/1 → 1/4 | s:bd ]", @@ -3607,6 +3633,58 @@ exports[`runs examples > example "rev" example index 0 1`] = ` ] `; +exports[`runs examples > example "revdim" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", +] +`; + +exports[`runs examples > example "revdim" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", +] +`; + +exports[`runs examples > example "revlp" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 ]", +] +`; + +exports[`runs examples > example "revlp" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 ]", +] +`; + exports[`runs examples > example "ribbon" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:C3 ]", From a3649148c1ab0975a1a3486a3bd68313c762dbb5 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 08:35:15 +0200 Subject: [PATCH 09/14] more logical naming + update docs --- packages/core/controls.mjs | 43 +++++++++++++++++------------ packages/superdough/superdough.mjs | 12 ++++---- website/src/pages/learn/effects.mdx | 26 +++++++++-------- 3 files changed, 47 insertions(+), 34 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index e72f006db..63ffdaf4e 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -972,53 +972,62 @@ const generic_params = [ [['room', 'size']], /** * Reverb lowpass starting frequency (in hertz). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * - * @name revlp - * @param {number} level between 0 and 20000hz + * @name roomlp + * @synonyms rlp + * @param {number} frequency between 0 and 20000hz * @example - * s("bd sd").room(0.5).revlp(10000) + * s("bd sd").room(0.5).rlp(10000) * @example - * s("bd sd").room(0.5).revlp(5000) + * s("bd sd").room(0.5).rlp(5000) */ - ['revlp'], + ['roomlp', 'rlp'], /** * Reverb lowpass frequency at -60dB (in hertz). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * - * @name revdim - * @param {number} level between 0 and 20000hz + * @name roomdim + * @synonyms rdim + * @param {number} frequency between 0 and 20000hz * @example - * s("bd sd").room(0.5).revlp(10000).revdim(8000) + * s("bd sd").room(0.5).rlp(10000).rdim(8000) * @example - * s("bd sd").room(0.5).revlp(5000).revdim(400) + * s("bd sd").room(0.5).rlp(5000).rdim(400) * */ - ['revdim'], + ['roomdim', 'rdim'], /** * Reverb fade time (in seconds). + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * - * @name fade + * @name roomfade + * @synonyms rfade * @param {number} seconds for the reverb to fade * @example - * s("bd sd").room(0.5).revlp(10000).fade(0.5) + * s("bd sd").room(0.5).rlp(10000).rfade(0.5) * @example - * s("bd sd").room(0.5).revlp(5000).fade(4) + * s("bd sd").room(0.5).rlp(5000).rfade(4) * */ - ['fade'], + ['roomfade', 'rfade'], /** * Sets the room size of the reverb, see {@link room}. + * When this property is changed, the reverb will be recaculated, so only change this sparsely.. * * @name roomsize * @param {number | Pattern} size between 0 and 10 - * @synonyms size, sz + * @synonyms rsize, sz, size * @example - * s("bd sd").room(.8).roomsize("<0 1 2 4 8>") + * s("bd sd").room(.8).rsize(1) + * @example + * s("bd sd").room(.8).rsize(4) * */ // TODO: find out why : // s("bd sd").room(.8).roomsize("<0 .2 .4 .6 .8 [1,0]>").osc() // .. does not work. Is it because room is only one effect? - ['size', 'sz', 'roomsize'], + ['roomsize', 'size', 'sz', 'rsize'], // ['sagogo'], // ['sclap'], // ['sclaves'], diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 28f4c261e..e387ff6ce 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -227,10 +227,10 @@ export const superdough = async (value, deadline, hapDuration) => { delaytime = 0.25, orbit = 1, room, - fade = 0.1, - revlp = 15000, - revdim = 1000, - size = 2, + roomfade = 0.1, + roomlp = 15000, + roomdim = 1000, + roomsize = 2, velocity = 1, analyze, // analyser wet fft = 8, // fftSize 0 - 10 @@ -368,8 +368,8 @@ export const superdough = async (value, deadline, hapDuration) => { } // reverb let reverbSend; - if (room > 0 && size > 0) { - const reverbNode = getReverb(orbit, size, fade, revlp, revdim); + if (room > 0 && roomsize > 0) { + const reverbNode = getReverb(orbit, roomsize, roomfade, roomlp, roomdim); reverbSend = effectSend(post, reverbNode, room); } diff --git a/website/src/pages/learn/effects.mdx b/website/src/pages/learn/effects.mdx index 062a1947a..2ee6c44a2 100644 --- a/website/src/pages/learn/effects.mdx +++ b/website/src/pages/learn/effects.mdx @@ -183,36 +183,40 @@ global effects use the same chain for all events of the same orbit: -## delay +## Delay + +### delay -## delaytime +### delaytime -## delayfeedback +### delayfeedback -## room +## Reverb + +### room -## roomsize +### roomsize -## fade +### roomfade - + -## revlp +### roomlp - + -## revdim +### roomdim - + Next, we'll look at strudel's support for [Csound](/learn/csound). From 904306454349862a7356f355c294e3e89f457842 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Tue, 3 Oct 2023 08:36:52 +0200 Subject: [PATCH 10/14] snapshot --- test/__snapshots__/examples.test.mjs.snap | 185 ++++++++++++---------- 1 file changed, 99 insertions(+), 86 deletions(-) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index 616fff125..99d078ea3 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1773,32 +1773,6 @@ exports[`runs examples > example "every" example index 0 1`] = ` ] `; -exports[`runs examples > example "fade" example index 0 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 fade:0.5 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 fade:0.5 ]", -] -`; - -exports[`runs examples > example "fade" example index 1 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 fade:4 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 fade:4 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 fade:4 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 fade:4 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 fade:4 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 fade:4 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 fade:4 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 fade:4 ]", -] -`; - exports[`runs examples > example "fast" example index 0 1`] = ` [ "[ 0/1 → 1/4 | s:bd ]", @@ -3633,58 +3607,6 @@ exports[`runs examples > example "rev" example index 0 1`] = ` ] `; -exports[`runs examples > example "revdim" example index 0 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 revdim:8000 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 revdim:8000 ]", -] -`; - -exports[`runs examples > example "revdim" example index 1 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 revdim:400 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 revdim:400 ]", -] -`; - -exports[`runs examples > example "revlp" example index 0 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:10000 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:10000 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:10000 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:10000 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:10000 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:10000 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:10000 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:10000 ]", -] -`; - -exports[`runs examples > example "revlp" example index 1 1`] = ` -[ - "[ 0/1 → 1/2 | s:bd room:0.5 revlp:5000 ]", - "[ 1/2 → 1/1 | s:sd room:0.5 revlp:5000 ]", - "[ 1/1 → 3/2 | s:bd room:0.5 revlp:5000 ]", - "[ 3/2 → 2/1 | s:sd room:0.5 revlp:5000 ]", - "[ 2/1 → 5/2 | s:bd room:0.5 revlp:5000 ]", - "[ 5/2 → 3/1 | s:sd room:0.5 revlp:5000 ]", - "[ 3/1 → 7/2 | s:bd room:0.5 revlp:5000 ]", - "[ 7/2 → 4/1 | s:sd room:0.5 revlp:5000 ]", -] -`; - exports[`runs examples > example "ribbon" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:C3 ]", @@ -3732,16 +3654,107 @@ exports[`runs examples > example "room" example index 1 1`] = ` ] `; +exports[`runs examples > example "roomdim" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:10000 roomdim:8000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:10000 roomdim:8000 ]", +] +`; + +exports[`runs examples > example "roomdim" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:5000 roomdim:400 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:5000 roomdim:400 ]", +] +`; + +exports[`runs examples > example "roomfade" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:10000 roomfade:0.5 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:10000 roomfade:0.5 ]", +] +`; + +exports[`runs examples > example "roomfade" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:5000 roomfade:4 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:5000 roomfade:4 ]", +] +`; + +exports[`runs examples > example "roomlp" example index 0 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:10000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:10000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:10000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:10000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:10000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:10000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:10000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:10000 ]", +] +`; + +exports[`runs examples > example "roomlp" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.5 roomlp:5000 ]", + "[ 1/2 → 1/1 | s:sd room:0.5 roomlp:5000 ]", + "[ 1/1 → 3/2 | s:bd room:0.5 roomlp:5000 ]", + "[ 3/2 → 2/1 | s:sd room:0.5 roomlp:5000 ]", + "[ 2/1 → 5/2 | s:bd room:0.5 roomlp:5000 ]", + "[ 5/2 → 3/1 | s:sd room:0.5 roomlp:5000 ]", + "[ 3/1 → 7/2 | s:bd room:0.5 roomlp:5000 ]", + "[ 7/2 → 4/1 | s:sd room:0.5 roomlp:5000 ]", +] +`; + exports[`runs examples > example "roomsize" example index 0 1`] = ` [ - "[ 0/1 → 1/2 | s:bd room:0.8 size:0 ]", - "[ 1/2 → 1/1 | s:sd room:0.8 size:0 ]", - "[ 1/1 → 3/2 | s:bd room:0.8 size:1 ]", - "[ 3/2 → 2/1 | s:sd room:0.8 size:1 ]", - "[ 2/1 → 5/2 | s:bd room:0.8 size:2 ]", - "[ 5/2 → 3/1 | s:sd room:0.8 size:2 ]", - "[ 3/1 → 7/2 | s:bd room:0.8 size:4 ]", - "[ 7/2 → 4/1 | s:sd room:0.8 size:4 ]", + "[ 0/1 → 1/2 | s:bd room:0.8 roomsize:1 ]", + "[ 1/2 → 1/1 | s:sd room:0.8 roomsize:1 ]", + "[ 1/1 → 3/2 | s:bd room:0.8 roomsize:1 ]", + "[ 3/2 → 2/1 | s:sd room:0.8 roomsize:1 ]", + "[ 2/1 → 5/2 | s:bd room:0.8 roomsize:1 ]", + "[ 5/2 → 3/1 | s:sd room:0.8 roomsize:1 ]", + "[ 3/1 → 7/2 | s:bd room:0.8 roomsize:1 ]", + "[ 7/2 → 4/1 | s:sd room:0.8 roomsize:1 ]", +] +`; + +exports[`runs examples > example "roomsize" example index 1 1`] = ` +[ + "[ 0/1 → 1/2 | s:bd room:0.8 roomsize:4 ]", + "[ 1/2 → 1/1 | s:sd room:0.8 roomsize:4 ]", + "[ 1/1 → 3/2 | s:bd room:0.8 roomsize:4 ]", + "[ 3/2 → 2/1 | s:sd room:0.8 roomsize:4 ]", + "[ 2/1 → 5/2 | s:bd room:0.8 roomsize:4 ]", + "[ 5/2 → 3/1 | s:sd room:0.8 roomsize:4 ]", + "[ 3/1 → 7/2 | s:bd room:0.8 roomsize:4 ]", + "[ 7/2 → 4/1 | s:sd room:0.8 roomsize:4 ]", ] `; From 508af7eb72dfd2a56f3bad50355df987e0b75094 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 4 Oct 2023 23:48:53 +0200 Subject: [PATCH 11/14] update internal reverb param names --- packages/superdough/superdough.mjs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index e387ff6ce..1e26cc9d0 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -108,11 +108,11 @@ function getDelay(orbit, delaytime, delayfeedback, t) { let reverbs = {}; -function getReverb(orbit, duration = 2, fade, revlp, revdim) { +function getReverb(orbit, duration = 2, fade, lp, dim) { // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb(getAudioContext(), duration, fade, revlp, revdim); + const reverb = ac.createReverb(getAudioContext(), duration, fade, lp, dim); reverb.connect(getDestination()); reverbs[orbit] = reverb; } @@ -120,14 +120,14 @@ function getReverb(orbit, duration = 2, fade, revlp, revdim) { if ( reverbs[orbit].duration !== duration || reverbs[orbit].fade !== fade || - reverbs[orbit].revlp !== revlp || - reverbs[orbit].revdim !== revdim + reverbs[orbit].lp !== lp || + reverbs[orbit].dim !== dim ) { - reverbs[orbit].setDuration(duration, fade, revlp, revdim); + reverbs[orbit].setDuration(duration, fade, lp, dim); reverbs[orbit].duration = duration; reverbs[orbit].fade = fade; - reverbs[orbit].revlp = revlp; - reverbs[orbit].revdim = revdim; + reverbs[orbit].lp = lp; + reverbs[orbit].dim = dim; } return reverbs[orbit]; From ce842f75612202b916c838a26e1fa7dd58ab3c35 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Wed, 4 Oct 2023 23:56:00 +0200 Subject: [PATCH 12/14] simplify createReverb --- packages/superdough/reverb.mjs | 4 ++-- packages/superdough/superdough.mjs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 7151e7fc3..52d6983c0 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -2,12 +2,12 @@ import reverbGen from './reverbGen.mjs'; if (typeof AudioContext !== 'undefined') { AudioContext.prototype.generateReverb = reverbGen.generateReverb; - AudioContext.prototype.createReverb = function (audioContext, duration, fade, revlp, revdim) { + AudioContext.prototype.createReverb = function (duration, fade, revlp, revdim) { const convolver = this.createConvolver(); convolver.setDuration = (d, fade, revlp, revdim) => { this.generateReverb( { - audioContext, + audioContext: this, sampleRate: 44100, numChannels: 2, decayTime: d, diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 1e26cc9d0..0f2b3dd01 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -112,7 +112,7 @@ function getReverb(orbit, duration = 2, fade, lp, dim) { // If no reverb has been created for a given orbit, create one if (!reverbs[orbit]) { const ac = getAudioContext(); - const reverb = ac.createReverb(getAudioContext(), duration, fade, lp, dim); + const reverb = ac.createReverb(duration, fade, lp, dim); reverb.connect(getDestination()); reverbs[orbit] = reverb; } From 3c4f835d8b6fb8f520c7302bff4b83d5bd2dfe5d Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 5 Oct 2023 00:00:47 +0200 Subject: [PATCH 13/14] consistent naming + simplify --- packages/superdough/reverb.mjs | 14 +++++++------- packages/superdough/superdough.mjs | 6 +----- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/superdough/reverb.mjs b/packages/superdough/reverb.mjs index 52d6983c0..de6c903e0 100644 --- a/packages/superdough/reverb.mjs +++ b/packages/superdough/reverb.mjs @@ -2,9 +2,9 @@ import reverbGen from './reverbGen.mjs'; if (typeof AudioContext !== 'undefined') { AudioContext.prototype.generateReverb = reverbGen.generateReverb; - AudioContext.prototype.createReverb = function (duration, fade, revlp, revdim) { + AudioContext.prototype.createReverb = function (duration, fade, lp, dim) { const convolver = this.createConvolver(); - convolver.setDuration = (d, fade, revlp, revdim) => { + convolver.generate = (d, fade, lp, dim) => { this.generateReverb( { audioContext: this, @@ -12,8 +12,8 @@ if (typeof AudioContext !== 'undefined') { numChannels: 2, decayTime: d, fadeInTime: fade, - lpFreqStart: revlp, - lpFreqEnd: revdim, + lpFreqStart: lp, + lpFreqEnd: dim, }, (buffer) => { convolver.buffer = buffer; @@ -21,10 +21,10 @@ if (typeof AudioContext !== 'undefined') { ); convolver.duration = d; convolver.fade = fade; - convolver.revlp = revlp; - convolver.revdim = revdim; + convolver.lp = lp; + convolver.dim = dim; }; - convolver.setDuration(duration, fade, revlp, revdim); + convolver.generate(duration, fade, lp, dim); return convolver; }; } diff --git a/packages/superdough/superdough.mjs b/packages/superdough/superdough.mjs index 0f2b3dd01..ea8afc58c 100644 --- a/packages/superdough/superdough.mjs +++ b/packages/superdough/superdough.mjs @@ -123,11 +123,7 @@ function getReverb(orbit, duration = 2, fade, lp, dim) { reverbs[orbit].lp !== lp || reverbs[orbit].dim !== dim ) { - reverbs[orbit].setDuration(duration, fade, lp, dim); - reverbs[orbit].duration = duration; - reverbs[orbit].fade = fade; - reverbs[orbit].lp = lp; - reverbs[orbit].dim = dim; + reverbs[orbit].generate(duration, fade, lp, dim); } return reverbs[orbit]; From 75099abbc125908df97ee80a12ede3abda5e1ee3 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 5 Oct 2023 00:03:02 +0200 Subject: [PATCH 14/14] remove unused reverb method --- packages/superdough/reverbGen.mjs | 76 ------------------------------- 1 file changed, 76 deletions(-) diff --git a/packages/superdough/reverbGen.mjs b/packages/superdough/reverbGen.mjs index cac5d24a1..494299372 100644 --- a/packages/superdough/reverbGen.mjs +++ b/packages/superdough/reverbGen.mjs @@ -70,82 +70,6 @@ reverbGen.generateGraph = function (data, width, height, min, max) { return canvas; }; -/** Saves an AudioBuffer as a 16-bit WAV file on the client's host - file system. Normalizes it to peak at +-32767, and optionally - truncates it if there's a lot of "silence" at the end. - - @param {!AudioBuffer} buffer The buffer to save. - @param {string} name Name of file to create. - @param {number?} opt_minTail Defines what counts as "silence" for - auto-truncating the buffer. If there is a point past which every - value of every channel is less than opt_minTail, then the buffer - is truncated at that point. This is expressed as an integer, - applying to the post-normalized and integer-converted - buffer. The default is 0, meaning don't truncate. */ -reverbGen.saveWavFile = function (buffer, name, opt_minTail) { - var bitsPerSample = 16; - var bytesPerSample = 2; - var sampleRate = buffer.sampleRate; - var numChannels = buffer.numberOfChannels; - var channels = getAllChannelData(buffer); - var numSampleFrames = channels[0].length; - var scale = 32767; - // Find normalization constant. - var max = 0; - for (var i = 0; i < numChannels; i++) { - for (var j = 0; j < numSampleFrames; j++) { - max = Math.max(max, Math.abs(channels[i][j])); - } - } - if (max) { - scale = 32767 / max; - } - // Find truncation point. - if (opt_minTail) { - var truncateAt = 0; - for (var i = 0; i < numChannels; i++) { - for (var j = 0; j < numSampleFrames; j++) { - var absSample = Math.abs(Math.round(scale * channels[i][j])); - if (absSample > opt_minTail) { - truncateAt = j; - } - } - } - numSampleFrames = truncateAt + 1; - } - var sampleDataBytes = bytesPerSample * numChannels * numSampleFrames; - var fileBytes = sampleDataBytes + 44; - var arrayBuffer = new ArrayBuffer(fileBytes); - var dataView = new DataView(arrayBuffer); - dataView.setUint32(0, 1179011410, true); // "RIFF" - dataView.setUint32(4, fileBytes - 8, true); // file length - dataView.setUint32(8, 1163280727, true); // "WAVE" - dataView.setUint32(12, 544501094, true); // "fmt " - dataView.setUint32(16, 16, true); // fmt chunk length - dataView.setUint16(20, 1, true); // PCM format - dataView.setUint16(22, numChannels, true); // NumChannels - dataView.setUint32(24, sampleRate, true); // SampleRate - var bytesPerSampleFrame = numChannels * bytesPerSample; - dataView.setUint32(28, sampleRate * bytesPerSampleFrame, true); // ByteRate - dataView.setUint16(32, bytesPerSampleFrame, true); // BlockAlign - dataView.setUint16(34, bitsPerSample, true); // BitsPerSample - dataView.setUint32(36, 1635017060, true); // "data" - dataView.setUint32(40, sampleDataBytes, true); - for (var j = 0; j < numSampleFrames; j++) { - for (var i = 0; i < numChannels; i++) { - dataView.setInt16(44 + j * bytesPerSampleFrame + i * bytesPerSample, Math.round(scale * channels[i][j]), true); - } - } - var blob = new Blob([arrayBuffer], { type: 'audio/wav' }); - var url = window.URL.createObjectURL(blob); - var linkEl = document.createElement('a'); - linkEl.href = url; - linkEl.download = name; - linkEl.style.display = 'none'; - document.body.appendChild(linkEl); - linkEl.click(); -}; - /** Applies a constantly changing lowpass filter to the given sound. @private