Merge pull request '[perf] propagate onceEnded and releaseAudioNode' (#1809) from jeromew/strudel:fix-perf12 into main

Reviewed-on: https://codeberg.org/uzu/strudel/pulls/1809
Reviewed-by: Aria <glossing@noreply.codeberg.org>
This commit is contained in:
Aria
2025-12-08 19:27:30 +01:00
7 changed files with 58 additions and 39 deletions
+3 -3
View File
@@ -7,6 +7,7 @@ import {
getPitchEnvelope,
getVibratoOscillator,
onceEnded,
releaseAudioNode,
} from '@strudel/webaudio';
import gm from './gm.mjs';
@@ -172,9 +173,8 @@ export function registerSoundfonts() {
bufferSource.stop(envEnd);
const stop = (releaseTime) => {};
onceEnded(bufferSource, () => {
bufferSource.disconnect();
vibratoOscillator?.stop();
node.disconnect();
releaseAudioNode(bufferSource);
releaseAudioNode(vibratoOscillator);
onended();
});
return { node, stop };
+13 -8
View File
@@ -189,7 +189,7 @@ export function applyParameterModulators(audioContext, param, start, end, envelo
getParamADSR(param, attack, decay, sustain, release, min, max, start, holdEnd, curve);
}
const lfo = getParamLfo(audioContext, param, start, end, lfoValues);
return { lfo, disconnect: () => lfo?.disconnect() };
return lfo;
}
export function createFilter(context, start, end, params, cps, cycle) {
let {
@@ -283,9 +283,12 @@ export function drywet(dry, wet, wetAmount = 0) {
wet_gain.connect(mix);
return {
node: mix,
onended: () => {
dry_gain.disconnect(mix);
wet_gain.disconnect(mix);
teardown: () => {
releaseAudioNode(dry_gain);
releaseAudioNode(wet_gain);
// it is not the responsability of drywet
// to call `releaseAudioNode` on
// the 2 external args dry and wet
dry.disconnect(dry_gain);
wet.disconnect(wet_gain);
},
@@ -324,10 +327,10 @@ export function getVibratoOscillator(param, value, t) {
gain.gain.value = vibmod * 100;
vibratoOscillator.connect(gain);
gain.connect(param);
vibratoOscillator.onended = () => {
gain.disconnect(param);
vibratoOscillator.disconnect(gain);
};
onceEnded(vibratoOscillator, () => {
releaseAudioNode(gain);
releaseAudioNode(vibratoOscillator);
});
vibratoOscillator.start(t);
return vibratoOscillator;
}
@@ -613,6 +616,8 @@ export const releaseAudioNode = (node) => {
// returns true and either its active source flag is true or
// any AudioNode connected to one of its inputs is actively processing.
if (node instanceof AudioWorkletNode) {
// while `end` is not native to the web audio API, it is common practice in superdough
// to use that param in the worklets to trigger returning false from the processor
node.parameters.get('end')?.setValueAtTime(0, 0);
}
};
+5 -2
View File
@@ -1,4 +1,4 @@
import { drywet } from './helpers.mjs';
import { drywet, onceEnded, releaseAudioNode } from './helpers.mjs';
import { getAudioContext } from './audioContext.mjs';
let noiseCache = {};
@@ -65,9 +65,12 @@ export function getNoiseOscillator(type = 'white', t, density = 0.02) {
export function getNoiseMix(inputNode, wet, t) {
const noiseOscillator = getNoiseOscillator('pink', t);
const noiseMix = drywet(inputNode, noiseOscillator.node, wet);
noiseOscillator.node.onended = () => noiseMix.onended();
onceEnded(noiseOscillator.node, () => {
releaseAudioNode(noiseOscillator.node);
});
return {
node: noiseMix.node,
stop: (time) => noiseOscillator?.stop(time),
teardown: noiseMix.teardown,
};
}
+4 -4
View File
@@ -323,10 +323,10 @@ 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);
onceEnded(bufferSource, function () {
bufferSource.disconnect();
vibratoOscillator?.stop();
node.disconnect();
out.disconnect();
releaseAudioNode(bufferSource);
releaseAudioNode(vibratoOscillator);
releaseAudioNode(node);
releaseAudioNode(out);
onended();
});
let envEnd = holdEnd + release + 0.01;
+11 -2
View File
@@ -9,7 +9,16 @@ import './reverb.mjs';
import './vowel.mjs';
import { nanFallback, _mod, cycleToSeconds, pickAndRename } from './util.mjs';
import workletsUrl from './worklets.mjs?audioworklet';
import { createFilter, gainNode, getCompressor, getDistortion, getLfo, getWorklet, effectSend } from './helpers.mjs';
import {
createFilter,
gainNode,
getCompressor,
getDistortion,
getLfo,
getWorklet,
effectSend,
releaseAudioNode,
} from './helpers.mjs';
import { map } from 'nanostores';
import { logger } from './logger.mjs';
import { loadBuffer } from './sampler.mjs';
@@ -505,7 +514,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
} else if (getSound(s)) {
const { onTrigger } = getSound(s);
const onEnded = () => {
audioNodes.forEach((n) => n?.disconnect());
audioNodes.forEach((n) => releaseAudioNode(n));
activeSoundSources.delete(chainID);
};
const soundHandle = await onTrigger(t, value, onEnded, cps);
+19 -17
View File
@@ -12,6 +12,7 @@ import {
getVibratoOscillator,
getWorklet,
noises,
onceEnded,
releaseAudioNode,
webAudioTimeout,
} from './helpers.mjs';
@@ -52,7 +53,7 @@ export function registerSynthSounds() {
const g = gainNode(0.3);
let sound = getOscillator(s, t, value, () => {
g.disconnect();
releaseAudioNode(g);
onended();
});
@@ -110,15 +111,15 @@ export function registerSynthSounds() {
const mix = gainNode(mixGain);
o.onended = () => {
o.disconnect();
g.disconnect();
sat.disconnect();
noise.node.disconnect();
noiseGain.disconnect();
mix.disconnect();
onceEnded(o, () => {
releaseAudioNode(o);
releaseAudioNode(g);
releaseAudioNode(sat);
releaseAudioNode(noise.node);
releaseAudioNode(noiseGain);
releaseAudioNode(mix);
onended();
};
});
const node = o.connect(sat).connect(g).connect(mix);
noise.node.connect(noiseGain).connect(mix);
@@ -384,11 +385,11 @@ export function registerSynthSounds() {
const { duration } = value;
o.onended = () => {
o.disconnect();
g.disconnect();
onceEnded(o, () => {
releaseAudioNode(o);
releaseAudioNode(g);
onended();
};
});
const envGain = gainNode(1);
let node = o.connect(g).connect(envGain);
@@ -489,11 +490,12 @@ export function getOscillator(s, t, value, onended) {
noiseMix = getNoiseMix(o, noise, t);
}
o.onended = () => {
noiseMix || o.disconnect();
noiseMix?.node.disconnect();
onceEnded(o, () => {
noiseMix?.teardown();
releaseAudioNode(o);
releaseAudioNode(noiseMix?.node);
onended();
};
});
o.start(t);
return {
+3 -3
View File
@@ -320,10 +320,10 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
ac,
() => {
releaseAudioNode(source);
vibratoOscillator?.stop();
releaseAudioNode(vibratoOscillator);
fm?.stop();
wtPosModulators?.disconnect();
wtWarpModulators?.disconnect();
releaseAudioNode(wtPosModulators);
releaseAudioNode(wtWarpModulators);
onended();
},
t,