feat: web matrix mixer per-instrument strips
CI build oscope-of / build-check (push) Has been cancelled

This commit is contained in:
L'électron rare
2026-06-28 20:10:30 +02:00
parent 29e0df3378
commit 6a14b3b67d
3 changed files with 76 additions and 0 deletions
+11
View File
@@ -142,3 +142,14 @@ button.queued { animation: queued-blink 600ms ease-in-out infinite alternate; }
.tl-bar.in-loop { background: #1a2a1a; border-color: #2a5a2a; color: #4d9; }
.tl-bar.tl-head { background: #fa0; border-color: #fc0; color: #000; }
.tl-bar.tl-head.in-loop { background: #fa0; border-color: #fc0; color: #000; }
/* --- Matrix mixer: 16-strip volume + mute --- */
.matrix-mixer { display: flex; gap: 4px; margin: 12px 0; overflow-x: auto;
-webkit-overflow-scrolling: touch; padding-bottom: 4px; }
.mix-strip { display: flex; flex-direction: column; align-items: center; gap: 4px;
min-width: 40px; flex-shrink: 0; }
.mix-fader { writing-mode: vertical-lr; direction: rtl; width: 28px; height: 110px;
accent-color: #4c8; cursor: pointer; -webkit-appearance: slider-vertical; appearance: slider-vertical; }
.mix-mute { width: auto; padding: 4px 6px; font-size: 11px; }
.mix-mute.on { background: #622; border-color: #c44; color: #f88; font-weight: 700; }
.mix-label { font-size: 10px; color: #9af; text-align: center; word-break: break-all; }
+64
View File
@@ -407,6 +407,69 @@ function initTimelinePointer() {
});
}
// --- Matrix mixer: 16-voice per-strip volume + mute ---
const mixerLevel = {};
const mixerMuted = {};
MATRIX_VOICES.forEach(v => { mixerLevel[v] = 0.8; mixerMuted[v] = false; });
function renderMixer() {
const container = document.getElementById("matrix-mixer");
if (!container) return;
container.innerHTML = "";
MATRIX_VOICES.forEach(voice => {
const strip = document.createElement("div");
strip.className = "mix-strip";
strip.dataset.voice = voice;
const fader = document.createElement("input");
fader.type = "range";
fader.min = "0";
fader.max = "1.5";
fader.step = "0.05";
fader.value = String(mixerLevel[voice]);
fader.className = "mix-fader";
fader.dataset.voice = voice;
const muteBtn = document.createElement("button");
muteBtn.className = "mix-mute";
muteBtn.textContent = "M";
const label = document.createElement("span");
label.className = "mix-label";
label.textContent = voice;
fader.addEventListener("input", () => {
const v = +fader.value;
mixerLevel[voice] = v;
// Moving the fader unmutes + sends immediately
if (mixerMuted[voice]) {
mixerMuted[voice] = false;
strip.classList.remove("muted");
muteBtn.classList.remove("on");
}
send("/launch/vol", voice, v);
});
muteBtn.addEventListener("click", () => {
mixerMuted[voice] = !mixerMuted[voice];
if (mixerMuted[voice]) {
muteBtn.classList.add("on");
strip.classList.add("muted");
send("/launch/vol", voice, 0);
} else {
muteBtn.classList.remove("on");
strip.classList.remove("muted");
send("/launch/vol", voice, mixerLevel[voice]);
}
});
strip.appendChild(fader);
strip.appendChild(muteBtn);
strip.appendChild(label);
container.appendChild(strip);
});
}
// --- Matrix audio-reactive glow (trigger envelope; math in matrix_glow.js) ---
const voiceLevel = new Array(MATRIX_VOICES.length).fill(0);
let glowRaf = null;
@@ -634,6 +697,7 @@ document.addEventListener("DOMContentLoaded", () => {
renderMatrix();
renderTimeline();
initTimelinePointer();
renderMixer();
// Name input: rename selected preset and update button labels
const melName = document.getElementById("mel-name");
+1
View File
@@ -170,6 +170,7 @@
<div id="matrix-timeline" class="matrix-timeline"></div>
<div id="matrix-grid"></div>
</div>
<div id="matrix-mixer" class="matrix-mixer"></div>
</section>
</div>
<script type="module">import * as G from "./matrix_glow.js"; window.MatrixGlow = G;</script>