Simplify declarations via globalThis; fix cleanup/stop

This commit is contained in:
Aria 2025-11-18 16:02:33 -06:00
parent ac3f477f30
commit 654c1a86f0
2 changed files with 44 additions and 108 deletions

View file

@ -468,7 +468,8 @@ export const { attack, att } = registerControl('attack', 'att');
* ._scope() * ._scope()
* *
*/ */
export const { fmh, fmh1, fmh2, fmh3, fmh4, fmh5, fmh6, fmh7, fmh8 } = registerMultiControl(['fmh', 'fmi'], 8, 'fmh'); Object.assign(globalThis, registerMultiControl(['fmh', 'fmi'], 8, 'fmh'));
/** /**
* Sets the Frequency Modulation of the synth. * Sets the Frequency Modulation of the synth.
* Controls the modulation index, which defines the brightness of the sound. * Controls the modulation index, which defines the brightness of the sound.
@ -491,8 +492,8 @@ export const { fmh, fmh1, fmh2, fmh3, fmh4, fmh5, fmh6, fmh7, fmh8 } = registerM
* .fmh(1.06).fmh2(10).fmh3(0.1) * .fmh(1.06).fmh2(10).fmh3(0.1)
* *
*/ */
export const { fmi, fmi1, fmi2, fmi3, fmi4, fmi5, fmi6, fmi7, fmi8, fm, fm1, fm2, fm3, fm4, fm5, fm6, fm7, fm8 } = Object.assign(globalThis, registerMultiControl(['fmi', 'fmh'], 8, 'fm'));
registerMultiControl(['fmi', 'fmh'], 8, 'fm');
// fm envelope // fm envelope
/** /**
* Ramp type of fm envelope. Exp might be a bit broken.. * Ramp type of fm envelope. Exp might be a bit broken..
@ -511,10 +512,8 @@ export const { fmi, fmi1, fmi2, fmi3, fmi4, fmi5, fmi6, fmi7, fmi8, fm, fm1, fm2
* ._scope() * ._scope()
* *
*/ */
export const { fmenv, fmenv1, fmenv2, fmenv3, fmenv4, fmenv5, fmenv6, fmenv7, fmenv8 } = registerMultiControl( Object.assign(globalThis, registerMultiControl('fmenv', 8));
'fmenv',
8,
);
/** /**
* Attack time for the FM envelope: time it takes to reach maximum modulation * Attack time for the FM envelope: time it takes to reach maximum modulation
* *
@ -531,26 +530,7 @@ export const { fmenv, fmenv1, fmenv2, fmenv3, fmenv4, fmenv5, fmenv6, fmenv7, fm
* ._scope() * ._scope()
* *
*/ */
export const { Object.assign(globalThis, registerMultiControl('fmattack', 8, 'fmatt'));
fmattack,
fmattack1,
fmattack2,
fmattack3,
fmattack4,
fmattack5,
fmattack6,
fmattack7,
fmattack8,
fmatt,
fmatt1,
fmatt2,
fmatt3,
fmatt4,
fmatt5,
fmatt6,
fmatt7,
fmatt8,
} = registerMultiControl('fmattack', 8, 'fmatt');
/** /**
* Waveform of the fm modulator * Waveform of the fm modulator
@ -566,10 +546,7 @@ export const {
* n("0 1 2 3".fast(4)).chord("<Dm Am F G>").voicing().s("sawtooth").fmwave("brown").fm(.6) * n("0 1 2 3".fast(4)).chord("<Dm Am F G>").voicing().s("sawtooth").fmwave("brown").fm(.6)
* *
*/ */
export const { fmwave, fmwave1, fmwave2, fmwave3, fmwave4, fmwave5, fmwave6, fmwave7, fmwave8 } = registerMultiControl( Object.assign(globalThis, registerMultiControl('fmwave', 8));
'fmwave',
8,
);
/** /**
* Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase.
@ -588,26 +565,8 @@ export const { fmwave, fmwave1, fmwave2, fmwave3, fmwave4, fmwave5, fmwave6, fmw
* ._scope() * ._scope()
* *
*/ */
export const { Object.assign(globalThis, registerMultiControl('fmdecay', 8, 'fmdec'));
fmdecay,
fmdecay1,
fmdecay2,
fmdecay3,
fmdecay4,
fmdecay5,
fmdecay6,
fmdecay7,
fmdecay8,
fmdec,
fmdec1,
fmdec2,
fmdec3,
fmdec4,
fmdec5,
fmdec6,
fmdec7,
fmdec8,
} = registerMultiControl('fmdecay', 8, 'fmdec');
/** /**
* Sustain level for the FM envelope: how much modulation is applied after the decay phase * Sustain level for the FM envelope: how much modulation is applied after the decay phase
* *
@ -625,26 +584,8 @@ export const {
* ._scope() * ._scope()
* *
*/ */
export const { Object.assign(globalThis, registerMultiControl('fmsustain', 8, 'fmsus'));
fmsustain,
fmsustain1,
fmsustain2,
fmsustain3,
fmsustain4,
fmsustain5,
fmsustain6,
fmsustain7,
fmsustain8,
fmsus,
fmsus1,
fmsus2,
fmsus3,
fmsus4,
fmsus5,
fmsus6,
fmsus7,
fmsus8,
} = registerMultiControl('fmsustain', 8, 'fmsus');
/** /**
* Release time for the FM envelope: how much modulation is applied after the note is released * Release time for the FM envelope: how much modulation is applied after the note is released
* *
@ -656,31 +597,12 @@ export const {
* @param {number | Pattern} time release time * @param {number | Pattern} time release time
* *
*/ */
export const { Object.assign(globalThis, registerMultiControl('fmrelease', 8, 'fmrel'));
fmrelease,
fmrelease1,
fmrelease2,
fmrelease3,
fmrelease4,
fmrelease5,
fmrelease6,
fmrelease7,
fmrelease8,
fmrel,
fmrel1,
fmrel2,
fmrel3,
fmrel4,
fmrel5,
fmrel6,
fmrel7,
fmrel8,
} = registerMultiControl('fmrelease', 8, 'fmrel');
// FM Matrix // FM Matrix
for (let i = 0; i <= 8; i++) { for (let i = 0; i <= 8; i++) {
for (let j = 0; j <= 8; j++) { for (let j = 0; j <= 8; j++) {
registerControl(`fmi${i}${j}`, `fm${i}${j}`); Object.assign(globalThis, registerControl(`fmi${i}${j}`, `fm${i}${j}`));
} }
} }

View file

@ -336,6 +336,7 @@ export function webAudioTimeout(audioContext, onComplete, startTime, stopTime) {
constantNode.stop(stopTime); constantNode.stop(stopTime);
return constantNode; return constantNode;
} }
const mod = (freq, type = 'sine') => { const mod = (freq, type = 'sine') => {
const ctx = getAudioContext(); const ctx = getAudioContext();
let osc; let osc;
@ -349,10 +350,10 @@ const mod = (freq, type = 'sine') => {
osc.frequency.value = freq; osc.frequency.value = freq;
} }
osc.start(); osc.start();
return { osc, stop: (t) => osc.stop(t), freq }; return { osc, freq };
}; };
const fm = (frequencyparam, harmonicityRatio, modulationIndex, wave = 'sine') => { const fm = (frequencyparam, harmonicityRatio, wave = 'sine') => {
const carrfreq = frequencyparam.value; const carrfreq = frequencyparam.value;
const modfreq = carrfreq * harmonicityRatio; const modfreq = carrfreq * harmonicityRatio;
return mod(modfreq, wave); return mod(modfreq, wave);
@ -360,7 +361,7 @@ const fm = (frequencyparam, harmonicityRatio, modulationIndex, wave = 'sine') =>
export function applyFM(param, value, begin) { export function applyFM(param, value, begin) {
const ac = getAudioContext(); const ac = getAudioContext();
let stop = (t) => {}; const toStop = []; // fm oscillators we will expose `stop` for
const fms = {}; const fms = {};
// Matrix // Matrix
for (let i = 1; i <= 8; i++) { for (let i = 1; i <= 8; i++) {
@ -377,8 +378,8 @@ export function applyFM(param, value, begin) {
if (!amt) continue; if (!amt) continue;
let io = []; let io = [];
for (let [isMod, idx] of [ for (let [isMod, idx] of [
[true, i], [true, i], // source
[false, j], [false, j], // target
]) { ]) {
if (idx === 0) { if (idx === 0) {
io.push(param); io.push(param);
@ -387,15 +388,11 @@ export function applyFM(param, value, begin) {
if (!fms[idx]) { if (!fms[idx]) {
const idxS = idx === 1 ? '' : idx; const idxS = idx === 1 ? '' : idx;
const { osc, freq } = fm(param, value[`fmh${idxS}`] ?? 1, value[`fmwave${idxS}`] ?? 'sine'); const { osc, freq } = fm(param, value[`fmh${idxS}`] ?? 1, value[`fmwave${idxS}`] ?? 'sine');
const currStop = stop; toStop.push(osc);
stop = (t) => { const toCleanup = [osc]; // nodes we want to cleanup after oscillator `stop`
currStop(t);
osc.stop(t);
};
const adsr = ['attack', 'decay', 'sustain', 'release'].map((s) => value[`fm${s}${idxS}`]); const adsr = ['attack', 'decay', 'sustain', 'release'].map((s) => value[`fm${s}${idxS}`]);
if (!adsr.some((v) => v !== undefined)) { let output = osc;
fms[idx] = { input: osc.frequency, output: osc, freq }; if (adsr.some((v) => v !== undefined)) {
} else {
const envGain = ac.createGain(); const envGain = ac.createGain();
const [attack, decay, sustain, release] = getADSRValues(adsr); const [attack, decay, sustain, release] = getADSRValues(adsr);
const holdEnd = begin + value.duration; const holdEnd = begin + value.duration;
@ -412,12 +409,16 @@ export function applyFM(param, value, begin) {
holdEnd, holdEnd,
fmEnvelopeType === 'exp' ? 'exponential' : 'linear', fmEnvelopeType === 'exp' ? 'exponential' : 'linear',
); );
fms[idx] = { input: osc.frequency, output: osc.connect(envGain), freq }; toCleanup.push(envGain);
output = osc.connect(envGain);
} }
fms[idx] = { input: osc.frequency, output, freq, toCleanup };
osc.onended = () => cleanupNodes(fms[idx].toCleanup);
} }
const { input, output, freq } = fms[idx]; const { input, output, freq, toCleanup } = fms[idx];
const g = gainNode(amt * freq); const g = gainNode(amt * freq);
io.push(isMod ? output.connect(g) : input); io.push(isMod ? output.connect(g) : input);
toCleanup.push(g);
} }
if (!io[1]) { if (!io[1]) {
logger( logger(
@ -429,7 +430,9 @@ export function applyFM(param, value, begin) {
io[0].connect(io[1]); io[0].connect(io[1]);
} }
} }
return { stop }; return {
stop: (t) => toStop.forEach((m) => m?.stop(t)),
};
} }
// Saturation curves // Saturation curves
@ -549,3 +552,14 @@ export const destroyAudioWorkletNode = (node) => {
node.disconnect(); node.disconnect();
node.parameters.get('end')?.setValueAtTime(0, 0); node.parameters.get('end')?.setValueAtTime(0, 0);
}; };
export const cleanupNode = (node, time) => {
if (node == null) return;
node.disconnect?.();
node.parameters?.get('end')?.setValueAtTime(0, 0);
node.stop?.(time);
};
export const cleanupNodes = (nodes, time) => {
nodes.forEach((n) => cleanupNode(n, time));
};