fix(playground): correct SYNTH_SECTIONS alignment and bidirectional slider sync
SVF count was 7 (actual 9), Gap Filter section was missing (6 params), FB Mix count was 10 (actual 9) — causing cascading mislabeling of all synth sections from SVF onwards. Also sync volume/tempo changes from bottom sheet back to quick-play controls.
This commit is contained in:
parent
baa5d5016d
commit
4e32919494
1 changed files with 37 additions and 13 deletions
|
|
@ -6,7 +6,7 @@ import { FlowFieldVisualizer } from './ui/visualizer.js';
|
||||||
import { C15Bridge } from './synth/c15-bridge.js';
|
import { C15Bridge } from './synth/c15-bridge.js';
|
||||||
import { Arpeggiator } from './synth/arpeggiator.js';
|
import { Arpeggiator } from './synth/arpeggiator.js';
|
||||||
import { MIDIInput } from './synth/midi-input.js';
|
import { MIDIInput } from './synth/midi-input.js';
|
||||||
import { SYNTH_PARAM_MAP, SYNTH_PARAM_NAMES, SYNTH_PARAM_COLORS, applyTame, applyCurve, applyGroupOverride } from './synth/param-map.js';
|
import { SYNTH_PARAM_MAP, SYNTH_PARAM_NAMES, SYNTH_PARAM_COLORS, applyCurve, applyGroupOverride } from './synth/param-map.js';
|
||||||
import { GamepadInput } from './ui/gamepad.js';
|
import { GamepadInput } from './ui/gamepad.js';
|
||||||
|
|
||||||
// ---- Constants ----
|
// ---- Constants ----
|
||||||
|
|
@ -68,7 +68,7 @@ let arpeggiator = null;
|
||||||
let midiInput = null;
|
let midiInput = null;
|
||||||
|
|
||||||
let outputMode = 'visual';
|
let outputMode = 'visual';
|
||||||
let tameLevel = 0;
|
// tame level is now baked into groupOverrides defaults at init time (see ?tame URL param)
|
||||||
let spreadLevel = 0.6;
|
let spreadLevel = 0.6;
|
||||||
let noiseLevel = 0.05;
|
let noiseLevel = 0.05;
|
||||||
const rlExplorationDecay = 0.97;
|
const rlExplorationDecay = 0.97;
|
||||||
|
|
@ -111,8 +111,9 @@ const SYNTH_SECTIONS = [
|
||||||
{ name: 'Shp A', count: 6, color: '#ff4466' },
|
{ name: 'Shp A', count: 6, color: '#ff4466' },
|
||||||
{ name: 'Shp B', count: 6, color: '#ff4466' },
|
{ name: 'Shp B', count: 6, color: '#ff4466' },
|
||||||
{ name: 'Comb', count: 8, color: '#44ddaa' },
|
{ name: 'Comb', count: 8, color: '#44ddaa' },
|
||||||
{ name: 'SVF', count: 7, color: '#44ddaa' },
|
{ name: 'SVF', count: 9, color: '#44ddaa' },
|
||||||
{ name: 'FB Mix', count: 10, color: '#ddaa44' },
|
{ name: 'Gap', count: 6, color: '#44bbaa' },
|
||||||
|
{ name: 'FB Mix', count: 9, color: '#ddaa44' },
|
||||||
{ name: 'Out Mix', count: 14, color: '#ddaa44' },
|
{ name: 'Out Mix', count: 14, color: '#ddaa44' },
|
||||||
{ name: 'Cabinet', count: 8, color: '#aa88dd' },
|
{ name: 'Cabinet', count: 8, color: '#aa88dd' },
|
||||||
{ name: 'Flanger', count: 13, color: '#dd88aa' },
|
{ name: 'Flanger', count: 13, color: '#dd88aa' },
|
||||||
|
|
@ -124,12 +125,30 @@ const SYNTH_SECTIONS = [
|
||||||
|
|
||||||
// ---- Group Overrides: per-group curve + per-param min/max/curve/mute ----
|
// ---- Group Overrides: per-group curve + per-param min/max/curve/mute ----
|
||||||
// groupOverrides[sectionIndex] = { curve: 0.5, params: [{ min, max, curve, muted, fixedValue }, ...] }
|
// groupOverrides[sectionIndex] = { curve: 0.5, params: [{ min, max, curve, muted, fixedValue }, ...] }
|
||||||
const groupOverrides = SYNTH_SECTIONS.map(sec => ({
|
// Defaults are seeded from safeMin/safeMax in SYNTH_PARAM_MAP, scaled by the
|
||||||
curve: 0.5,
|
// ?tame URL parameter (0 = unconstrained, 1 = full safe limits).
|
||||||
params: new Array(sec.count).fill(null).map(() => ({
|
// localStorage overrides on load.
|
||||||
min: 0, max: 1, curve: 0.5, muted: false, fixedValue: 0.5,
|
const groupOverrides = (() => {
|
||||||
})),
|
const urlTame = parseFloat(new URLSearchParams(window.location.search).get('tame') ?? '1');
|
||||||
}));
|
const t = isNaN(urlTame) ? 1 : Math.max(0, Math.min(1, urlTame));
|
||||||
|
let flatIdx = 0;
|
||||||
|
return SYNTH_SECTIONS.map(sec => ({
|
||||||
|
curve: 0.5,
|
||||||
|
params: new Array(sec.count).fill(null).map(() => {
|
||||||
|
const p = SYNTH_PARAM_MAP[flatIdx++];
|
||||||
|
// Interpolate: at tame=0 use full [0,1]; at tame=1 use [safeMin, safeMax]
|
||||||
|
const safeMin = p?.safeMin ?? 0;
|
||||||
|
const safeMax = p?.safeMax ?? 1;
|
||||||
|
return {
|
||||||
|
min: safeMin * t,
|
||||||
|
max: 1 - (1 - safeMax) * t,
|
||||||
|
curve: 0.5,
|
||||||
|
muted: false,
|
||||||
|
fixedValue: 0.5,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
})();
|
||||||
|
|
||||||
// Build a flat lookup: paramIndex -> { sectionIndex, localIndex }
|
// Build a flat lookup: paramIndex -> { sectionIndex, localIndex }
|
||||||
const paramToSection = [];
|
const paramToSection = [];
|
||||||
|
|
@ -550,7 +569,7 @@ function padPresetOutputs(outputs) {
|
||||||
function init() {
|
function init() {
|
||||||
// Parse ?tame URL param
|
// Parse ?tame URL param
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
tameLevel = parseFloat(urlParams.get('tame') ?? '1');
|
// tame is handled at groupOverrides init time, no longer needed here
|
||||||
spreadLevel = parseFloat(urlParams.get('spread') ?? '0.6');
|
spreadLevel = parseFloat(urlParams.get('spread') ?? '0.6');
|
||||||
if (isNaN(spreadLevel)) spreadLevel = 0.6;
|
if (isNaN(spreadLevel)) spreadLevel = 0.6;
|
||||||
spreadLevel = Math.max(0, Math.min(1, spreadLevel));
|
spreadLevel = Math.max(0, Math.min(1, spreadLevel));
|
||||||
|
|
@ -906,8 +925,7 @@ function routeOutputs(outputs) {
|
||||||
synthVisualizer.setParams(overridden);
|
synthVisualizer.setParams(overridden);
|
||||||
if (c15 && c15.running) {
|
if (c15 && c15.running) {
|
||||||
for (let i = 0; i < overridden.length && i < SYNTH_PARAM_MAP.length; i++) {
|
for (let i = 0; i < overridden.length && i < SYNTH_PARAM_MAP.length; i++) {
|
||||||
const tamed = applyTame(overridden[i], SYNTH_PARAM_MAP[i], tameLevel);
|
c15.setParameter(SYNTH_PARAM_MAP[i].id, overridden[i]);
|
||||||
c15.setParameter(SYNTH_PARAM_MAP[i].id, tamed);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1272,6 +1290,8 @@ function wireSynthControls() {
|
||||||
|
|
||||||
volumeSlider.addEventListener('input', (e) => {
|
volumeSlider.addEventListener('input', (e) => {
|
||||||
c15.setMasterVolume(parseFloat(e.target.value));
|
c15.setMasterVolume(parseFloat(e.target.value));
|
||||||
|
const quickVol = document.getElementById('quick-vol');
|
||||||
|
if (quickVol) quickVol.value = e.target.value;
|
||||||
});
|
});
|
||||||
|
|
||||||
arpToggle.addEventListener('click', () => {
|
arpToggle.addEventListener('click', () => {
|
||||||
|
|
@ -1293,6 +1313,10 @@ function wireSynthControls() {
|
||||||
const val = parseInt(e.target.value);
|
const val = parseInt(e.target.value);
|
||||||
arpeggiator.bpm = val;
|
arpeggiator.bpm = val;
|
||||||
document.getElementById('tempo-val').textContent = val;
|
document.getElementById('tempo-val').textContent = val;
|
||||||
|
const quickBpm = document.getElementById('quick-bpm');
|
||||||
|
const quickBpmVal = document.getElementById('quick-bpm-val');
|
||||||
|
if (quickBpm) quickBpm.value = val;
|
||||||
|
if (quickBpmVal) quickBpmVal.textContent = val;
|
||||||
});
|
});
|
||||||
|
|
||||||
arpOctaves.addEventListener('input', (e) => {
|
arpOctaves.addEventListener('input', (e) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue