From c0b00339c5f7d9f82c7468ac8c0f0fd78132d30c Mon Sep 17 00:00:00 2001 From: "Jade (Rose) Rowland" Date: Tue, 5 Mar 2024 00:23:19 -0500 Subject: [PATCH] implemented modulators --- packages/superdough/helpers.mjs | 7 +- packages/superdough/synth.mjs | 138 ++----------------------------- packages/superdough/worklets.mjs | 1 + 3 files changed, 9 insertions(+), 137 deletions(-) diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 01fc9bf2..8643e3e4 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -202,7 +202,7 @@ const fm = (frequencyparam, harmonicityRatio, modulationIndex, wave = 'sine') => const modgain = modfreq * modulationIndex; return mod(modfreq, modgain, wave); }; -export function applyFM(param, value) { +export function applyFM(param, value, begin) { const { fmh: fmHarmonicity = 1, fmi: fmModulationIndex, @@ -214,16 +214,15 @@ export function applyFM(param, value) { fmvelocity: fmVelocity, fmwave: fmWaveform = 'sine', duration, - begin, } = value; - const ac = getAudioContext(); if (fmModulationIndex) { + const ac = getAudioContext(); const envGain = ac.createGain(); const { node: modulator } = fm(param, fmHarmonicity, fmModulationIndex, fmWaveform); if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { // no envelope by default - modulator.connect(param.frequency); + modulator.connect(param); } else { const [attack, decay, sustain, release] = getADSRValues([fmAttack, fmDecay, fmSustain, fmRelease]); const holdEnd = begin + duration; diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 558112ba..1ec74f97 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -3,24 +3,6 @@ import { registerSound, getAudioContext, getWorklet } from './superdough.mjs'; import { applyFM, gainNode, getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs'; import { getNoiseMix, getNoiseOscillator } from './noise.mjs'; -const mod = (freq, range = 1, type = 'sine') => { - 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 = (osc, harmonicityRatio, modulationIndex, wave = 'sine') => { - const carrfreq = osc.frequency.value; - const modfreq = carrfreq * harmonicityRatio; - const modgain = modfreq * modulationIndex; - return mod(modfreq, modgain, wave); -}; - const getFrequencyFromValue = (value) => { let { note, freq } = value; note = note || 36; @@ -38,59 +20,6 @@ const getFrequencyFromValue = (value) => { const waveforms = ['triangle', 'square', 'sawtooth', 'sine']; const noises = ['pink', 'white', 'brown', 'crackle']; -const applyModulators = (node, value) => { - let { - n: partials, - note, - freq, - noise = 0, - // fm - fmh: fmHarmonicity = 1, - fmi: fmModulationIndex, - fmenv: fmEnvelopeType = 'exp', - fmattack: fmAttack, - fmdecay: fmDecay, - fmsustain: fmSustain, - fmrelease: fmRelease, - fmvelocity: fmVelocity, - fmwave: fmWaveform = 'sine', - duration, - begin: t, - } = value; - - const ac = getAudioContext(); - if (fmModulationIndex) { - let envGain = ac.createGain(); - const { node: modulator, stop } = fm(node, fmHarmonicity, fmModulationIndex, fmWaveform); - if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { - // no envelope by default - modulator.connect(node.frequency); - } else { - const [attack, decay, sustain, release] = getADSRValues([fmAttack, fmDecay, fmSustain, fmRelease]); - const holdEnd = t + duration; - getParamADSR( - envGain.gain, - attack, - decay, - sustain, - release, - 0, - 1, - t, - holdEnd, - fmEnvelopeType === 'exp' ? 'exponential' : 'linear', - ); - modulator.connect(envGain); - envGain.connect(node.frequency); - } - } - - getVibratoOscillator(node.detune, value, t); - getPitchEnvelope(node.detune, value, t, t + duration); - - return node; -}; - export function registerSynthSounds() { [...waveforms].forEach((s) => { registerSound( @@ -166,7 +95,7 @@ export function registerSynthSounds() { // console.log(node.parameters.get('frequency')); getPitchEnvelope(node.parameters.get('detune'), value, begin, holdend); getVibratoOscillator(node.parameters.get('detune'), value, begin); - // applyFM(node.parameters.get('frequency')); + applyFM(node.parameters.get('frequency'), value, begin); const envGain = gainNode(1); node = node.connect(envGain); @@ -260,24 +189,7 @@ export function waveformN(partials, type) { // expects one of waveforms as s export function getOscillator(s, t, value) { - let { - n: partials, - note, - freq, - noise = 0, - // fm - fmh: fmHarmonicity = 1, - fmi: fmModulationIndex, - fmenv: fmEnvelopeType = 'exp', - fmattack: fmAttack, - fmdecay: fmDecay, - fmsustain: fmSustain, - fmrelease: fmRelease, - fmvelocity: fmVelocity, - fmwave: fmWaveform = 'sine', - duration, - } = value; - let ac = getAudioContext(); + let { n: partials, duration, noise = 0 } = value; let o; // If no partials are given, use stock waveforms if (!partials || s === 'sine') { @@ -288,55 +200,15 @@ export function getOscillator(s, t, value) { else { o = waveformN(partials, s); } - - // get frequency from note... - note = note || 36; - if (typeof note === 'string') { - note = noteToMidi(note); // e.g. c3 => 48 - } - // get frequency - if (!freq && typeof note === 'number') { - freq = midiToFreq(note); // + 48); - } - // set frequency - o.frequency.value = Number(freq); + o.frequency.value = getFrequencyFromValue(value); o.start(t); - // FM - let stopFm; - let envGain = ac.createGain(); - if (fmModulationIndex) { - const { node: modulator, stop } = fm(o, fmHarmonicity, fmModulationIndex, fmWaveform); - if (![fmAttack, fmDecay, fmSustain, fmRelease, fmVelocity].find((v) => v !== undefined)) { - // no envelope by default - modulator.connect(o.frequency); - } else { - const [attack, decay, sustain, release] = getADSRValues([fmAttack, fmDecay, fmSustain, fmRelease]); - const holdEnd = t + duration; - getParamADSR( - envGain.gain, - attack, - decay, - sustain, - release, - 0, - 1, - t, - holdEnd, - fmEnvelopeType === 'exp' ? 'exponential' : 'linear', - ); - modulator.connect(envGain); - envGain.connect(o.frequency); - } - stopFm = stop; - } - - // Additional oscillator for vibrato effect let vibratoOscillator = getVibratoOscillator(o.detune, value, t); // pitch envelope getPitchEnvelope(o.detune, value, t, t + duration); + applyFM(o.frequency, value, t); let noiseMix; if (noise) { @@ -348,7 +220,7 @@ export function getOscillator(s, t, value) { stop: (time) => { vibratoOscillator?.stop(time); noiseMix?.stop(time); - stopFm?.(time); + // stopFm?.(time); o.stop(time); }, triggerRelease: (time) => { diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index ab8b9c07..2722c8eb 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -112,6 +112,7 @@ class DistortProcessor extends AudioWorkletProcessor { registerProcessor('distort-processor', DistortProcessor); // adjust waveshape to remove frequencies above nyquist to prevent aliasing +// referenced from https://www.kvraudio.com/forum/viewtopic.php?t=375517 const polyBlep = (phase, dt) => { // 0 <= phase < 1 if (phase < dt) {