playlist funcional. inserindo loops de uma mesma faixa de pattern
Deploy / Deploy (push) Successful in 2m6s
Details
Deploy / Deploy (push) Successful in 2m6s
Details
This commit is contained in:
parent
f9ae0d92a8
commit
37c7fa006f
|
|
@ -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) => {
|
tracksToRender.forEach((trackData) => {
|
||||||
const audioTrackLane = document.createElement("div");
|
const audioTrackLane = document.createElement("div");
|
||||||
audioTrackLane.className = "audio-track-lane";
|
audioTrackLane.className = "audio-track-lane";
|
||||||
|
|
@ -369,6 +410,29 @@ export function renderAudioEditor() {
|
||||||
clipDiv.style.left = `${leftPos}px`;
|
clipDiv.style.left = `${leftPos}px`;
|
||||||
clipDiv.style.width = `${widthDim}px`;
|
clipDiv.style.width = `${widthDim}px`;
|
||||||
clipDiv.style.height = "100%";
|
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);
|
const gridStyle = getComputedStyle(grid);
|
||||||
clipDiv.style.backgroundImage = gridStyle.backgroundImage;
|
clipDiv.style.backgroundImage = gridStyle.backgroundImage;
|
||||||
clipDiv.style.backgroundSize = gridStyle.backgroundSize;
|
clipDiv.style.backgroundSize = gridStyle.backgroundSize;
|
||||||
|
|
@ -391,6 +455,8 @@ export function renderAudioEditor() {
|
||||||
label.style.pointerEvents = "none";
|
label.style.pointerEvents = "none";
|
||||||
label.style.whiteSpace = "nowrap";
|
label.style.whiteSpace = "nowrap";
|
||||||
label.style.overflow = "hidden";
|
label.style.overflow = "hidden";
|
||||||
|
label.style.position = "relative";
|
||||||
|
label.style.zIndex = "7";
|
||||||
clipDiv.appendChild(label);
|
clipDiv.appendChild(label);
|
||||||
|
|
||||||
clipDiv.addEventListener("dblclick", (e) => {
|
clipDiv.addEventListener("dblclick", (e) => {
|
||||||
|
|
|
||||||
|
|
@ -586,7 +586,11 @@ export function startSongPatternPlaybackOnTransport() {
|
||||||
|
|
||||||
for (const hit of activePatternHits) {
|
for (const hit of activePatternHits) {
|
||||||
const patt = track.patterns?.[hit.patternIndex];
|
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)
|
// ✅ 1) PLUGIN com piano roll (notes)
|
||||||
if (
|
if (
|
||||||
|
|
@ -626,7 +630,7 @@ export function startSongPatternPlaybackOnTransport() {
|
||||||
// ✅ 2) Lógica antiga de STEP (sampler / plugin sem notes)
|
// ✅ 2) Lógica antiga de STEP (sampler / plugin sem notes)
|
||||||
if (!patt.steps) continue;
|
if (!patt.steps) continue;
|
||||||
|
|
||||||
if (patt.steps[hit.localStep]) {
|
if (patt.steps[stepInPattern]) {
|
||||||
if (track.type === "sampler" && track.player) {
|
if (track.type === "sampler" && track.player) {
|
||||||
track.player.restart = true;
|
track.player.restart = true;
|
||||||
try { track.player.start(time); } catch {}
|
try { track.player.start(time); } catch {}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue