mirror of
https://codeberg.org/uzu/strudel
synced 2026-07-23 21:47:16 -04:00
Revert to old stop format for compatibility
This commit is contained in:
@@ -144,7 +144,7 @@ export function registerSoundfonts() {
|
||||
Object.entries(gm).forEach(([name, fonts]) => {
|
||||
registerSound(
|
||||
name,
|
||||
async (time, value) => {
|
||||
async (time, value, onended) => {
|
||||
const [attack, decay, sustain, release] = getADSRValues([
|
||||
value.attack,
|
||||
value.decay,
|
||||
@@ -166,10 +166,13 @@ export function registerSoundfonts() {
|
||||
let vibratoOscillator = getVibratoOscillator(bufferSource.detune, value, time);
|
||||
// pitch envelope
|
||||
getPitchEnvelope(bufferSource.detune, value, time, holdEnd);
|
||||
const cleanup = () => {
|
||||
bufferSource.onended = () => {
|
||||
cleanupNodes([bufferSource, vibratoOscillator, node]);
|
||||
onended();
|
||||
};
|
||||
return { node, cleanup };
|
||||
const envEnd = holdEnd + release + 0.01;
|
||||
bufferSource.stop(envEnd);
|
||||
return { node, stop: bufferSource.stop };
|
||||
},
|
||||
{ type: 'soundfont', prebake: true, fonts },
|
||||
);
|
||||
|
||||
@@ -518,15 +518,13 @@ export const getFrequencyFromValue = (value, defaultNote = 36) => {
|
||||
return Number(freq);
|
||||
};
|
||||
|
||||
export const cleanupNode = (node) => {
|
||||
if (node == null) {
|
||||
return;
|
||||
}
|
||||
node.disconnect();
|
||||
export const cleanupNode = (node, time) => {
|
||||
if (node == null) return;
|
||||
node.disconnect?.();
|
||||
node.parameters?.get('end')?.setValueAtTime(0, 0);
|
||||
node.stop?.();
|
||||
node.stop?.(time);
|
||||
};
|
||||
|
||||
export const cleanupNodes = (nodes) => {
|
||||
nodes.map(cleanupNodes);
|
||||
export const cleanupNodes = (nodes, time) => {
|
||||
nodes.map((n) => cleanupNode(n, time));
|
||||
};
|
||||
|
||||
@@ -319,10 +319,11 @@ 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 = () => {
|
||||
const stop = () => {
|
||||
cleanupNodes([bufferSource, vibratoOscillator, node]);
|
||||
onended();
|
||||
};
|
||||
const handle = { node: out, cleanup };
|
||||
const handle = { node: out, stop };
|
||||
|
||||
// cut groups
|
||||
if (cut !== undefined) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import './vowel.mjs';
|
||||
import { _mod, clamp, cycleToSeconds, pickAndRename } from './util.mjs';
|
||||
import workletsUrl from './worklets.mjs?audioworklet';
|
||||
import {
|
||||
cleanupNode,
|
||||
cleanupNodes,
|
||||
createFilter,
|
||||
gainNode,
|
||||
getCompressor,
|
||||
@@ -152,7 +154,7 @@ export const getAudioDevices = async () => {
|
||||
|
||||
let defaultDefaultValues = {
|
||||
s: 'triangle',
|
||||
gain: 1,
|
||||
gain: 0.8,
|
||||
postgain: 1,
|
||||
density: '.03',
|
||||
channels: [1, 2],
|
||||
@@ -169,6 +171,7 @@ let defaultDefaultValues = {
|
||||
velocity: 1,
|
||||
fft: 8,
|
||||
tremolodepth: 1,
|
||||
tremolophase: 0,
|
||||
drive: 0.69,
|
||||
};
|
||||
|
||||
@@ -484,15 +487,15 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
}
|
||||
|
||||
// get source AudioNode
|
||||
let sourceNode, cleanup;
|
||||
let sourceNode, stop;
|
||||
if (source) {
|
||||
sourceNode = source(t, value, hapDuration, cps);
|
||||
} else if (getSound(s)) {
|
||||
const { onTrigger } = getSound(s);
|
||||
const soundHandle = await onTrigger(t, value, () => void 0, cps);
|
||||
const soundHandle = await onTrigger(t, value, () => {}, cps);
|
||||
if (soundHandle) {
|
||||
sourceNode = soundHandle.node;
|
||||
cleanup = soundHandle.cleanup;
|
||||
stop = soundHandle.stop;
|
||||
activeSoundSources.set(chainID, new WeakRef(soundHandle)); // allow GC
|
||||
}
|
||||
} else {
|
||||
@@ -509,7 +512,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
return;
|
||||
}
|
||||
const chain = []; // audio nodes that will be connected to each other sequentially
|
||||
chain.push({ input: sourceNode, output: sourceNode, cleanup });
|
||||
chain.push({ input: sourceNode, output: sourceNode, stop });
|
||||
FX = [...FX, value]; // run through the FX chain and then run through all FX outside of it as well
|
||||
for (const fx of FX) {
|
||||
let {
|
||||
@@ -684,18 +687,8 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
}
|
||||
// phaser
|
||||
if (fx.phaserrate !== undefined && phaserdepth > 0) {
|
||||
const dry = gainNode(1);
|
||||
const phaserFX = getPhaser(t, endWithRelease, fx.phaserrate, phaserdepth, fx.phasercenter, fx.phasersweep);
|
||||
const fb = gainNode(fx.phaserFeedback ?? 0.4);
|
||||
const mix = gainNode(0.5);
|
||||
dry.connect(mix);
|
||||
dry.connect(phaserFX).connect(mix);
|
||||
mix.connect(fb).connect(phaserFX);
|
||||
cleanup = () => {
|
||||
phaserFX.disconnect();
|
||||
dry.disconnect();
|
||||
};
|
||||
chain.push({ input: dry, output: mix, cleanup });
|
||||
chain.push(phaserFX);
|
||||
}
|
||||
// delay
|
||||
if (delay > 0 && delaytime > 0 && delayfeedback > 0) {
|
||||
@@ -707,13 +700,8 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
const sum = new GainNode(ac, { gain: 1, channelCount: 2, channelCountMode: 'explicit' });
|
||||
dry.connect(dryDelay).connect(sum);
|
||||
dry.connect(delayNode).connect(wetDelay).connect(sum);
|
||||
const cleanup = () => {
|
||||
dryDelay.disconnect();
|
||||
delayNode.disconnect();
|
||||
dry.disconnect();
|
||||
wetDelay.disconnect();
|
||||
};
|
||||
chain.push({ input: dry, output: sum, cleanup });
|
||||
const stop = () => cleanupNodes([dryDelay, delayNode, dry, wetDelay]);
|
||||
chain.push({ input: dry, output: sum, stop });
|
||||
}
|
||||
// reverb
|
||||
if (fx.room > 0) {
|
||||
@@ -743,13 +731,8 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
const sum = new GainNode(ac, { gain: 1, channelCount: 2, channelCountMode: 'explicit' });
|
||||
dry.connect(dryReverb).connect(sum);
|
||||
dry.connect(reverbNode).connect(wetReverb).connect(sum);
|
||||
const cleanup = () => {
|
||||
dryReverb.disconnect();
|
||||
reverbNode.disconnect();
|
||||
dry.disconnect();
|
||||
wetReverb.disconnect();
|
||||
};
|
||||
chain.push({ input: dry, output: sum, cleanup });
|
||||
const stop = () => cleanupNodes([dryReverb, reverbNode, dry, wetReverb]);
|
||||
chain.push({ input: dry, output: sum, stop });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -818,9 +801,7 @@ export const superdough = async (value, t, hapDuration, cps = 0.5, cycle = 0.5)
|
||||
ac,
|
||||
() => {
|
||||
audioNodes.forEach((n) => {
|
||||
const node = n?.output ?? n;
|
||||
node?.disconnect();
|
||||
n?.cleanup?.();
|
||||
cleanupNode(n?.output ?? n, endWithRelease + 0.01);
|
||||
});
|
||||
activeSoundSources.delete(chainID);
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
getVibratoOscillator,
|
||||
getWorklet,
|
||||
noises,
|
||||
webAudioTimeout,
|
||||
} from './helpers.mjs';
|
||||
import { getNoiseMix, getNoiseOscillator } from './noise.mjs';
|
||||
|
||||
@@ -39,24 +40,27 @@ export function registerSynthSounds() {
|
||||
[...waveforms].forEach((s) => {
|
||||
registerSound(
|
||||
s,
|
||||
(t, value) => {
|
||||
(t, value, onended) => {
|
||||
const [attack, decay, sustain, release] = getADSRValues(
|
||||
[value.attack, value.decay, value.sustain, value.release],
|
||||
'linear',
|
||||
[0.001, 0.05, 0.6, 0.01],
|
||||
);
|
||||
const { node: o } = getOscillator(s, t, value);
|
||||
const { node: o, stop } = getOscillator(s, t, value);
|
||||
// turn down
|
||||
const g = gainNode(0.3);
|
||||
const cleanup = () => {
|
||||
o.onended = () => {
|
||||
cleanupNodes([o, g]);
|
||||
onended();
|
||||
};
|
||||
const node = o.connect(g).connect(gainNode(1));
|
||||
const holdEnd = t + value.duration;
|
||||
getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, t, holdEnd, 'linear');
|
||||
const envEnd = holdEnd + release + 0.01;
|
||||
stop(envEnd);
|
||||
return {
|
||||
node,
|
||||
cleanup,
|
||||
stop,
|
||||
};
|
||||
},
|
||||
{ type: 'synth', prebake: true },
|
||||
@@ -65,7 +69,7 @@ export function registerSynthSounds() {
|
||||
|
||||
registerSound(
|
||||
'sbd',
|
||||
(t, value) => {
|
||||
(t, value, onended) => {
|
||||
const { duration, decay = 0.5, pdecay = 0.5, penv = 36, clip } = value;
|
||||
const ctx = getAudioContext();
|
||||
const attackhold = 0.02;
|
||||
@@ -95,8 +99,9 @@ export function registerSynthSounds() {
|
||||
|
||||
const mix = gainNode(mixGain);
|
||||
|
||||
const cleanup = () => {
|
||||
o.onended = () => {
|
||||
cleanupNodes([o, g, sat, noise.node, noiseGain]);
|
||||
onended();
|
||||
};
|
||||
|
||||
const node = o.connect(sat).connect(g).connect(mix);
|
||||
@@ -112,9 +117,11 @@ export function registerSynthSounds() {
|
||||
mix.gain.setValueAtTime(mixGain, end - 0.01);
|
||||
mix.gain.linearRampToValueAtTime(0, end);
|
||||
|
||||
o.stop(end);
|
||||
|
||||
return {
|
||||
node,
|
||||
cleanup,
|
||||
stop: o.stop,
|
||||
};
|
||||
},
|
||||
{ type: 'synth', prebake: true },
|
||||
@@ -122,7 +129,7 @@ export function registerSynthSounds() {
|
||||
|
||||
registerSound(
|
||||
'supersaw',
|
||||
(begin, value) => {
|
||||
(begin, value, onended) => {
|
||||
const ac = getAudioContext();
|
||||
let { duration, n, unison = 5, spread = 0.6, detune } = value;
|
||||
detune = detune ?? n ?? 0.18;
|
||||
@@ -162,12 +169,18 @@ export function registerSynthSounds() {
|
||||
|
||||
getParamADSR(node.gain, attack, decay, sustain, release, 0, 0.3 * gainAdjustment, begin, holdend, 'linear');
|
||||
|
||||
const cleanup = () => {
|
||||
cleanupNodes([o, fm, vibratoOscillator]);
|
||||
};
|
||||
const timeoutNode = webAudioTimeout(
|
||||
ac,
|
||||
() => {
|
||||
cleanupNodes([o, fm, vibratoOscillator]);
|
||||
onended();
|
||||
},
|
||||
begin,
|
||||
end,
|
||||
);
|
||||
return {
|
||||
node,
|
||||
cleanup,
|
||||
stop: timeoutNode.stop,
|
||||
};
|
||||
},
|
||||
{ prebake: true, type: 'synth' },
|
||||
@@ -175,7 +188,7 @@ export function registerSynthSounds() {
|
||||
|
||||
registerSound(
|
||||
'bytebeat',
|
||||
(begin, value) => {
|
||||
(begin, value, onended) => {
|
||||
const defaultBeats = [
|
||||
'(t%255 >= t/255%255)*255',
|
||||
'(t*(t*8%60 <= 300)|(-t)*(t*4%512 < 256))+t/400',
|
||||
@@ -223,14 +236,18 @@ export function registerSynthSounds() {
|
||||
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 = () => {
|
||||
cleanupNodes([node, o]);
|
||||
};
|
||||
|
||||
const timeoutNode = webAudioTimeout(
|
||||
ac,
|
||||
() => {
|
||||
cleanupNodes([node, o]);
|
||||
onended();
|
||||
},
|
||||
begin,
|
||||
end,
|
||||
);
|
||||
return {
|
||||
node,
|
||||
cleanup,
|
||||
stop: timeoutNode.stop,
|
||||
};
|
||||
},
|
||||
{ prebake: true, type: 'synth' },
|
||||
@@ -238,7 +255,7 @@ export function registerSynthSounds() {
|
||||
|
||||
registerSound(
|
||||
'pulse',
|
||||
(begin, value) => {
|
||||
(begin, value, onended) => {
|
||||
const ac = getAudioContext();
|
||||
let { pwrate, pwsweep } = value;
|
||||
if (pwsweep == null) {
|
||||
@@ -288,12 +305,18 @@ export function registerSynthSounds() {
|
||||
lfo = getLfo(ac, begin, end, { frequency: pwrate, depth: pwsweep });
|
||||
lfo.connect(o.parameters.get('pulsewidth'));
|
||||
}
|
||||
const cleanup = () => {
|
||||
cleanupNodes([o, lfo, fm, vibratoOscillator]);
|
||||
};
|
||||
const timeoutNode = webAudioTimeout(
|
||||
ac,
|
||||
() => {
|
||||
cleanupNodes([node, o, lfo, fm, vibratoOscillator]);
|
||||
onended();
|
||||
},
|
||||
begin,
|
||||
end,
|
||||
);
|
||||
return {
|
||||
node,
|
||||
cleanup,
|
||||
stop: timeoutNode.stop,
|
||||
};
|
||||
},
|
||||
{ prebake: true, type: 'synth' },
|
||||
@@ -302,7 +325,7 @@ export function registerSynthSounds() {
|
||||
[...noises].forEach((s) => {
|
||||
registerSound(
|
||||
s,
|
||||
(t, value) => {
|
||||
(t, value, onended) => {
|
||||
const [attack, decay, sustain, release] = getADSRValues(
|
||||
[value.attack, value.decay, value.sustain, value.release],
|
||||
'linear',
|
||||
@@ -314,7 +337,7 @@ export function registerSynthSounds() {
|
||||
let { density } = value;
|
||||
sound = getNoiseOscillator(s, t, density);
|
||||
|
||||
let { node: o } = sound;
|
||||
let { node: o, stop } = sound;
|
||||
|
||||
// turn down
|
||||
const g = gainNode(0.3);
|
||||
@@ -325,12 +348,15 @@ export function registerSynthSounds() {
|
||||
let node = o.connect(g).connect(envGain);
|
||||
const holdEnd = t + duration;
|
||||
getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, t, holdEnd, 'linear');
|
||||
const cleanup = () => {
|
||||
o.onended = () => {
|
||||
cleanupNodes([node, o, g]);
|
||||
onended();
|
||||
};
|
||||
const envEnd = holdEnd + release + 0.01;
|
||||
stop(envEnd);
|
||||
return {
|
||||
node,
|
||||
cleanup,
|
||||
stop,
|
||||
};
|
||||
},
|
||||
{ type: 'synth', prebake: true },
|
||||
@@ -397,8 +423,14 @@ export function getOscillator(s, t, value) {
|
||||
if (noise) {
|
||||
noiseMix = getNoiseMix(o, noise, t);
|
||||
}
|
||||
|
||||
const stop = (time) => {
|
||||
fmModulator.stop(time);
|
||||
vibratoOscillator?.stop(time);
|
||||
noiseMix?.stop(time);
|
||||
o.stop(time);
|
||||
};
|
||||
return {
|
||||
node: noiseMix?.node || o,
|
||||
stop,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
getPitchEnvelope,
|
||||
getVibratoOscillator,
|
||||
getWorklet,
|
||||
webAudioTimeout,
|
||||
} from './helpers.mjs';
|
||||
import { logger } from './logger.mjs';
|
||||
|
||||
@@ -165,8 +166,8 @@ const _processTables = (json, baseUrl, frameLen, options = {}) => {
|
||||
export function registerWaveTable(key, tables, params) {
|
||||
registerSound(
|
||||
key,
|
||||
(t, hapValue, cps) => {
|
||||
return onTriggerSynth(t, hapValue, tables, cps, params?.frameLen ?? 2048);
|
||||
(t, hapValue, onended, cps) => {
|
||||
return onTriggerSynth(t, hapValue, onended, tables, cps, params?.frameLen ?? 2048);
|
||||
},
|
||||
{
|
||||
type: 'wavetable',
|
||||
@@ -206,7 +207,7 @@ export const tables = async (url, frameLen, json, options = {}) => {
|
||||
});
|
||||
};
|
||||
|
||||
export async function onTriggerSynth(t, value, tables, cps, frameLen) {
|
||||
export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
|
||||
const { s, n = 0, duration, clip } = value;
|
||||
const ac = getAudioContext();
|
||||
const [attack, decay, sustain, release] = getADSRValues([value.attack, value.decay, value.sustain, value.release]);
|
||||
@@ -314,8 +315,15 @@ export async function onTriggerSynth(t, value, tables, cps, frameLen) {
|
||||
getParamADSR(node.gain, attack, decay, sustain, release, 0, 0.3, t, holdEnd, 'linear');
|
||||
getPitchEnvelope(source.parameters.get('detune'), value, t, holdEnd);
|
||||
const handle = { node, source };
|
||||
handle.cleanup = () => {
|
||||
cleanupNodes([source, vibratoOscillator, fm, node, wtPosModulators, wtWarpModulators]);
|
||||
};
|
||||
const timeoutNode = webAudioTimeout(
|
||||
ac,
|
||||
() => {
|
||||
cleanupNodes([source, vibratoOscillator, fm, node, wtPosModulators, wtWarpModulators]);
|
||||
onended();
|
||||
},
|
||||
t,
|
||||
envEnd,
|
||||
);
|
||||
handle.stop = timeoutNode.stop;
|
||||
return handle;
|
||||
}
|
||||
|
||||
@@ -73,9 +73,6 @@ export const getZZFX = (value, t) => {
|
||||
const source = getAudioContext().createBufferSource();
|
||||
source.buffer = buffer;
|
||||
source.start(t);
|
||||
const cleanup = () => {
|
||||
cleanupNode(source);
|
||||
};
|
||||
return {
|
||||
node: source,
|
||||
};
|
||||
@@ -85,8 +82,12 @@ export function registerZZFXSounds() {
|
||||
['zzfx', 'z_sine', 'z_sawtooth', 'z_triangle', 'z_square', 'z_tan', 'z_noise'].forEach((wave) => {
|
||||
registerSound(
|
||||
wave,
|
||||
(t, value) => {
|
||||
(t, value, onended) => {
|
||||
const { node: o } = getZZFX({ s: wave, ...value }, t);
|
||||
o.onended = () => {
|
||||
cleanupNode(o);
|
||||
onended();
|
||||
};
|
||||
return {
|
||||
node: o,
|
||||
stop: () => {},
|
||||
|
||||
Reference in New Issue
Block a user