feat(control): reglages group in morceau panel

Add the Reglages group to the morceau panel: two threshold sliders
(open/close, the close one being the user-requested "sensibilite"),
a transient-duration slider (1-8 bars), and a breakdown kept-voices
checkbox grid covering all MATRIX_VOICES.

Wired over /matrix/globalsettings with inbound sync and onOpen
get-request. Imports consolidated into a single coherent block;
no duplicate identifiers. DOM container added to index.html;
styles appended to control.css.
This commit is contained in:
L'électron rare
2026-06-30 13:45:17 +02:00
parent 64348aa90d
commit d483a29d3d
3 changed files with 79 additions and 2 deletions
+8
View File
@@ -237,3 +237,11 @@ button.queued { animation: queued-blink 600ms ease-in-out infinite alternate; }
gap: 6px; font-size: 13px; color: #ccc; }
.morceau-slot select { flex: 0 0 auto; background: #222; color: #eee;
border: 1px solid #444; border-radius: 4px; padding: 2px 4px; }
/* --- Réglages group --- */
.morceau-settings { display: flex; flex-direction: column; gap: 4px; margin-top: 4px; }
.morceau-set-row { display: grid; grid-template-columns: 1fr 2fr auto; align-items: center; gap: 6px; font-size: 12px; color: #ccc; }
.morceau-set-row output { min-width: 2.5em; text-align: right; color: #8c8; }
.morceau-keep { display: flex; flex-wrap: wrap; gap: 2px 8px; margin-top: 4px; font-size: 11px; color: #aaa; }
.morceau-keep > span { flex-basis: 100%; color: #888; }
.morceau-keep-box { display: inline-flex; align-items: center; gap: 2px; }
+2
View File
@@ -181,6 +181,8 @@
<div id="morceau-panel" class="morceau-panel">
<div class="morceau-title">Actions globales (morceau)</div>
<div id="morceau-slots" class="morceau-slots"></div>
<div class="morceau-title">Réglages</div>
<div id="morceau-settings" class="morceau-settings"></div>
</div>
</section>
</div>
+69 -2
View File
@@ -3,9 +3,10 @@
// matrix-state.js, transport over osc.js. Later phases add settings + global controls.
import { send, on, onOpen } from "./osc.js";
import {
GLOBAL_ACTION_CHOICES, GLOBAL_SLOTS, matGlobalActions,
setGlobalAction, saveMatState,
GLOBAL_ACTION_CHOICES, GLOBAL_SLOTS, matGlobalActions, setGlobalAction, saveMatState,
matGlobalSettings, setGlobalSetting, parseGlobalSettings,
} from "./matrix-state.js";
import { MATRIX_VOICES } from "./matrix-state.js";
export function init() {
const host = document.getElementById("morceau-slots");
@@ -31,6 +32,72 @@ export function init() {
wrap.appendChild(sel);
host.appendChild(wrap);
});
// --- Réglages: thresholds, transient duration, breakdown kept-voices ---
const sHost = document.getElementById("morceau-settings");
if (sHost) {
const sendSettings = () => {
const s = matGlobalSettings;
send("/matrix/globalsettings", s.openThresh, s.closeThresh, s.transientBars,
s.breakdownKeep.length, ...s.breakdownKeep);
saveMatState();
};
const slider = (key, label, min, max, step) => {
const row = document.createElement("label");
row.className = "morceau-set-row";
const span = document.createElement("span"); span.textContent = label;
const inp = document.createElement("input");
inp.type = "range"; inp.min = min; inp.max = max; inp.step = step;
inp.value = matGlobalSettings[key];
const out = document.createElement("output"); out.textContent = matGlobalSettings[key];
inp.addEventListener("input", () => { out.textContent = inp.value; });
inp.addEventListener("change", () => {
setGlobalSetting(key, key === "transientBars" ? Math.round(+inp.value) : +inp.value);
sendSettings();
});
row.append(span, inp, out);
sHost.appendChild(row);
return { inp, out };
};
const ctl = {
openThresh: slider("openThresh", "Seuil ouvert", 0.5, 0.95, 0.01),
closeThresh: slider("closeThresh", "Seuil fermé (sensibilité)", 0.05, 0.6, 0.01),
transientBars: slider("transientBars", "Durée (mesures)", 1, 8, 1),
};
const keepWrap = document.createElement("div");
keepWrap.className = "morceau-keep";
const keepTitle = document.createElement("span");
keepTitle.textContent = "Voix gardées (breakdown)";
keepWrap.appendChild(keepTitle);
const keepBoxes = {};
MATRIX_VOICES.forEach((v) => {
const lab = document.createElement("label"); lab.className = "morceau-keep-box";
const cb = document.createElement("input"); cb.type = "checkbox"; cb.value = v;
cb.checked = matGlobalSettings.breakdownKeep.includes(v);
cb.addEventListener("change", () => {
const keep = MATRIX_VOICES.filter((x) => keepBoxes[x].checked);
setGlobalSetting("breakdownKeep", keep);
sendSettings();
});
keepBoxes[v] = cb;
lab.append(cb, document.createTextNode(v));
keepWrap.appendChild(lab);
});
sHost.appendChild(keepWrap);
on("/matrix/globalsettings", (args) => {
const s = parseGlobalSettings(args);
setGlobalSetting("openThresh", s.openThresh);
setGlobalSetting("closeThresh", s.closeThresh);
setGlobalSetting("transientBars", s.transientBars);
setGlobalSetting("breakdownKeep", s.breakdownKeep);
ctl.openThresh.inp.value = s.openThresh; ctl.openThresh.out.textContent = s.openThresh;
ctl.closeThresh.inp.value = s.closeThresh; ctl.closeThresh.out.textContent = s.closeThresh;
ctl.transientBars.inp.value = s.transientBars; ctl.transientBars.out.textContent = s.transientBars;
MATRIX_VOICES.forEach((v) => { keepBoxes[v].checked = s.breakdownKeep.includes(v); });
saveMatState();
});
onOpen(() => send("/matrix/globalsettings/get"));
send("/matrix/globalsettings/get");
}
on("/matrix/globalaction", (args) => {
const slot = Math.round(Number(args[0]));
const id = String(args[1] || "none");