criação de novas patterns de composição nos projetos
Deploy / Deploy (push) Successful in 2m5s
Details
Deploy / Deploy (push) Successful in 2m5s
Details
This commit is contained in:
parent
17d3419475
commit
43418094f8
|
|
@ -544,31 +544,31 @@ export async function parseMmpContent(xmlString) {
|
||||||
const basslineContainers = bbTrackNodes
|
const basslineContainers = bbTrackNodes
|
||||||
.map((trackNode, idx) => {
|
.map((trackNode, idx) => {
|
||||||
const trackName = trackNode.getAttribute("name") || "Beat/Bassline";
|
const trackName = trackNode.getAttribute("name") || "Beat/Bassline";
|
||||||
|
const playlistClips = Array.from(trackNode.querySelectorAll(":scope > bbtco")).map((bbtco, cidx) => {
|
||||||
const playlistClips = Array.from(
|
const pos = parseInt(bbtco.getAttribute("pos"), 10) || 0;
|
||||||
trackNode.querySelectorAll(":scope > bbtco")
|
const len = parseInt(bbtco.getAttribute("len"), 10) || 192;
|
||||||
).map((bbtco) => ({
|
|
||||||
pos: parseInt(bbtco.getAttribute("pos"), 10) || 0,
|
|
||||||
len: parseInt(bbtco.getAttribute("len"), 10) || 192,
|
|
||||||
name: trackName,
|
|
||||||
}));
|
|
||||||
|
|
||||||
// ❌ remove esta linha:
|
|
||||||
// if (playlistClips.length === 0) return null;
|
|
||||||
|
|
||||||
// ✅ mantém mesmo vazia
|
|
||||||
return {
|
return {
|
||||||
id: `bassline_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
id: `plc_${idx}_${pos}_${len}_${cidx}`, // determinístico
|
||||||
|
pos,
|
||||||
|
len,
|
||||||
name: trackName,
|
name: trackName,
|
||||||
type: "bassline",
|
|
||||||
playlist_clips: playlistClips, // pode ser []
|
|
||||||
patternIndex: idx,
|
|
||||||
instrumentSourceId: rackId,
|
|
||||||
volume: 1,
|
|
||||||
pan: 0,
|
|
||||||
patterns: [],
|
|
||||||
isMuted: trackNode.getAttribute("muted") === "1",
|
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// NÃO retornar null quando não tem clips:
|
||||||
|
return {
|
||||||
|
id: `bb_container_${idx}`,
|
||||||
|
name: trackName,
|
||||||
|
type: "bassline",
|
||||||
|
patternIndex: idx,
|
||||||
|
playlist_clips: playlistClips, // pode ser []
|
||||||
|
patterns: [],
|
||||||
|
isMuted: false,
|
||||||
|
instrumentSourceId: bbRackId, // ou algo equivalente no teu parse
|
||||||
|
volume: 1,
|
||||||
|
pan: 0,
|
||||||
|
};
|
||||||
|
|
||||||
})
|
})
|
||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -213,8 +213,27 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Seleção de pattern
|
// --- Criar/Remover Pattern (toolbar) ---
|
||||||
|
const addPatternBtn = document.getElementById("add-pattern-btn");
|
||||||
|
const removePatternBtn = document.getElementById("remove-pattern-btn");
|
||||||
|
|
||||||
|
addPatternBtn?.addEventListener("click", () => {
|
||||||
|
const refTrack = (appState.pattern.tracks || []).find(t => t.type !== "bassline");
|
||||||
|
const nextIndex = refTrack?.patterns?.length ?? 0;
|
||||||
|
|
||||||
|
const defaultName = `Pattern ${nextIndex + 1}`;
|
||||||
|
const name = (prompt("Nome do novo pattern:", defaultName) || defaultName).trim();
|
||||||
|
|
||||||
|
// cria e já seleciona
|
||||||
|
sendAction({ type: "ADD_PATTERN", patternIndex: nextIndex, name, select: true });
|
||||||
|
});
|
||||||
|
|
||||||
|
// opcional (se quiser implementar remover depois)
|
||||||
|
removePatternBtn?.addEventListener("click", () => {
|
||||||
|
sendAction({ type: "REMOVE_LAST_PATTERN" });
|
||||||
|
});
|
||||||
|
|
||||||
|
//Seleção de pattern
|
||||||
const globalPatternSelector = document.getElementById(
|
const globalPatternSelector = document.getElementById(
|
||||||
"global-pattern-selector"
|
"global-pattern-selector"
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -499,11 +499,15 @@ function _ensureBasslineForPatternIndex(patternIndex) {
|
||||||
instrumentSourceId: null,
|
instrumentSourceId: null,
|
||||||
volume: 1,
|
volume: 1,
|
||||||
pan: 0,
|
pan: 0,
|
||||||
|
instrumentSourceId: _getDefaultRackId(),
|
||||||
};
|
};
|
||||||
appState.pattern.tracks.push(b);
|
appState.pattern.tracks.push(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Array.isArray(b.playlist_clips)) b.playlist_clips = [];
|
if (!Array.isArray(b.playlist_clips)) b.playlist_clips = [];
|
||||||
|
|
||||||
|
// Isso deixa o modo focado funcionando em patterns recém-criados.
|
||||||
|
if (!b.instrumentSourceId) b.instrumentSourceId = _getDefaultRackId();
|
||||||
|
|
||||||
// garante ids nos clips antigos
|
// garante ids nos clips antigos
|
||||||
b.playlist_clips.forEach((c) => {
|
b.playlist_clips.forEach((c) => {
|
||||||
|
|
@ -513,6 +517,13 @@ function _ensureBasslineForPatternIndex(patternIndex) {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _getDefaultRackId() {
|
||||||
|
const inst = (appState.pattern.tracks || []).find(
|
||||||
|
(t) => t.type !== "bassline" && t.parentBasslineId
|
||||||
|
);
|
||||||
|
return inst?.parentBasslineId ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// BROADCAST
|
// BROADCAST
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
@ -708,6 +719,71 @@ async function handleActionBroadcast(action) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case "ADD_PATTERN": {
|
||||||
|
const who = actorOf(action);
|
||||||
|
|
||||||
|
const desiredIndex = Number.isFinite(Number(action.patternIndex))
|
||||||
|
? Number(action.patternIndex)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
const nonBass = (appState.pattern.tracks || []).filter(t => t.type !== "bassline");
|
||||||
|
const currentCount = nonBass.reduce((m, t) => Math.max(m, t.patterns?.length ?? 0), 0);
|
||||||
|
|
||||||
|
const idx = desiredIndex != null ? desiredIndex : currentCount;
|
||||||
|
const finalName = String(action.name || `Pattern ${idx + 1}`).trim();
|
||||||
|
|
||||||
|
// 1) cria patterns vazios em TODAS as tracks (respeita bars-input)
|
||||||
|
_ensurePatternsUpTo(idx);
|
||||||
|
|
||||||
|
// 2) garante nome/pos estáveis no índice criado
|
||||||
|
for (const t of nonBass) {
|
||||||
|
t.patterns[idx] = t.patterns[idx] || _makeEmptyPattern(idx);
|
||||||
|
t.patterns[idx].name = finalName;
|
||||||
|
if (t.patterns[idx].pos == null) t.patterns[idx].pos = idx * LMMS_BAR_TICKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3) cria a lane "bassline" (a coluna do pattern)
|
||||||
|
const b = _ensureBasslineForPatternIndex(idx);
|
||||||
|
b.patternIndex = idx;
|
||||||
|
b.name = finalName;
|
||||||
|
if (!b.instrumentSourceId) b.instrumentSourceId = _getDefaultRackId();
|
||||||
|
|
||||||
|
// 4) opcional: já selecionar
|
||||||
|
if (action.select) {
|
||||||
|
appState.pattern.activePatternIndex = idx;
|
||||||
|
appState.pattern.tracks.forEach((track) => (track.activePatternIndex = idx));
|
||||||
|
}
|
||||||
|
|
||||||
|
renderAll();
|
||||||
|
showToast(`➕ ${who} criou: ${finalName}`, "success");
|
||||||
|
saveStateToSession();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case "REMOVE_LAST_PATTERN": {
|
||||||
|
const nonBass = (appState.pattern.tracks || []).filter(t => t.type !== "bassline");
|
||||||
|
const count = nonBass.reduce((m, t) => Math.max(m, t.patterns?.length ?? 0), 0);
|
||||||
|
const last = count - 1;
|
||||||
|
if (last <= 0) break;
|
||||||
|
|
||||||
|
// remove patterns nas tracks
|
||||||
|
for (const t of nonBass) t.patterns.pop();
|
||||||
|
|
||||||
|
// remove a lane bassline correspondente
|
||||||
|
appState.pattern.tracks = appState.pattern.tracks.filter(
|
||||||
|
t => !(t.type === "bassline" && Number(t.patternIndex) === last)
|
||||||
|
);
|
||||||
|
|
||||||
|
// ajusta seleção
|
||||||
|
const newIdx = Math.min(appState.pattern.activePatternIndex, last - 1);
|
||||||
|
appState.pattern.activePatternIndex = newIdx;
|
||||||
|
appState.pattern.tracks.forEach((t) => (t.activePatternIndex = newIdx));
|
||||||
|
|
||||||
|
renderAll();
|
||||||
|
saveStateToSession();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case "ADD_PLAYLIST_PATTERN_CLIP": {
|
case "ADD_PLAYLIST_PATTERN_CLIP": {
|
||||||
const { patternIndex, pos, len, clipId, name } = action;
|
const { patternIndex, pos, len, clipId, name } = action;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue