simplify modulators

This commit is contained in:
Jade (Rose) Rowland
2025-09-28 14:57:12 -04:00
parent 23d4bfa9d9
commit c689874bb4
2 changed files with 78 additions and 41 deletions
+35
View File
@@ -153,6 +153,41 @@ export const getADSRValues = (params, curve = 'linear', defaultValues) => {
return [Math.max(a ?? 0, envmin), Math.max(d ?? 0, envmin), Math.min(sustain, envmax), Math.max(r ?? 0, releaseMin)];
};
// helper utility for applying standard modulators to a parameter
export function applyParameterModulators(audioContext, param, start, end, envelopeValues, lfoValues) {
let { amount,offset,defaultAmount = 1, curve = 'linear', values, holdEnd, defaultValues } = envelopeValues;
if (amount == null) {
const hasADSRParams = values.some(p => p != null);
amount = hasADSRParams ? defaultAmount : 0;
}
const min = offset ?? 0;
const max = amount + min
const diff = Math.abs(max - min)
if (diff) {
const [attack, decay, sustain, release] = getADSRValues(values, curve, defaultValues);
getParamADSR(param, attack, decay, sustain, release, min, max, start, holdEnd, curve);
}
let lfo
let {defaultDepth = 1,depth, dcoffset, ...getLfoInputs} = lfoValues
if (depth == null) {
const hasLFOParams = Object.values(getLfoInputs).some(v => v != null)
depth = hasLFOParams ? defaultDepth : 0;
}
if (depth) {
lfo = getLfo(audioContext, start, end, {
depth,
dcoffset,
...getLfoInputs
});
lfo.connect(param);
}
return { lfo, disconnect: () => lfo?.disconnect() }
}
export function createFilter(context, type, frequency, Q, att, dec, sus, rel, fenv, start, end, fanchor, model, drive) {
const curve = 'exponential';
const [attack, decay, sustain, release] = getADSRValues([att, dec, sus, rel], curve, [0.005, 0.14, 0, 0.1]);
+43 -41
View File
@@ -1,6 +1,7 @@
import { getAudioContext, registerSound } from './index.mjs';
import { getCommonSampleInfo } from './util.mjs';
import {
applyParameterModulators,
destroyAudioWorkletNode,
getADSRValues,
getFrequencyFromValue,
@@ -220,8 +221,10 @@ export const tables = async (url, frameLen, json, options = {}) => {
});
};
export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
const { s, n = 0, duration, wtenv } = value;
const { s, n = 0, duration } = value;
const ac = getAudioContext();
const [attack, decay, sustain, release] = getADSRValues([value.attack, value.decay, value.sustain, value.release]);
let { warpmode } = value;
@@ -245,7 +248,7 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
position: value.wt,
warp: value.warp,
warpMode: warpmode,
voices: value.unison,
voices: Math.max(value.unison ?? 1, 1),
spread: value.spread,
phaserand: (value.wtphaserand ?? value.unison > 1) ? 1 : 0,
},
@@ -261,55 +264,54 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
const wtParams = source.parameters;
const positionParam = wtParams.get('position');
const warpParam = wtParams.get('warp');
let posLFO;
if ([wtenv, ...posADSRParams].some((p) => p !== undefined)) {
const [pAttack, pDecay, pSustain, pRelease] = getADSRValues(posADSRParams, 'linear', [0, 0.5, 0, 0.1]);
const min = value.wt ?? 0;
const max = (wtenv ?? 0.5) + min;
getParamADSR(positionParam, pAttack, pDecay, pSustain, pRelease, min, max, t, holdEnd, 'linear');
}
let wtrate = value.wtrate;
if (value.wtsync != null) {
wtrate = wtrate = cps * value.wtsync;
wtrate = cps * value.wtsync;
}
if ([wtrate, value.wtdepth, value.wtshape, value.wtskew, value.wtdc].some((p) => p != null)) {
const posLFO = getLfo(ac, t, endWithRelease, {
frequency: wtrate,
depth: value.wtdepth ?? 0.5,
shape: value.wtshape,
skew: value.wtskew,
dcoffset: value.wtdc ?? 0,
});
posLFO.connect(positionParam);
}
let warpLFO;
if (posADSRParams.some((p) => p !== undefined)) {
const [wAttack, wDecay, wSustain, wRelease] = getADSRValues(warpADSRParams);
const min = value.warp ?? 0;
const max = (value.warpenv ?? 0.5) + min;
getParamADSR(warpParam, wAttack, wDecay, wSustain, wRelease, min, max, t, holdEnd, 'linear', [0, 0.5, 0, 0.1]);
}
const wtPosModulators = applyParameterModulators(ac, positionParam, t, endWithRelease, {
offset: value.wt,
amount: value.wtenv,
defaultAmount: 0.5,
shape: 'linear',
values: posADSRParams,
holdEnd,
defaultValues: [0, 0.5, 0, 0.1],
}, {
frequency: wtrate,
depth: value.wtdepth,
defaultDepth: 0.5,
shape: value.wtshape,
skew: value.wtskew,
dcoffset: value.wtdc ?? 0,
});
let warprate = value.warprate;
if (value.warpsync != null) {
console.info(value.warpsync);
warprate = warprate = cps * value.warpsync;
}
if ([warprate, value.warpdepth, value.warpshape, value.warpskew, value.warpdc].some((p) => p != null)) {
const warpLFO = getLfo(ac, t, endWithRelease, {
frequency: warprate,
depth: value.warpdepth ?? 0.5,
shape: value.warpshape,
skew: value.warpskew,
dcoffset: value.warpdc ?? 0,
});
warpLFO.connect(warpParam);
}
const wtWarpModulators = applyParameterModulators(ac, warpParam, t, endWithRelease, {
offset: value.warp,
amount: value.warpenv,
defaultAmount: 0.5,
shape: 'linear',
values: warpADSRParams,
holdEnd,
defaultValues: [0, 0.5, 0, 0.1],
}, {
frequency: warprate,
depth: value.warpdepth,
defaultDepth: 0.5,
shape: value.warpshape,
skew: value.warpskew,
dcoffset: value.warpdc ?? 0,
});
const vibratoOscillator = getVibratoOscillator(source.detune, value, t);
const envGain = ac.createGain();
const node = source.connect(envGain);
getParamADSR(node.gain, attack, decay, sustain, release, 0, 1, t, holdEnd, 'linear');
getPitchEnvelope(source.detune, value, t, holdEnd);
getPitchEnvelope(source.parameters.get('detune'), value, t, holdEnd);
const handle = { node, source };
const timeoutNode = webAudioTimeout(
ac,
@@ -318,8 +320,8 @@ export async function onTriggerSynth(t, value, onended, tables, cps, frameLen) {
destroyAudioWorkletNode(source);
vibratoOscillator?.stop();
node.disconnect();
posLFO?.disconnect();
warpLFO?.disconnect();
wtPosModulators?.disconnect();
wtWarpModulators?.disconnect();
onended();
},
t,