From ebeaac3c5ff20efa2879bbd393a4dee4572da0c3 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Tue, 29 Aug 2023 12:54:17 +0200 Subject: [PATCH 01/20] adding fm wave choice --- packages/superdough/synth.mjs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 57317133..ee863192 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -2,7 +2,8 @@ import { midiToFreq, noteToMidi } from './util.mjs'; import { registerSound, getAudioContext } from './superdough.mjs'; import { getOscillator, gainNode, getEnvelope } from './helpers.mjs'; -const mod = (freq, range = 1, type = 'sine') => { +const mod = (freq, range = 1, type) => { + console.log(type) const ctx = getAudioContext(); const osc = ctx.createOscillator(); osc.type = type; @@ -13,7 +14,7 @@ const mod = (freq, range = 1, type = 'sine') => { return { node: g, stop: (t) => osc.stop(t) }; }; -const fm = (osc, harmonicityRatio, modulationIndex, wave = 'sine') => { +const fm = (osc, harmonicityRatio, modulationIndex, wave) => { const carrfreq = osc.frequency.value; const modfreq = carrfreq * harmonicityRatio; const modgain = modfreq * modulationIndex; @@ -33,6 +34,7 @@ export function registerSynthSounds() { release = 0.01, fmh: fmHarmonicity = 1, fmi: fmModulationIndex, + fmwave: fmWaveform = 'sine' } = value; let { n, note, freq } = value; // with synths, n and note are the same thing @@ -50,7 +52,11 @@ export function registerSynthSounds() { let stopFm; if (fmModulationIndex) { - const { node: modulator, stop } = fm(o, fmHarmonicity, fmModulationIndex); + const { node: modulator, stop } = fm( + o, fmHarmonicity, + fmModulationIndex, + fmWaveform + ); modulator.connect(o.frequency); stopFm = stop; } From 94bcfb11deb30e15f8712c622bae983540ee6fad Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Tue, 29 Aug 2023 13:16:39 +0200 Subject: [PATCH 02/20] removing rogue console.log --- packages/superdough/synth.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index ee863192..4ef9ddb8 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -3,7 +3,6 @@ import { registerSound, getAudioContext } from './superdough.mjs'; import { getOscillator, gainNode, getEnvelope } from './helpers.mjs'; const mod = (freq, range = 1, type) => { - console.log(type) const ctx = getAudioContext(); const osc = ctx.createOscillator(); osc.type = type; From 8fc7688585cc8d95d1d5285ee20e2247bb72aa98 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Tue, 29 Aug 2023 13:46:43 +0200 Subject: [PATCH 03/20] envelopes on fmsynth --- packages/superdough/helpers.mjs | 13 +++++++++ packages/superdough/synth.mjs | 52 ++++++++++++++++++++++++++++----- 2 files changed, 58 insertions(+), 7 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..fe389954 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -1,6 +1,6 @@ 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 mod = (freq, range = 1, type) => { const ctx = getAudioContext(); @@ -13,10 +13,38 @@ const mod = (freq, range = 1, type) => { return { node: g, stop: (t) => osc.stop(t) }; }; -const fm = (osc, harmonicityRatio, modulationIndex, wave) => { +const fm = (t, + osc, + harmonicityRatio, + harmonicityAttack, + harmonicityDecay, + modulationIndex, + modulationAttack, + modulationDecay, + wave, ) => { + const carrfreq = osc.frequency.value; - const modfreq = carrfreq * harmonicityRatio; - const modgain = modfreq * modulationIndex; + 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); }; @@ -32,8 +60,12 @@ export function registerSynthSounds() { sustain = 0.6, release = 0.01, fmh: fmHarmonicity = 1, + fmhattack: fmHarmonicityAttack = null, + fmhdecay: fmHarmonicityDecay = null, fmi: fmModulationIndex, - fmwave: fmWaveform = 'sine' + fmiattack: fmModulationAttack = null, + fmidecay: fmModulationDecay = null, + fmwave: fmWaveform = 'sine', } = value; let { n, note, freq } = value; // with synths, n and note are the same thing @@ -52,9 +84,15 @@ export function registerSynthSounds() { let stopFm; if (fmModulationIndex) { const { node: modulator, stop } = fm( - o, fmHarmonicity, + t, + o, + fmHarmonicity, + fmHarmonicityAttack, + fmHarmonicityDecay, fmModulationIndex, - fmWaveform + fmModulationAttack, + fmModulationDecay, + fmWaveform, ); modulator.connect(o.frequency); stopFm = stop; From c87c3c6672ee55f59958850e0391821f5303d89a Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Tue, 29 Aug 2023 16:31:47 +0200 Subject: [PATCH 04/20] Revert "envelopes on fmsynth" This reverts commit 8fc7688585cc8d95d1d5285ee20e2247bb72aa98. --- packages/superdough/helpers.mjs | 13 --------- packages/superdough/synth.mjs | 52 +++++---------------------------- 2 files changed, 7 insertions(+), 58 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index e07f0d7d..7cc54c8d 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -39,19 +39,6 @@ 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 fe389954..4ef9ddb8 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -1,6 +1,6 @@ import { midiToFreq, noteToMidi } from './util.mjs'; import { registerSound, getAudioContext } from './superdough.mjs'; -import { getOscillator, gainNode, getEnvelope, getExpEnvelope } from './helpers.mjs'; +import { getOscillator, gainNode, getEnvelope } from './helpers.mjs'; const mod = (freq, range = 1, type) => { const ctx = getAudioContext(); @@ -13,38 +13,10 @@ const mod = (freq, range = 1, type) => { return { node: g, stop: (t) => osc.stop(t) }; }; -const fm = (t, - osc, - harmonicityRatio, - harmonicityAttack, - harmonicityDecay, - modulationIndex, - modulationAttack, - modulationDecay, - wave, ) => { - +const fm = (osc, harmonicityRatio, modulationIndex, 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; - } - + const modfreq = carrfreq * harmonicityRatio; + const modgain = modfreq * modulationIndex; return mod(modfreq, modgain, wave); }; @@ -60,12 +32,8 @@ export function registerSynthSounds() { 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', + fmwave: fmWaveform = 'sine' } = value; let { n, note, freq } = value; // with synths, n and note are the same thing @@ -84,15 +52,9 @@ export function registerSynthSounds() { let stopFm; if (fmModulationIndex) { const { node: modulator, stop } = fm( - t, - o, - fmHarmonicity, - fmHarmonicityAttack, - fmHarmonicityDecay, + o, fmHarmonicity, fmModulationIndex, - fmModulationAttack, - fmModulationDecay, - fmWaveform, + fmWaveform ); modulator.connect(o.frequency); stopFm = stop; From c3cd5d08c9d2e315890b4b215fdce9039ceb7379 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Tue, 29 Aug 2023 17:25:27 +0200 Subject: [PATCH 05/20] 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); From c625096f5993b7c92a84f4be89be7ce5966cc156 Mon Sep 17 00:00:00 2001 From: Raphael Forment Date: Tue, 29 Aug 2023 17:29:50 +0200 Subject: [PATCH 06/20] removing comments --- packages/superdough/synth.mjs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index d8f9890f..6b9ef751 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -3,10 +3,6 @@ import { registerSound, getAudioContext } from './superdough.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; @@ -14,9 +10,6 @@ const fm = (osc, harmonicityRatio, modulationIndex, wave) => { }; const mod = (freq, range = 1, type) => { - /* - * This function creates an oscillator - */ const ctx = getAudioContext(); const osc = ctx.createOscillator(); osc.type = type; From b1b084452c6bd70d65c8c17fdbc27ec687da3fbd Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 03:56:31 +0200 Subject: [PATCH 07/20] fix: format --- packages/superdough/synth.mjs | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 6b9ef751..d8097745 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -20,7 +20,6 @@ const mod = (freq, range = 1, type) => { return { node: g, stop: (t) => osc.stop(t) }; }; - export function registerSynthSounds() { ['sine', 'square', 'triangle', 'sawtooth'].forEach((wave) => { registerSound( @@ -34,13 +33,13 @@ 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' + 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; // with synths, n and note are the same thing @@ -58,14 +57,14 @@ export function registerSynthSounds() { let stopFm, fmEnvelope; if (fmModulationIndex) { - 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); + 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; } From ee0aaca2b0725c05b111f9f42df57d2a63d7fdce Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 04:13:41 +0200 Subject: [PATCH 08/20] only use fm envelope when needed --- packages/core/controls.mjs | 7 ++++++ packages/superdough/synth.mjs | 44 ++++++++++++++++++++++++----------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index b8edd51c..9901f974 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -134,6 +134,13 @@ const generic_params = [ * */ [['fmi', 'fmh'], 'fm'], + // fm envelope + ['fmenv'], + ['fmattack'], + ['fmdecay'], + ['fmsustain'], + ['fmrelease'], + ['fmvelocity'], /** * Select the sound bank to use. To be used together with `s`. The bank name (+ "_") will be prepended to the value of `s`. diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index d8097745..dbaab5e4 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -26,7 +26,7 @@ export function registerSynthSounds() { wave, (t, value, onended) => { // destructure adsr here, because the default should be different for synths and samples - const { + let { attack = 0.001, decay = 0.05, sustain = 0.6, @@ -34,11 +34,11 @@ export function registerSynthSounds() { 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, + 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; @@ -55,22 +55,37 @@ export function registerSynthSounds() { // make oscillator const { node: o, stop } = getOscillator({ t, s: wave, freq }); + // FM + FM envelope let stopFm, fmEnvelope; if (fmModulationIndex) { 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; + if ([fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { + // no envelope by default + modulator.connect(o.frequency); + } else { + fmAttack = fmAttack ?? 0.001; + fmDecay = fmDecay ?? 0.001; + fmSustain = fmSustain ?? 1; + fmRelease = fmRelease ?? 0.001; + fmVelocity = fmVelocity ?? 1; + 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); } - modulator.connect(fmEnvelope.node); - fmEnvelope.node.connect(o.frequency); stopFm = stop; } + + // turn down const g = gainNode(0.3); - // envelope + + // gain envelope const { node: envelope, stop: releaseEnvelope } = getEnvelope(attack, decay, sustain, release, 1, t); + o.onended = () => { o.disconnect(); g.disconnect(); @@ -80,6 +95,7 @@ export function registerSynthSounds() { node: o.connect(g).connect(envelope), stop: (releaseTime) => { releaseEnvelope(releaseTime); + fmEnvelope?.stop(releaseTime); let end = releaseTime + release; stop(end); stopFm?.(end); From 7cf7ebc555ae0d3f53a9b4b42b17d12dedd2581f Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 04:17:49 +0200 Subject: [PATCH 09/20] fix: condition flip --- packages/superdough/synth.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index dbaab5e4..6095d881 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -59,7 +59,7 @@ export function registerSynthSounds() { let stopFm, fmEnvelope; if (fmModulationIndex) { const { node: modulator, stop } = fm(o, fmHarmonicity, fmModulationIndex, fmWaveform); - if ([fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { + if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { // no envelope by default modulator.connect(o.frequency); } else { From 6bcf8b91cc60ecc157f4afdbf06db97f693c577b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 04:22:54 +0200 Subject: [PATCH 10/20] fix: getExpEnvelope full adsr --- packages/superdough/helpers.mjs | 7 ++++--- packages/superdough/synth.mjs | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index e07f0d7d..eb05c4cb 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -39,15 +39,16 @@ export const getEnvelope = (attack, decay, sustain, release, velocity, begin) => }; }; -export const getExpEnvelope = (attack, decay, velocity, begin) => { +export const getExpEnvelope = (attack, decay, sustain, release, 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); + gainNode.gain.exponentialRampToValueAtTime(sustain * velocity, begin + attack + decay); return { node: gainNode, stop: (t) => { - gainNode.gain.exponentialRampToValueAtTime(0.0001, t + decay); + // similar to getEnvelope, this will glitch if sustain level has not been reached + gainNode.gain.exponentialRampToValueAtTime(0.0001, t + release); }, }; }; diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 6095d881..1e9ea114 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -70,7 +70,7 @@ export function registerSynthSounds() { fmVelocity = fmVelocity ?? 1; fmEnvelope = getEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); if (fmEnvelopeType === 'exp') { - fmEnvelope = getExpEnvelope(fmAttack, fmDecay, fmVelocity, t); + fmEnvelope = getExpEnvelope(fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity, t); fmEnvelope.node.maxValue = fmModulationIndex * 2; fmEnvelope.node.minValue = 0.00001; } From 88b5b9b21ceaa9377f3ded8f5698f8c648dbf348 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 04:24:44 +0200 Subject: [PATCH 11/20] fix: history --- packages/superdough/synth.mjs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 1e9ea114..c1e31973 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -2,13 +2,6 @@ import { midiToFreq, noteToMidi } from './util.mjs'; import { registerSound, getAudioContext } from './superdough.mjs'; import { getOscillator, gainNode, getEnvelope, getExpEnvelope } from './helpers.mjs'; -const fm = (osc, harmonicityRatio, modulationIndex, wave) => { - const carrfreq = osc.frequency.value; - const modfreq = carrfreq * harmonicityRatio; - const modgain = modfreq * modulationIndex; - return mod(modfreq, modgain, wave); -}; - const mod = (freq, range = 1, type) => { const ctx = getAudioContext(); const osc = ctx.createOscillator(); @@ -20,6 +13,13 @@ 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) => { registerSound( From 6ca89cb90741238a42bcc84f5c0c55ce876dc61b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 04:25:12 +0200 Subject: [PATCH 12/20] keep sine as mod default --- packages/superdough/synth.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index c1e31973..ba14e16b 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -2,7 +2,7 @@ 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 mod = (freq, range = 1, type = 'sine') => { const ctx = getAudioContext(); const osc = ctx.createOscillator(); osc.type = type; From 3057773009e9aeeb95efc269fa3dac0d30e34661 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 04:25:40 +0200 Subject: [PATCH 13/20] keep sine as fm default --- packages/superdough/synth.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index ba14e16b..592ecb2e 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -13,7 +13,7 @@ const mod = (freq, range = 1, type = 'sine') => { return { node: g, stop: (t) => osc.stop(t) }; }; -const fm = (osc, harmonicityRatio, modulationIndex, wave) => { +const fm = (osc, harmonicityRatio, modulationIndex, wave = 'sine') => { const carrfreq = osc.frequency.value; const modfreq = carrfreq * harmonicityRatio; const modgain = modfreq * modulationIndex; From 26d7a32f111b3dfa057380b321af464cd80e0b02 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 04:26:14 +0200 Subject: [PATCH 14/20] remove comments --- packages/superdough/synth.mjs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 592ecb2e..c6773350 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -34,11 +34,11 @@ export function registerSynthSounds() { 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, + fmattack: fmAttack, + fmdecay: fmDecay, + fmsustain: fmSustain, + fmrelease: fmRelease, + fmvelocity: fmVelocity, fmwave: fmWaveform = 'sine', } = value; let { n, note, freq } = value; From 876f97e9a222e89b1e3f3391214f37e06f8dc6e6 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 04:35:35 +0200 Subject: [PATCH 15/20] fix: exp envelope allow 0 without blowup --- packages/superdough/helpers.mjs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index eb05c4cb..c97c06dc 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -40,6 +40,8 @@ export const getEnvelope = (attack, decay, sustain, release, velocity, begin) => }; export const getExpEnvelope = (attack, decay, sustain, release, velocity, begin) => { + sustain = Math.max(0.001, sustain); + velocity = Math.max(0.001, velocity); const gainNode = getAudioContext().createGain(); gainNode.gain.setValueAtTime(0.0001, begin); gainNode.gain.exponentialRampToValueAtTime(velocity, begin + attack); From 66c895ee3c0ba69fe5c77e2061ca2eb77f9c9b85 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 11:52:49 +0200 Subject: [PATCH 16/20] fix: bad import --- packages/superdough/synth.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 80909f0b..9756ced0 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -1,6 +1,6 @@ import { midiToFreq, noteToMidi } from './util.mjs'; import { registerSound, getAudioContext } from './superdough.mjs'; -import { getOscillator, gainNode, getEnvelope, getExpEnvelope } from './helpers.mjs'; +import { gainNode, getEnvelope, getExpEnvelope } from './helpers.mjs'; const mod = (freq, range = 1, type = 'sine') => { const ctx = getAudioContext(); From b48fd78cf079f56336f218ab455714459fbc9a50 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 12:29:29 +0200 Subject: [PATCH 17/20] fm envelope docs --- packages/core/controls.mjs | 56 ++++++++++++++++++++++++++++-- packages/superdough/synth.mjs | 2 +- website/src/pages/learn/synths.mdx | 16 +++++++++ 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 8dba27b8..63ade17b 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -118,7 +118,9 @@ const generic_params = [ * @name fmh * @param {number | Pattern} harmonicity * @example - * note("c e g b").fm(4).fmh("<1 2 1.5 1.61>") + * note("c e g b") + * .fm(4) + * .fmh("<1 2 1.5 1.61>") * */ [['fmh', 'fmi'], 'fmh'], @@ -130,15 +132,65 @@ const generic_params = [ * @param {number | Pattern} brightness modulation index * @synonyms fmi * @example - * note("c e g b").fm("<0 1 2 8 32>") + * note("c e g b") + * .fm("<0 1 2 8 32>") * */ [['fmi', 'fmh'], 'fm'], // fm envelope + /** + * Ramp type of fm envelope. Exp might be a bit broken.. + * + * @name fmenv + * @param {number | Pattern} type lin | exp + * @example + * note("c e g b") + * .fm(4) + * .fmdecay(.2) + * .fmsustain(.4) + * .fmenv("") + * + */ ['fmenv'], + /** + * Attack time for the FM envelope: time it takes to reach maximum modulation + * + * @name fmattack + * @param {number | Pattern} time attack time + * @example + * note("c e g b") + * .fm(4) + * .fmattack("<0 .05 .1 .2>") + * + */ ['fmattack'], + /** + * Decay time for the FM envelope: seconds until the sustain level is reached after the attack phase. + * + * @name fmdecay + * @param {number | Pattern} time decay time + * @example + * note("c e g b") + * .fm(4) + * .fmdecay("<.01 .05 .1 .2>") + * .fmsustain(.4) + * + */ ['fmdecay'], + /** + * Sustain level for the FM envelope: how much modulation is applied after the decay phase + * + * @name fmsustain + * @param {number | Pattern} level sustain level + * @example + * note("c e g b") + * .fm(4) + * .fmdecay(.1) + * .fmsustain("<1 .75 .5 0>") + * + */ ['fmsustain'], + // these are not really useful... skipping for now ['fmrelease'], ['fmvelocity'], diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 9756ced0..a409d990 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -33,7 +33,7 @@ export function registerSynthSounds() { release = 0.01, fmh: fmHarmonicity = 1, fmi: fmModulationIndex, - fmenv: fmEnvelopeType = 'linear', + fmenv: fmEnvelopeType = 'lin', fmattack: fmAttack, fmdecay: fmDecay, fmsustain: fmSustain, diff --git a/website/src/pages/learn/synths.mdx b/website/src/pages/learn/synths.mdx index 6ebf2613..9c515752 100644 --- a/website/src/pages/learn/synths.mdx +++ b/website/src/pages/learn/synths.mdx @@ -38,4 +38,20 @@ Now we not only pattern the notes, but the sound as well! +### fmattack + + + +### fmdecay + + + +### fmsustain + + + +### fmenv + + + Next up: [Audio Effects](/learn/effects)... From a7246c65185e459dcd04dcfbbefc6495376cf664 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 12:30:01 +0200 Subject: [PATCH 18/20] snapshots --- test/__snapshots__/examples.test.mjs.snap | 84 +++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index ee937e2b..a94d32b3 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1825,6 +1825,69 @@ exports[`runs examples > example "fm" example index 0 1`] = ` ] `; +exports[`runs examples > example "fmattack" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | note:c fmi:4 fmattack:0 ]", + "[ 1/4 → 1/2 | note:e fmi:4 fmattack:0 ]", + "[ 1/2 → 3/4 | note:g fmi:4 fmattack:0 ]", + "[ 3/4 → 1/1 | note:b fmi:4 fmattack:0 ]", + "[ 1/1 → 5/4 | note:c fmi:4 fmattack:0.05 ]", + "[ 5/4 → 3/2 | note:e fmi:4 fmattack:0.05 ]", + "[ 3/2 → 7/4 | note:g fmi:4 fmattack:0.05 ]", + "[ 7/4 → 2/1 | note:b fmi:4 fmattack:0.05 ]", + "[ 2/1 → 9/4 | note:c fmi:4 fmattack:0.1 ]", + "[ 9/4 → 5/2 | note:e fmi:4 fmattack:0.1 ]", + "[ 5/2 → 11/4 | note:g fmi:4 fmattack:0.1 ]", + "[ 11/4 → 3/1 | note:b fmi:4 fmattack:0.1 ]", + "[ 3/1 → 13/4 | note:c fmi:4 fmattack:0.2 ]", + "[ 13/4 → 7/2 | note:e fmi:4 fmattack:0.2 ]", + "[ 7/2 → 15/4 | note:g fmi:4 fmattack:0.2 ]", + "[ 15/4 → 4/1 | note:b fmi:4 fmattack:0.2 ]", +] +`; + +exports[`runs examples > example "fmdecay" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | note:c fmi:4 fmdecay:0.01 fmsustain:0.4 ]", + "[ 1/4 → 1/2 | note:e fmi:4 fmdecay:0.01 fmsustain:0.4 ]", + "[ 1/2 → 3/4 | note:g fmi:4 fmdecay:0.01 fmsustain:0.4 ]", + "[ 3/4 → 1/1 | note:b fmi:4 fmdecay:0.01 fmsustain:0.4 ]", + "[ 1/1 → 5/4 | note:c fmi:4 fmdecay:0.05 fmsustain:0.4 ]", + "[ 5/4 → 3/2 | note:e fmi:4 fmdecay:0.05 fmsustain:0.4 ]", + "[ 3/2 → 7/4 | note:g fmi:4 fmdecay:0.05 fmsustain:0.4 ]", + "[ 7/4 → 2/1 | note:b fmi:4 fmdecay:0.05 fmsustain:0.4 ]", + "[ 2/1 → 9/4 | note:c fmi:4 fmdecay:0.1 fmsustain:0.4 ]", + "[ 9/4 → 5/2 | note:e fmi:4 fmdecay:0.1 fmsustain:0.4 ]", + "[ 5/2 → 11/4 | note:g fmi:4 fmdecay:0.1 fmsustain:0.4 ]", + "[ 11/4 → 3/1 | note:b fmi:4 fmdecay:0.1 fmsustain:0.4 ]", + "[ 3/1 → 13/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0.4 ]", + "[ 13/4 → 7/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 ]", + "[ 7/2 → 15/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 ]", + "[ 15/4 → 4/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0.4 ]", +] +`; + +exports[`runs examples > example "fmenv" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", + "[ 1/4 → 1/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", + "[ 1/2 → 3/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", + "[ 3/4 → 1/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", + "[ 1/1 → 5/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", + "[ 5/4 → 3/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", + "[ 3/2 → 7/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", + "[ 7/4 → 2/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", + "[ 2/1 → 9/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", + "[ 9/4 → 5/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", + "[ 5/2 → 11/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", + "[ 11/4 → 3/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", + "[ 3/1 → 13/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", + "[ 13/4 → 7/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", + "[ 7/2 → 15/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", + "[ 15/4 → 4/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", +] +`; + exports[`runs examples > example "fmh" example index 0 1`] = ` [ "[ 0/1 → 1/4 | note:c fmi:4 fmh:1 ]", @@ -1846,6 +1909,27 @@ exports[`runs examples > example "fmh" example index 0 1`] = ` ] `; +exports[`runs examples > example "fmsustain" example index 0 1`] = ` +[ + "[ 0/1 → 1/4 | note:c fmi:4 fmdecay:0.1 fmsustain:1 ]", + "[ 1/4 → 1/2 | note:e fmi:4 fmdecay:0.1 fmsustain:1 ]", + "[ 1/2 → 3/4 | note:g fmi:4 fmdecay:0.1 fmsustain:1 ]", + "[ 3/4 → 1/1 | note:b fmi:4 fmdecay:0.1 fmsustain:1 ]", + "[ 1/1 → 5/4 | note:c fmi:4 fmdecay:0.1 fmsustain:0.75 ]", + "[ 5/4 → 3/2 | note:e fmi:4 fmdecay:0.1 fmsustain:0.75 ]", + "[ 3/2 → 7/4 | note:g fmi:4 fmdecay:0.1 fmsustain:0.75 ]", + "[ 7/4 → 2/1 | note:b fmi:4 fmdecay:0.1 fmsustain:0.75 ]", + "[ 2/1 → 9/4 | note:c fmi:4 fmdecay:0.1 fmsustain:0.5 ]", + "[ 9/4 → 5/2 | note:e fmi:4 fmdecay:0.1 fmsustain:0.5 ]", + "[ 5/2 → 11/4 | note:g fmi:4 fmdecay:0.1 fmsustain:0.5 ]", + "[ 11/4 → 3/1 | note:b fmi:4 fmdecay:0.1 fmsustain:0.5 ]", + "[ 3/1 → 13/4 | note:c fmi:4 fmdecay:0.1 fmsustain:0 ]", + "[ 13/4 → 7/2 | note:e fmi:4 fmdecay:0.1 fmsustain:0 ]", + "[ 7/2 → 15/4 | note:g fmi:4 fmdecay:0.1 fmsustain:0 ]", + "[ 15/4 → 4/1 | note:b fmi:4 fmdecay:0.1 fmsustain:0 ]", +] +`; + exports[`runs examples > example "focus" example index 0 1`] = ` [ "[ 0/1 → 1/8 | s:sd ]", From 5b3cd166b0c0d356a8012aa112543ca1b04afce0 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 12:34:16 +0200 Subject: [PATCH 19/20] more clear example --- packages/core/controls.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 63ade17b..c6dbc614 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -147,7 +147,7 @@ const generic_params = [ * note("c e g b") * .fm(4) * .fmdecay(.2) - * .fmsustain(.4) + * .fmsustain(0) * .fmenv("") * */ From e29cf6fcc1b5f3e622830209d00fd333e4e0ad99 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Thu, 31 Aug 2023 12:34:49 +0200 Subject: [PATCH 20/20] update snapshot --- test/__snapshots__/examples.test.mjs.snap | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/test/__snapshots__/examples.test.mjs.snap b/test/__snapshots__/examples.test.mjs.snap index a94d32b3..787bc461 100644 --- a/test/__snapshots__/examples.test.mjs.snap +++ b/test/__snapshots__/examples.test.mjs.snap @@ -1869,22 +1869,22 @@ exports[`runs examples > example "fmdecay" example index 0 1`] = ` exports[`runs examples > example "fmenv" example index 0 1`] = ` [ - "[ 0/1 → 1/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", - "[ 1/4 → 1/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", - "[ 1/2 → 3/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", - "[ 3/4 → 1/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", - "[ 1/1 → 5/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", - "[ 5/4 → 3/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", - "[ 3/2 → 7/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", - "[ 7/4 → 2/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", - "[ 2/1 → 9/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", - "[ 9/4 → 5/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", - "[ 5/2 → 11/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", - "[ 11/4 → 3/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:exp ]", - "[ 3/1 → 13/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", - "[ 13/4 → 7/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", - "[ 7/2 → 15/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", - "[ 15/4 → 4/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0.4 fmenv:lin ]", + "[ 0/1 → 1/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]", + "[ 1/4 → 1/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]", + "[ 1/2 → 3/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]", + "[ 3/4 → 1/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]", + "[ 1/1 → 5/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]", + "[ 5/4 → 3/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]", + "[ 3/2 → 7/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]", + "[ 7/4 → 2/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]", + "[ 2/1 → 9/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]", + "[ 9/4 → 5/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]", + "[ 5/2 → 11/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]", + "[ 11/4 → 3/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:exp ]", + "[ 3/1 → 13/4 | note:c fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]", + "[ 13/4 → 7/2 | note:e fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]", + "[ 7/2 → 15/4 | note:g fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]", + "[ 15/4 → 4/1 | note:b fmi:4 fmdecay:0.2 fmsustain:0 fmenv:lin ]", ] `;