diff --git a/packages/superdough/helpers.mjs b/packages/superdough/helpers.mjs index 889ca18b..f5f3ad68 100644 --- a/packages/superdough/helpers.mjs +++ b/packages/superdough/helpers.mjs @@ -186,6 +186,16 @@ export function getVibratoOscillator(param, value, t) { return vibratoOscillator; } } +// ConstantSource inherits AudioScheduledSourceNode, which has scheduling abilities +// a bit of a hack, but it works very well :) +export function webAudioTimeout(audioContext, onComplete, startTime, stopTime) { + const constantNode = audioContext.createConstantSource(); + constantNode.start(startTime); + constantNode.stop(stopTime); + constantNode.onended = () => { + onComplete(); + }; +} const mod = (freq, range = 1, type = 'sine') => { const ctx = getAudioContext(); const osc = ctx.createOscillator(); diff --git a/packages/superdough/synth.mjs b/packages/superdough/synth.mjs index 0df098e1..bd82cd1a 100644 --- a/packages/superdough/synth.mjs +++ b/packages/superdough/synth.mjs @@ -1,6 +1,14 @@ import { clamp, midiToFreq, noteToMidi } from './util.mjs'; import { registerSound, getAudioContext, getWorklet } from './superdough.mjs'; -import { applyFM, gainNode, getADSRValues, getParamADSR, getPitchEnvelope, getVibratoOscillator } from './helpers.mjs'; +import { + applyFM, + gainNode, + getADSRValues, + getParamADSR, + getPitchEnvelope, + getVibratoOscillator, + webAudioTimeout, +} from './helpers.mjs'; import { getNoiseMix, getNoiseOscillator } from './noise.mjs'; const getFrequencyFromValue = (value) => { @@ -78,7 +86,7 @@ export function registerSynthSounds() { const end = holdend + release + 0.01; const voices = clamp(unison, 1, 100); - let node = getWorklet( + let o = getWorklet( ac, 'supersaw-oscillator', { @@ -93,20 +101,32 @@ export function registerSynthSounds() { outputChannelCount: [2], }, ); - const gainAdjustment = 1 / Math.sqrt(voices); - getPitchEnvelope(node.parameters.get('detune'), value, begin, holdend); - getVibratoOscillator(node.parameters.get('detune'), value, begin); - applyFM(node.parameters.get('frequency'), value, begin); - const envGain = gainNode(1); - node = node.connect(envGain); - getParamADSR(node.gain, attack, decay, sustain, release, 0, 0.3 * gainAdjustment, begin, holdend, 'linear'); + const gainAdjustment = 1 / Math.sqrt(voices); + getPitchEnvelope(o.parameters.get('detune'), value, begin, holdend); + const vibratoOscillator = getVibratoOscillator(o.parameters.get('detune'), value, begin); + const fm = applyFM(o.parameters.get('frequency'), value, begin); + let envGain = gainNode(1); + envGain = o.connect(envGain); + + webAudioTimeout( + ac, + () => { + o.disconnect(); + envGain.disconnect(); + onended(); + fm?.stop(); + vibratoOscillator?.stop(); + }, + begin, + holdend, + ); + + getParamADSR(envGain.gain, attack, decay, sustain, release, 0, 0.3 * gainAdjustment, begin, holdend, 'linear'); return { - node, - stop: (time) => { - // o.stop(time); - }, + node: envGain, + stop: (time) => {}, }; }, { prebake: true, type: 'synth' }, diff --git a/packages/superdough/worklets.mjs b/packages/superdough/worklets.mjs index 744ca301..c24cccb6 100644 --- a/packages/superdough/worklets.mjs +++ b/packages/superdough/worklets.mjs @@ -175,7 +175,6 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor { constructor() { super(); this.phase = []; - this.logged = 0; } static get parameterDescriptors() { return [ @@ -230,6 +229,7 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor { } // eslint-disable-next-line no-undef if (currentTime >= params.end[0]) { + // this.port.postMessage({ type: 'onended' }); return false; } let frequency = params.frequency[0]; @@ -246,9 +246,6 @@ class SuperSawOscillatorProcessor extends AudioWorkletProcessor { for (let n = 0; n < voices; n++) { const isOdd = (n & 1) == 1; - if (this.logged < 10) { - this.logged += 1; - } //applies unison "spread" detune in semitones const freq = frequency * Math.pow(2, getUnisonDetune(voices, freqspread, n) / 1.2); let gainL = gain1;