diff --git a/assets/js/creations/socket.js b/assets/js/creations/socket.js index b2d281f1..cd79875f 100755 --- a/assets/js/creations/socket.js +++ b/assets/js/creations/socket.js @@ -511,6 +511,39 @@ socket.on("action_broadcast", (payload) => { const LMMS_BAR_TICKS = 192; const STEPS_PER_BAR = 16; +// ticks por step (1/16) no LMMS: 192/16 = 12 +const TICKS_PER_STEP = LMMS_BAR_TICKS / STEPS_PER_BAR; + +function _syncNotesWithStepToggle(pattern, stepIndex, isActive) { + if (!pattern) return; + if (!Array.isArray(pattern.notes)) pattern.notes = []; + + const pos = Math.round(stepIndex * TICKS_PER_STEP); + + const samePos = (n) => Number(n?.pos) === pos; + + if (isActive) { + // se já existe nota nesse step, não duplica + if (pattern.notes.some(samePos)) return; + + // tenta reaproveitar "template" de nota pra manter key/vol/pan coerentes + const tpl = pattern.notes[0] || { vol: 100, len: TICKS_PER_STEP, pan: 0, key: 57 }; + + pattern.notes.push({ + vol: tpl.vol ?? 100, + len: tpl.len ?? TICKS_PER_STEP, + pan: tpl.pan ?? 0, + key: tpl.key ?? 57, + pos, + }); + + pattern.notes.sort((a, b) => Number(a.pos) - Number(b.pos)); + } else { + // remove todas as notas nesse step (útil inclusive pra "steps compostos") + pattern.notes = pattern.notes.filter((n) => !samePos(n)); + } +} + function _makeEmptyPattern(idx) { // tenta respeitar bars-input atual, mas nunca menos que 1 bar const bars = parseInt(document.getElementById("bars-input")?.value, 10) || 1; @@ -1039,6 +1072,11 @@ async function handleActionBroadcast(action) { t.patterns[pi].steps[si] = isActive; + _syncNotesWithStepToggle(t.patterns[pi], si, isActive); + + // importante: se o Audio Editor estiver tocando, precisa re-schedulear + restartAudioEditorIfPlaying(); + try { updateStepUI(ti, pi, si, isActive); } catch {} if (!isFromSelf) schedulePatternRerender(); } @@ -1098,6 +1136,8 @@ async function handleActionBroadcast(action) { // 5) Salva no sessionStorage saveStateToSession(); + restartAudioEditorIfPlaying(); + break; } // Samples