From 02db83b896e1f387aa0052ff61203e1eca59c711 Mon Sep 17 00:00:00 2001 From: Jose Luis Date: Sat, 21 Mar 2026 18:55:29 +0100 Subject: [PATCH] fix: VCA CV scaler always 1 so envelope works regardless of gain param MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bug: if VCA gain was 0 when CV was connected, cvMod (initialized with p.gain=0) would multiply envelope by 0 = silence forever. Fix: cvMod always has gain=1 (full pass-through). The envelope (0-1) controls the VCA amplitude directly. When CV is connected, base gain is zeroed so only the envelope signal is heard. When disconnected, base gain is restored from the param value. Before: cvMod.gain = p.gain → envelope × 0 = 0 (broken) After: cvMod.gain = 1 → envelope × 1 = envelope (correct) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/engine/audioEngine.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/engine/audioEngine.js b/src/engine/audioEngine.js index ad5b387..0894599 100644 --- a/src/engine/audioEngine.js +++ b/src/engine/audioEngine.js @@ -127,8 +127,9 @@ function createNode(mod) { } case 'vca': { const gain = new Tone.Gain(p.gain); - // CV modulation scaler: envelope (0-1) × gain param → added to gain.gain - const cvMod = new Tone.Gain(p.gain); + // CV scaler: always gain=1 so envelope (0-1) passes through fully. + // When CV is connected, base gain is zeroed — envelope controls amplitude entirely. + const cvMod = new Tone.Gain(1); cvMod.connect(gain.gain); return { node: gain, @@ -401,9 +402,10 @@ export function updateParam(moduleId, paramName, value) { break; case 'vca': if (paramName === 'gain') { + // Only update base gain if no CV is connected (CV zeroes it) const hasCV = state.connections.some(c => c.to.moduleId === moduleId && c.to.port === 'cv'); if (!hasCV) entry.node.gain.value = value; - if (entry._cvMod) entry._cvMod.gain.value = value; + // cvMod stays at 1 always — envelope controls full range } break; case 'delay':