editando patterns na playlist

This commit is contained in:
JotaChina committed 2025-12-27 12:12:22 -03:00
1 parent ec772ad8aa
commit ef499e3df9
6 files changed
+234 -5

No files matched your search

+126
View File
@@ -443,6 +443,42 @@ function schedulePatternRerender() {
});
}
function _genPlaylistClipId() {
return `plc_${Date.now()}_${Math.random().toString(36).slice(2)}`;
}
function _ensureBasslineForPatternIndex(patternIndex) {
let b = appState.pattern.tracks.find(
(t) => t.type === "bassline" && Number(t.patternIndex) === Number(patternIndex)
);
// fallback: cria se não existir (não quebra nada)
if (!b) {
b = {
id: `bassline_${Date.now()}_${Math.random().toString(36).slice(2)}`,
name: `Beat/Bassline ${patternIndex}`,
type: "bassline",
patternIndex: Number(patternIndex),
playlist_clips: [],
patterns: [],
isMuted: false,
instrumentSourceId: null,
volume: 1,
pan: 0,
};
appState.pattern.tracks.push(b);
}
if (!Array.isArray(b.playlist_clips)) b.playlist_clips = [];
// garante ids nos clips antigos
b.playlist_clips.forEach((c) => {
if (!c.id) c.id = _genPlaylistClipId();
});
return b;
}
// -----------------------------------------------------------------------------
// BROADCAST
// -----------------------------------------------------------------------------
@@ -518,6 +554,96 @@ async function handleActionBroadcast(action) {
showToast(`▶ ${who} Play bases`, "info");
break;
}
case "ADD_PLAYLIST_PATTERN_CLIP": {
const { patternIndex, pos, len, clipId, name } = action;
const b = _ensureBasslineForPatternIndex(patternIndex);
const newClip = {
id: clipId || _genPlaylistClipId(),
pos: Math.max(0, Math.floor(pos ?? 0)),
len: Math.max(12, Math.floor(len ?? 192)),
name: name || b.name || "Pattern",
};
// evita duplicar
if (!b.playlist_clips.some((c) => String(c.id) === String(newClip.id))) {
b.playlist_clips.push(newClip);
b.playlist_clips.sort((a, c) => (a.pos ?? 0) - (c.pos ?? 0));
}
renderAll();
saveStateToSession();
// ✅ sync XML (inclui bbtco depois do patch no file.js)
if (window.ROOM_NAME && isFromSelf) {
try {
const xml = generateXmlFromStateExported();
sendAction({ type: "SYNC_PATTERN_STATE", xml });
} catch {}
}
break;
}
case "UPDATE_PLAYLIST_PATTERN_CLIP": {
const { patternIndex, clipId, pos, len } = action;
const b = _ensureBasslineForPatternIndex(patternIndex);
const c = b.playlist_clips.find((x) => String(x.id) === String(clipId));
if (!c) break;
if (pos !== undefined) c.pos = Math.max(0, Math.floor(pos));
if (len !== undefined) c.len = Math.max(12, Math.floor(len));
b.playlist_clips.sort((a, d) => (a.pos ?? 0) - (d.pos ?? 0));
renderAll();
saveStateToSession();
if (window.ROOM_NAME && isFromSelf) {
try {
const xml = generateXmlFromStateExported();
sendAction({ type: "SYNC_PATTERN_STATE", xml });
} catch {}
}
break;
}
case "REMOVE_PLAYLIST_PATTERN_CLIP": {
const { patternIndex, clipId } = action;
const b = _ensureBasslineForPatternIndex(patternIndex);
b.playlist_clips = b.playlist_clips.filter((c) => String(c.id) !== String(clipId));
// limpa seleção se era o selecionado
if (appState.global.selectedPlaylistClipId === clipId) {
appState.global.selectedPlaylistClipId = null;
appState.global.selectedPlaylistPatternIndex = null;
}
renderAll();
saveStateToSession();
if (window.ROOM_NAME && isFromSelf) {
try {
const xml = generateXmlFromStateExported();
sendAction({ type: "SYNC_PATTERN_STATE", xml });
} catch {}
}
break;
}
case "SELECT_PLAYLIST_PATTERN_CLIP": {
const { patternIndex, clipId } = action;
appState.global.selectedPlaylistClipId = clipId ?? null;
appState.global.selectedPlaylistPatternIndex =
patternIndex ?? null;
renderAll();
saveStateToSession();
break;
}
case "UPDATE_PATTERN_NOTES": {
const {
trackIndex: ti,