2023-08-29 13:46:43 +02:00

122 lines
3.6 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { midiToFreq, noteToMidi } from './util.mjs';
import { registerSound, getAudioContext } from './superdough.mjs';
import { getOscillator, gainNode, getEnvelope, getExpEnvelope } from './helpers.mjs';
const mod = (freq, range = 1, type) => {
const ctx = getAudioContext();
const osc = ctx.createOscillator();
osc.type = type;
osc.frequency.value = freq;
osc.start();
const g = new GainNode(ctx, { gain: range });
osc.connect(g); // -range, range
return { node: g, stop: (t) => osc.stop(t) };
};
const fm = (t,
osc,
harmonicityRatio,
harmonicityAttack,
harmonicityDecay,
modulationIndex,
modulationAttack,
modulationDecay,
wave, ) => {
const carrfreq = osc.frequency.value;
let modfreq;
let modgain;
// Setting up envelopes if needed
if ((harmonicityAttack !== null) || (harmonicityDecay !== null)) {
let hattack = harmonicityAttack || 0.01;
let hdecay = harmonicityDecay || 0.5;
const harmonicEnv = getExpEnvelope(hattack, hdecay, 1, t);
modfreq = carrfreq * (harmonicityRatio * harmonicEnv);
} else {
modfreq = carrfreq * harmonicityRatio;
}
if ((modulationAttack !== null) || (modulationDecay !== null)) {
let mattack = modulationAttack || 0.01;
let mdecay = modulationDecay || 0.5;
const modulationEnv = getExpEnvelope(mattack, mdecay, 1, t);
modgain = modfreq * (modulationIndex * modulationEnv);
} else {
modgain = modfreq * modulationIndex;
}
return mod(modfreq, modgain, wave);
};
export function registerSynthSounds() {
['sine', 'square', 'triangle', 'sawtooth'].forEach((wave) => {
registerSound(
wave,
(t, value, onended) => {
// destructure adsr here, because the default should be different for synths and samples
const {
attack = 0.001,
decay = 0.05,
sustain = 0.6,
release = 0.01,
fmh: fmHarmonicity = 1,
fmhattack: fmHarmonicityAttack = null,
fmhdecay: fmHarmonicityDecay = null,
fmi: fmModulationIndex,
fmiattack: fmModulationAttack = null,
fmidecay: fmModulationDecay = null,
fmwave: fmWaveform = 'sine',
} = value;
let { n, note, freq } = value;
// with synths, n and note are the same thing
n = note || n || 36;
if (typeof n === 'string') {
n = noteToMidi(n); // e.g. c3 => 48
}
// get frequency
if (!freq && typeof n === 'number') {
freq = midiToFreq(n); // + 48);
}
// maybe pull out the above frequency resolution?? (there is also getFrequency but it has no default)
// make oscillator
const { node: o, stop } = getOscillator({ t, s: wave, freq });
let stopFm;
if (fmModulationIndex) {
const { node: modulator, stop } = fm(
t,
o,
fmHarmonicity,
fmHarmonicityAttack,
fmHarmonicityDecay,
fmModulationIndex,
fmModulationAttack,
fmModulationDecay,
fmWaveform,
);
modulator.connect(o.frequency);
stopFm = stop;
}
const g = gainNode(0.3);
// envelope
const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t);
o.onended = () => {
o.disconnect();
g.disconnect();
onended();
};
return {
node: o.connect(g).connect(envelope),
stop: (releaseTime) => {
releaseEnvelope(releaseTime);
let end = releaseTime + release;
stop(end);
stopFm?.(end);
},
};
},
{ type: 'synth', prebake: true },
);
});
}