Big matrix refactor

This commit is contained in:
Aria 2025-10-03 18:20:41 -05:00
parent c9381a11f9
commit 7cf9db4872
2 changed files with 55 additions and 71 deletions

View file

@ -650,7 +650,7 @@ export const {
// 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(`fm${i}${j}`); registerControl(`fmi${i}${j}`, `fm${i}${j}`);
} }
} }

View file

@ -309,7 +309,7 @@ export function webAudioTimeout(audioContext, onComplete, startTime, stopTime) {
constantNode.stop(stopTime); constantNode.stop(stopTime);
return constantNode; return constantNode;
} }
const mod = (freq, range = 1, type = 'sine') => { const mod = (freq, type = 'sine') => {
const ctx = getAudioContext(); const ctx = getAudioContext();
let osc; let osc;
if (noises.includes(type)) { if (noises.includes(type)) {
@ -321,99 +321,83 @@ const mod = (freq, range = 1, type = 'sine') => {
osc.type = type; osc.type = type;
osc.frequency.value = freq; osc.frequency.value = freq;
} }
osc.start(); osc.start();
const g = new GainNode(ctx, { gain: range }); return { osc, stop: (t) => osc.stop(t), freq };
osc.connect(g); // -range, range
return { fm: osc, scaled: g, stop: (t) => osc.stop(t) };
}; };
const fm = (frequencyparam, harmonicityRatio, modulationIndex, wave = 'sine') => { const fm = (frequencyparam, harmonicityRatio, modulationIndex, wave = 'sine') => {
const carrfreq = frequencyparam.value; const carrfreq = frequencyparam.value;
const modfreq = carrfreq * harmonicityRatio; const modfreq = carrfreq * harmonicityRatio;
const modgain = modfreq * modulationIndex; return mod(modfreq, wave)
return mod(modfreq, modgain, wave);
}; };
export function applyFM(param, value, begin) { export function applyFM(param, value, begin) {
const ac = getAudioContext(); const ac = getAudioContext();
let target = param;
let stop = (t) => {}; let stop = (t) => {};
const targets = [param]; const fms = {};
const modulators = [];
for (let i = 0; i < 8; i++) {
const iS = `${i === 0 ? '' : i}`;
let modulator;
const fmModulationIndex = value[`fmi${iS}`];
if (fmModulationIndex) {
const fmmod = fm(target, value[`fmh${iS}`] ?? 1, fmModulationIndex, value[`fmwave${iS}`] ?? 'sine');
modulator = fmmod.scaled;
const currStop = stop;
stop = (t) => {
currStop(t);
fmmod.stop(t);
};
const adsr = ['attack', 'decay', 'sustain', 'release'].map((s) => value[`fm${s}${iS}`]);
if (!adsr.some((v) => v !== undefined)) {
// no envelope by default
modulator.connect(target);
modulators.push(modulator);
} else {
const envGain = ac.createGain();
const [attack, decay, sustain, release] = getADSRValues(adsr);
const holdEnd = begin + value.duration;
const fmEnvelopeType = value[`fmenv${iS}`] ?? 'exp';
getParamADSR(
envGain.gain,
attack,
decay,
sustain,
release,
0,
1,
begin,
holdEnd,
fmEnvelopeType === 'exp' ? 'exponential' : 'linear',
);
modulator.connect(envGain);
envGain.connect(target);
modulators.push(envGain);
}
targets.push(fmmod.fm?.frequency);
if (targets[i]) {
// Cannot connect to noises, for example
target = targets[i];
}
}
}
// Matrix // Matrix
for (let i = 1; i <= 8; i++) { for (let i = 1; i <= 8; i++) {
for (let j = 0; j <= 8; j++) { for (let j = 0; j <= 8; j++) {
let control; let control;
if (i === j + 1) { if (i === j + 1) {
// Standard fm3 -> fm2 -> fm1 -> param usage // Standard fm3 -> fm2 -> fm1 -> param usage
control = `fm${i}`; const iS = i === 1 ? '' : i;
control = `fmi${iS}`;
} else { } else {
control = `fm${i}${j}`; control = `fmi${i}${j}`;
} }
const amt = value[control]; const amt = value[control];
if (!amt) continue; if (!amt) continue;
const source = modulators[i - 1]; let io = [];
const target = targets[j]; for (let [isMod, idx] of [[true, i], [false, j]]) {
if (!source) { debugger;
const iS = `${i === 1 ? '' : i}`; if (idx === 0) {
io.push(param);
continue;
}
if (!fms[idx]) {
const idxS = idx === 1 ? '' : idx;
const { osc, freq } = fm(param, value[`fmh${idxS}`] ?? 1, value[`fmwave${idxS}`] ?? 'sine');
const currStop = stop;
stop = (t) => {
currStop(t);
osc.stop(t);
};
const adsr = ['attack', 'decay', 'sustain', 'release'].map((s) => value[`fm${s}${idxS}`]);
if (!adsr.some((v) => v !== undefined)) {
fms[idx] = { input: osc.frequency, output: osc, freq};
} else {
const envGain = ac.createGain();
const [attack, decay, sustain, release] = getADSRValues(adsr);
const holdEnd = begin + value.duration;
const fmEnvelopeType = value[`fmenv${idxS}`] ?? 'exp';
getParamADSR(
envGain.gain,
attack,
decay,
sustain,
release,
0,
1,
begin,
holdEnd,
fmEnvelopeType === 'exp' ? 'exponential' : 'linear',
);
fms[idx] = { input: osc.frequency, output: osc.connect(envGain), freq};
}
}
const { input, output, freq } = fms[idx];
const g = gainNode(amt * freq);
io.push(isMod ? output.connect(g) : input);
}
if (!io[1]) {
logger( logger(
`[superdough] failed to connect FM from source ${i} (due to control ${control}): source ${i} does not exist. Please use control fm${iS} to set it up`, `[superdough] control ${control} failed to connect FM ${i} to target ${j} due to missing frequency parameter (likely because fm${j} is noise)`,
'warning', 'warning',
); );
continue; continue;
} }
if (!target) { io[0].connect(io[1]);
logger(
`[superdough] failed to connect FM to target ${j} (due to control ${control}): target ${j} does not exist. Please use control fm${j} to set it up`,
'warning',
);
continue;
}
source.connect(target);
} }
} }
return { stop }; return { stop };