From 37c7fa006f33699ba19ee2c5cf7cd178648ab9ff Mon Sep 17 00:00:00 2001 From: JotaChina Date: Thu, 25 Dec 2025 17:10:46 -0300 Subject: [PATCH] playlist funcional. inserindo loops de uma mesma faixa de pattern --- assets/js/creations/audio/audio_ui.js | 66 ++++++++++++++++++++ assets/js/creations/pattern/pattern_audio.js | 8 ++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/assets/js/creations/audio/audio_ui.js b/assets/js/creations/audio/audio_ui.js index 5d407984..202a5e99 100755 --- a/assets/js/creations/audio/audio_ui.js +++ b/assets/js/creations/audio/audio_ui.js @@ -277,6 +277,47 @@ export function renderAudioEditor() { }); } + function getLoopStepsForBasslineLane(basslineTrack) { + const patternIndex = basslineTrack.patternIndex ?? 0; + + // pega os instrumentos que pertencem a esse rack (mesma lógica do pattern_ui) :contentReference[oaicite:1]{index=1} + const srcId = basslineTrack.instrumentSourceId || basslineTrack.id; + const children = (appState.pattern.tracks || []).filter( + (t) => t.type !== "bassline" && t.parentBasslineId === srcId && !t.muted + ); + + let maxSteps = 0; + + for (const t of children) { + const p = t.patterns?.[patternIndex]; + if (!p) continue; + + if (Array.isArray(p.steps) && p.steps.length > 0) { + maxSteps = Math.max(maxSteps, p.steps.length); + continue; + } + + // fallback pra patterns que só têm notes + if (Array.isArray(p.notes) && p.notes.length > 0) { + let endTick = 0; + for (const n of p.notes) { + const pos = Number(n.pos) || 0; + const len = Math.max(0, Number(n.len) || 0); + endTick = Math.max(endTick, pos + len); + } + const steps = Math.ceil(endTick / TICKS_PER_STEP); + maxSteps = Math.max(maxSteps, steps); + } + } + + if (maxSteps <= 0) maxSteps = 16; // default + // arredonda pra múltiplo de 16 (bem “LMMS feel”) + maxSteps = Math.ceil(maxSteps / 16) * 16; + + return maxSteps; + } + + tracksToRender.forEach((trackData) => { const audioTrackLane = document.createElement("div"); audioTrackLane.className = "audio-track-lane"; @@ -369,6 +410,29 @@ export function renderAudioEditor() { clipDiv.style.left = `${leftPos}px`; clipDiv.style.width = `${widthDim}px`; clipDiv.style.height = "100%"; + // ✅ overlay de “marquinhas pretas” (loop do pattern) + const loopSteps = getLoopStepsForBasslineLane(trackData); + const loopPx = loopSteps * stepWidthPx; + + if (loopPx > 0) { + clipDiv.style.position = "absolute"; // garante + const markers = document.createElement("div"); + markers.style.position = "absolute"; + markers.style.inset = "0"; + markers.style.pointerEvents = "none"; + markers.style.opacity = "0.9"; + markers.style.backgroundImage = `repeating-linear-gradient( + to right, + rgba(0,0,0,0.75) 0px, + rgba(0,0,0,0.75) 2px, + transparent 2px, + transparent ${loopPx}px + )`; + // deixa o texto por cima + markers.style.zIndex = "6"; + clipDiv.appendChild(markers); + } + const gridStyle = getComputedStyle(grid); clipDiv.style.backgroundImage = gridStyle.backgroundImage; clipDiv.style.backgroundSize = gridStyle.backgroundSize; @@ -391,6 +455,8 @@ export function renderAudioEditor() { label.style.pointerEvents = "none"; label.style.whiteSpace = "nowrap"; label.style.overflow = "hidden"; + label.style.position = "relative"; + label.style.zIndex = "7"; clipDiv.appendChild(label); clipDiv.addEventListener("dblclick", (e) => { diff --git a/assets/js/creations/pattern/pattern_audio.js b/assets/js/creations/pattern/pattern_audio.js index fab1c3bf..e2f92ba7 100755 --- a/assets/js/creations/pattern/pattern_audio.js +++ b/assets/js/creations/pattern/pattern_audio.js @@ -586,7 +586,11 @@ export function startSongPatternPlaybackOnTransport() { for (const hit of activePatternHits) { const patt = track.patterns?.[hit.patternIndex]; - if (!patt) continue; + if (!patt?.steps) continue; + + // 👇 ADD + const pattLen = patt.steps.length; + const stepInPattern = pattLen > 0 ? (hit.localStep % pattLen) : hit.localStep; // ✅ 1) PLUGIN com piano roll (notes) if ( @@ -626,7 +630,7 @@ export function startSongPatternPlaybackOnTransport() { // ✅ 2) Lógica antiga de STEP (sampler / plugin sem notes) if (!patt.steps) continue; - if (patt.steps[hit.localStep]) { + if (patt.steps[stepInPattern]) { if (track.type === "sampler" && track.player) { track.player.restart = true; try { track.player.start(time); } catch {}