diff --git a/web_realart/public/control/control.css b/web_realart/public/control/control.css
index dead6fd..aab8471 100644
--- a/web_realart/public/control/control.css
+++ b/web_realart/public/control/control.css
@@ -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; }
diff --git a/web_realart/public/control/index.html b/web_realart/public/control/index.html
index 38df3e6..585aff0 100644
--- a/web_realart/public/control/index.html
+++ b/web_realart/public/control/index.html
@@ -181,6 +181,8 @@
Actions globales (morceau)
+
Réglages
+
diff --git a/web_realart/public/control/js/morceau-panel.js b/web_realart/public/control/js/morceau-panel.js
index 15b56f5..6d07c4a 100644
--- a/web_realart/public/control/js/morceau-panel.js
+++ b/web_realart/public/control/js/morceau-panel.js
@@ -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");