Dynamic changes to LFO
This commit is contained in:
54
www/bootstrap.js
vendored
54
www/bootstrap.js
vendored
@@ -360,6 +360,20 @@ class PatchRouter {
|
||||
}
|
||||
}
|
||||
|
||||
// ── Waveform sample (mirrors Rust waveform_sample) ───────────────────────────
|
||||
|
||||
function waveformSample(waveIdx, phase) {
|
||||
const TAU = 2 * Math.PI;
|
||||
switch (waveIdx) {
|
||||
case 0: return Math.sin(phase * TAU); // Sine
|
||||
case 1: return 2 * phase - 1; // Saw
|
||||
case 2: return phase < 0.5 ? 1 : -1; // Square
|
||||
case 3: return 4 * Math.abs(phase - Math.floor(phase + 0.5)) - 1; // Triangle
|
||||
case 4: return phase < 0.25 ? 1 : -1; // Pulse
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Computer keyboard map (standard DAW layout) ───────────────────────────────
|
||||
|
||||
const KEY_NOTE = {
|
||||
@@ -401,6 +415,15 @@ async function bootstrap() {
|
||||
const ro = new ResizeObserver(() => allCanvases.forEach(fitCanvas));
|
||||
allCanvases.forEach(c => ro.observe(c));
|
||||
|
||||
// ── Component registry: param min/max for CV display scaling ─────────
|
||||
// { kind → { paramId → { min, max } } }
|
||||
const paramRanges = new Map(
|
||||
JSON.parse(patchbay.available_components()).map(c => [
|
||||
c.kind,
|
||||
new Map(c.params.map(p => [p.id, { min: p.min, max: p.max }]))
|
||||
])
|
||||
);
|
||||
|
||||
// ── Default patch bay layout ──────────────────────────────────────────
|
||||
const cw = pbCanvas.width || pbCanvas.clientWidth || 800;
|
||||
patchbay.add_module("vco", cw * 0.08, 80);
|
||||
@@ -500,6 +523,37 @@ async function bootstrap() {
|
||||
const outLabel = router.isOutputPatched() ? "patched" : "unpatched";
|
||||
status.textContent = router.isOutputPatched() ? "Running · output patched" : "Running · output unpatched";
|
||||
|
||||
// ── Push live CV modulation offsets to the patchbay for display ──
|
||||
// This makes param bars and waveform previews reflect CV-modulated
|
||||
// values even when the knob hasn't moved.
|
||||
{
|
||||
const patch = JSON.parse(patchbay.get_patch_json());
|
||||
patchbay.clear_cv_mods();
|
||||
for (const cable of patch.cables) {
|
||||
if (!cable.dst_jack.startsWith("cv_")) continue;
|
||||
const srcMod = patch.modules.find(m => m.id === cable.src);
|
||||
if (!srcMod || srcMod.kind !== "lfo") continue;
|
||||
|
||||
const paramId = cable.dst_jack.slice(3); // "cv_rate_hz" → "rate_hz"
|
||||
const dstMod = patch.modules.find(m => m.id === cable.dst);
|
||||
if (!dstMod) continue;
|
||||
|
||||
const range = paramRanges.get(dstMod.kind)?.get(paramId);
|
||||
if (!range) continue;
|
||||
|
||||
// LFO normalised output: -depth..+depth
|
||||
const rate = srcMod.params.rate_hz ?? 2;
|
||||
const depth = srcMod.params.depth ?? 0.5;
|
||||
const waveIdx = Math.round(srcMod.params.waveform ?? 0);
|
||||
const phase = ((now / 1000) * rate) % 1.0;
|
||||
const lfoNorm = waveformSample(waveIdx, phase) * depth;
|
||||
|
||||
// Scale to ±½ of the param's full range for display
|
||||
const offset = lfoNorm * (range.max - range.min) * 0.5;
|
||||
patchbay.set_cv_mod(dstMod.id, paramId, offset);
|
||||
}
|
||||
}
|
||||
|
||||
oscilloscope.draw();
|
||||
spectrum.draw();
|
||||
patchbay.draw(now);
|
||||
|
||||
Reference in New Issue
Block a user