diff --git a/packages/soundfonts/fontloader.mjs b/packages/soundfonts/fontloader.mjs index 427f57e31..441d294b0 100644 --- a/packages/soundfonts/fontloader.mjs +++ b/packages/soundfonts/fontloader.mjs @@ -1,5 +1,6 @@ import { noteToMidi, freqToMidi, getSoundIndex } from '@strudel/core'; import { + cleanupNodes, getAudioContext, registerSound, getParamADSR, @@ -143,7 +144,7 @@ export function registerSoundfonts() { Object.entries(gm).forEach(([name, fonts]) => { registerSound( name, - async (time, value, onended) => { + async (time, value) => { const [attack, decay, sustain, release] = getADSRValues([ value.attack, value.decay, @@ -161,22 +162,14 @@ export function registerSoundfonts() { const node = bufferSource.connect(envGain); const holdEnd = time + duration; getParamADSR(node.gain, attack, decay, sustain, release, 0, 0.3, time, holdEnd, 'linear'); - let envEnd = holdEnd + release + 0.01; - // vibrato let vibratoOscillator = getVibratoOscillator(bufferSource.detune, value, time); // pitch envelope getPitchEnvelope(bufferSource.detune, value, time, holdEnd); - - bufferSource.stop(envEnd); - const stop = (releaseTime) => {}; - bufferSource.onended = () => { - bufferSource.disconnect(); - vibratoOscillator?.stop(); - node.disconnect(); - onended(); + const cleanup = () => { + cleanupNodes([bufferSource, vibratoOscillator, node]); }; - return { node, stop }; + return { node, cleanup }; }, { type: 'soundfont', prebake: true, fonts }, ); diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 47161ed61..fbc1e0b7a 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -518,10 +518,15 @@ export const getFrequencyFromValue = (value, defaultNote = 36) => { return Number(freq); }; -export const destroyAudioWorkletNode = (node) => { +export const cleanupNode = (node) => { if (node == null) { return; } node.disconnect(); - node.parameters.get('end')?.setValueAtTime(0, 0); + node.parameters?.get('end')?.setValueAtTime(0, 0); + node.stop?.(); +}; + +export const cleanupNodes = (nodes) => { + nodes.map(cleanupNodes); }; diff --git a/packages/superdough/sampler.mjs b/packages/superdough/sampler.mjs index b8c14f20d..1d4fcedec 100644 --- a/packages/superdough/sampler.mjs +++ b/packages/superdough/sampler.mjs @@ -1,7 +1,7 @@ import { getCommonSampleInfo } from './util.mjs'; import { registerSound, registerWaveTable } from './index.mjs'; import { getAudioContext } from './audioContext.mjs'; -import { getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs'; +import { getADSRValues, cleanupNodes, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs'; import { logger } from './logger.mjs'; const bufferCache = {}; // string: Promise @@ -320,9 +320,7 @@ export async function onTriggerSample(t, value, onended, bank, resolveUrl) { const out = ac.createGain(); // we need a separate gain for the cutgroups because firefox... node.connect(out); const cleanup = () => { - bufferSource.disconnect(); - vibratoOscillator?.stop(); - node.disconnect(); + cleanupNodes([bufferSource, vibratoOscillator, node]); }; const handle = { node: out, cleanup }; diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 9f3ab6cf2..b52efd87e 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -3,7 +3,7 @@ import { registerSound, soundMap } from './superdough.mjs'; import { getAudioContext } from './audioContext.mjs'; import { applyFM, - destroyAudioWorkletNode, + cleanupNodes, gainNode, getADSRValues, getFrequencyFromValue, @@ -49,8 +49,7 @@ export function registerSynthSounds() { // turn down const g = gainNode(0.3); const cleanup = () => { - o.disconnect(); - g.disconnect(); + cleanupNodes([o, g]); }; const node = o.connect(g).connect(gainNode(1)); const holdEnd = t + value.duration; @@ -97,11 +96,7 @@ export function registerSynthSounds() { const mix = gainNode(mixGain); const cleanup = () => { - o.disconnect(); - g.disconnect(); - sat.disconnect(); - noise.node.disconnect(); - noiseGain.disconnect(); + cleanupNodes([o, g, sat, noise.node, noiseGain]); }; const node = o.connect(sat).connect(g).connect(mix); @@ -168,9 +163,7 @@ export function registerSynthSounds() { getParamADSR(node.gain, attack, decay, sustain, release, 0, 0.3 * gainAdjustment, begin, holdend, 'linear'); const cleanup = () => { - destroyAudioWorkletNode(o); - fm?.stop(); - vibratoOscillator?.stop(); + cleanupNodes([o, fm, vibratoOscillator]); }; return { node, @@ -227,15 +220,12 @@ export function registerSynthSounds() { outputChannelCount: [2], }, ); - o.port.postMessage({ codeText: byteBeatExpression, byteBeatStartTime, frequency }); - const node = o.connect(gainNode(1)); - getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, begin, holdend, 'linear'); const cleanup = () => { - destroyAudioWorkletNode(o); + cleanupNodes([node, o]); }; return { @@ -299,10 +289,7 @@ export function registerSynthSounds() { lfo.connect(o.parameters.get('pulsewidth')); } const cleanup = () => { - destroyAudioWorkletNode(o); - destroyAudioWorkletNode(lfo); - fm?.stop(); - vibratoOscillator?.stop(); + cleanupNodes([o, lfo, fm, vibratoOscillator]); }; return { node, @@ -339,8 +326,7 @@ export function registerSynthSounds() { const holdEnd = t + duration; getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, t, holdEnd, 'linear'); const cleanup = () => { - g.disconnect(); - o.disconnect(); + cleanupNodes([node, o, g]); }; return { node, diff --git a/packages/superdough/wavetable.mjs b/packages/superdough/wavetable.mjs index 86c8f661b..c91813aad 100644 --- a/packages/superdough/wavetable.mjs +++ b/packages/superdough/wavetable.mjs @@ -3,14 +3,13 @@ import { getCommonSampleInfo } from './util.mjs'; import { applyFM, applyParameterModulators, - destroyAudioWorkletNode, + cleanupNodes, getADSRValues, getFrequencyFromValue, getParamADSR, getPitchEnvelope, getVibratoOscillator, getWorklet, - webAudioTimeout, } from './helpers.mjs'; import { logger } from './logger.mjs'; @@ -316,12 +315,7 @@ export async function onTriggerSynth(t, value, tables, cps, frameLen) { getPitchEnvelope(source.parameters.get('detune'), value, t, holdEnd); const handle = { node, source }; handle.cleanup = () => { - destroyAudioWorkletNode(source); - vibratoOscillator?.stop(); - fm?.stop(); - node.disconnect(); - wtPosModulators?.disconnect(); - wtWarpModulators?.disconnect(); + cleanupNodes([source, vibratoOscillator, fm, node, wtPosModulators, wtWarpModulators]); }; return handle; } diff --git a/packages/superdough/zzfx.mjs b/packages/superdough/zzfx.mjs index 58692f695..c7043ca80 100644 --- a/packages/superdough/zzfx.mjs +++ b/packages/superdough/zzfx.mjs @@ -3,6 +3,7 @@ import { midiToFreq, noteToMidi } from './util.mjs'; import { registerSound } from './superdough.mjs'; import { getAudioContext } from './audioContext.mjs'; import { buildSamples } from './zzfx_fork.mjs'; +import { cleanupNode } from './helpers.mjs'; export const getZZFX = (value, t) => { let { @@ -72,6 +73,9 @@ export const getZZFX = (value, t) => { const source = getAudioContext().createBufferSource(); source.buffer = buffer; source.start(t); + const cleanup = () => { + cleanupNode(source); + }; return { node: source, };