From c3cd5d08c9d2e315890b4b215fdce9039ceb7379 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Tue, 29 Aug 2023 17:25:27 +0200 Subject: [PATCH] adding linear and exponential envelope for fm synthesis --- packages/superdough/helpers.mjs | 13 ++++++++++ packages/superdough/synth.mjs | 45 +++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 7cc54c8d..e07f0d7d 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -39,6 +39,19 @@ export const getEnvelope = (attack, decay, sustain, release, velocity, begin) => }; }; +export const getExpEnvelope = (attack, decay, velocity, begin) => { + const gainNode = getAudioContext().createGain(); + gainNode.gain.setValueAtTime(0.0001, begin); + gainNode.gain.exponentialRampToValueAtTime(velocity, begin + attack); + gainNode.gain.exponentialRampToValueAtTime(0.0001, begin + attack + decay); + return { + node: gainNode, + stop: (t) => { + gainNode.gain.exponentialRampToValueAtTime(0.0001, t + decay); + }, + }; +}; + export const getADSR = (attack, decay, sustain, release, velocity, begin, end) => { const gainNode = getAudioContext().createGain(); gainNode.gain.setValueAtTime(0, begin); diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 4ef9ddb8..d8f9890f 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -1,8 +1,22 @@ import { midiToFreq, noteToMidi } from './util.mjs'; import { registerSound, getAudioContext } from './superdough.mjs'; -import { getOscillator, gainNode, getEnvelope } from './helpers.mjs'; +import { getOscillator, gainNode, getEnvelope, getExpEnvelope } from './helpers.mjs'; + +const fm = (osc, harmonicityRatio, modulationIndex, wave) => { + /* + * This function helps to setup a modulator for FM Synthesis + * @returns a modulator + */ + const carrfreq = osc.frequency.value; + const modfreq = carrfreq * harmonicityRatio; + const modgain = modfreq * modulationIndex; + return mod(modfreq, modgain, wave); +}; const mod = (freq, range = 1, type) => { + /* + * This function creates an oscillator + */ const ctx = getAudioContext(); const osc = ctx.createOscillator(); osc.type = type; @@ -13,12 +27,6 @@ const mod = (freq, range = 1, type) => { return { node: g, stop: (t) => osc.stop(t) }; }; -const fm = (osc, harmonicityRatio, modulationIndex, wave) => { - const carrfreq = osc.frequency.value; - const modfreq = carrfreq * harmonicityRatio; - const modgain = modfreq * modulationIndex; - return mod(modfreq, modgain, wave); -}; export function registerSynthSounds() { ['sine', 'square', 'triangle', 'sawtooth'].forEach((wave) => { @@ -33,6 +41,12 @@ export function registerSynthSounds() { release = 0.01, fmh: fmHarmonicity = 1, fmi: fmModulationIndex, + fmenv: fmEnvelopeType = 'linear', + fmattack: fmAttack = 0.001, + fmdecay: fmDecay = 0.2, + fmsustain: fmSustain = 0.001, + fmrelease: fmRelease = 0.1, + fmvelocity: fmVelocity = 1, fmwave: fmWaveform = 'sine' } = value; let { n, note, freq } = value; @@ -49,14 +63,17 @@ export function registerSynthSounds() { // make oscillator const { node: o, stop } = getOscillator({ t, s: wave, freq }); - let stopFm; + let stopFm, fmEnvelope; if (fmModulationIndex) { - const { node: modulator, stop } = fm( - o, fmHarmonicity, - fmModulationIndex, - fmWaveform - ); - modulator.connect(o.frequency); + const { node: modulator, stop } = fm( o, fmHarmonicity, fmModulationIndex, fmWaveform); + fmEnvelope = getEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); + if (fmEnvelopeType === "exp") { + fmEnvelope = getExpEnvelope(fmAttack, fmDecay, fmVelocity, t); + fmEnvelope.node.maxValue = fmModulationIndex * 2; + fmEnvelope.node.minValue = 0.00001; + } + modulator.connect(fmEnvelope.node); + fmEnvelope.node.connect(o.frequency); stopFm = stop; } const g = gainNode(0.3);