tentando obter playlist real do projeto mmp
Deploy / Deploy (push) Successful in 1m53s
Details
Deploy / Deploy (push) Successful in 1m53s
Details
This commit is contained in:
parent
73721f7b9b
commit
09c3ee742a
|
|
@ -94,13 +94,12 @@ function parseInstrumentNode(trackNode, sortedBBTrackNameNodes, pathMap) {
|
||||||
const instrumentNode = trackNode.querySelector("instrument");
|
const instrumentNode = trackNode.querySelector("instrument");
|
||||||
const instrumentTrackNode = trackNode.querySelector("instrumenttrack");
|
const instrumentTrackNode = trackNode.querySelector("instrumenttrack");
|
||||||
|
|
||||||
// Se não tiver instrumento ou track válida, retorna null
|
|
||||||
if (!instrumentNode || !instrumentTrackNode) return null;
|
if (!instrumentNode || !instrumentTrackNode) return null;
|
||||||
|
|
||||||
const trackName = trackNode.getAttribute("name");
|
const trackName = trackNode.getAttribute("name");
|
||||||
const instrumentName = instrumentNode.getAttribute("name");
|
const instrumentName = instrumentNode.getAttribute("name");
|
||||||
|
|
||||||
// Lógica de Patterns
|
// Identifica e ordena os patterns
|
||||||
const allPatternsNodeList = trackNode.querySelectorAll("pattern");
|
const allPatternsNodeList = trackNode.querySelectorAll("pattern");
|
||||||
const allPatternsArray = Array.from(allPatternsNodeList).sort((a, b) => {
|
const allPatternsArray = Array.from(allPatternsNodeList).sort((a, b) => {
|
||||||
return (parseInt(a.getAttribute("pos"), 10) || 0) - (parseInt(b.getAttribute("pos"), 10) || 0);
|
return (parseInt(a.getAttribute("pos"), 10) || 0) - (parseInt(b.getAttribute("pos"), 10) || 0);
|
||||||
|
|
@ -123,15 +122,19 @@ function parseInstrumentNode(trackNode, sortedBBTrackNameNodes, pathMap) {
|
||||||
const notes = [];
|
const notes = [];
|
||||||
const ticksPerStep = 12;
|
const ticksPerStep = 12;
|
||||||
|
|
||||||
|
// Extrai as notas e popula os steps
|
||||||
patternNode.querySelectorAll("note").forEach((noteNode) => {
|
patternNode.querySelectorAll("note").forEach((noteNode) => {
|
||||||
|
const pos = parseInt(noteNode.getAttribute("pos"), 10);
|
||||||
notes.push({
|
notes.push({
|
||||||
pos: parseInt(noteNode.getAttribute("pos"), 10),
|
pos: pos,
|
||||||
len: parseInt(noteNode.getAttribute("len"), 10),
|
len: parseInt(noteNode.getAttribute("len"), 10),
|
||||||
key: parseInt(noteNode.getAttribute("key"), 10),
|
key: parseInt(noteNode.getAttribute("key"), 10),
|
||||||
vol: parseInt(noteNode.getAttribute("vol"), 10),
|
vol: parseInt(noteNode.getAttribute("vol"), 10),
|
||||||
pan: parseInt(noteNode.getAttribute("pan"), 10),
|
pan: parseInt(noteNode.getAttribute("pan"), 10),
|
||||||
});
|
});
|
||||||
const stepIndex = Math.round(parseInt(noteNode.getAttribute("pos"), 10) / ticksPerStep);
|
|
||||||
|
// Converte posição em tick para índice do step (grid)
|
||||||
|
const stepIndex = Math.round(pos / ticksPerStep);
|
||||||
if (stepIndex < patternSteps) steps[stepIndex] = true;
|
if (stepIndex < patternSteps) steps[stepIndex] = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -143,7 +146,6 @@ function parseInstrumentNode(trackNode, sortedBBTrackNameNodes, pathMap) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// Lógica de Sample vs Plugin
|
|
||||||
let finalSamplePath = null;
|
let finalSamplePath = null;
|
||||||
let trackType = "plugin";
|
let trackType = "plugin";
|
||||||
|
|
||||||
|
|
@ -180,7 +182,7 @@ function parseInstrumentNode(trackNode, sortedBBTrackNameNodes, pathMap) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// =================================================================
|
// =================================================================
|
||||||
// 🔥 FUNÇÃO DE PARSING PRINCIPAL
|
// 🔥 FUNÇÃO DE PARSING PRINCIPAL (CORRIGIDA)
|
||||||
// =================================================================
|
// =================================================================
|
||||||
export async function parseMmpContent(xmlString) {
|
export async function parseMmpContent(xmlString) {
|
||||||
resetProjectState();
|
resetProjectState();
|
||||||
|
|
@ -196,8 +198,8 @@ export async function parseMmpContent(xmlString) {
|
||||||
const xmlDoc = parser.parseFromString(xmlString, "application/xml");
|
const xmlDoc = parser.parseFromString(xmlString, "application/xml");
|
||||||
|
|
||||||
appState.global.originalXmlDoc = xmlDoc;
|
appState.global.originalXmlDoc = xmlDoc;
|
||||||
let newTracks = [];
|
|
||||||
|
|
||||||
|
// Configuração Global (BPM, Compasso)
|
||||||
const head = xmlDoc.querySelector("head");
|
const head = xmlDoc.querySelector("head");
|
||||||
if (head) {
|
if (head) {
|
||||||
const setVal = (id, attr, def) => {
|
const setVal = (id, attr, def) => {
|
||||||
|
|
@ -211,11 +213,8 @@ export async function parseMmpContent(xmlString) {
|
||||||
|
|
||||||
const pathMap = getSamplePathMap();
|
const pathMap = getSamplePathMap();
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// 1. Identifica colunas de beat/patterns (usado para mapear steps)
|
||||||
// 1. PREPARAÇÃO
|
// Normalmente ficam dentro do primeiro container de Bassline
|
||||||
// -------------------------------------------------------------
|
|
||||||
|
|
||||||
// Identifica colunas de beat (bbtco) para dar nome aos patterns
|
|
||||||
const bbTrackNodes = Array.from(xmlDoc.querySelectorAll('track[type="1"]'));
|
const bbTrackNodes = Array.from(xmlDoc.querySelectorAll('track[type="1"]'));
|
||||||
let sortedBBTrackNameNodes = [];
|
let sortedBBTrackNameNodes = [];
|
||||||
if (bbTrackNodes.length > 0) {
|
if (bbTrackNodes.length > 0) {
|
||||||
|
|
@ -225,23 +224,27 @@ export async function parseMmpContent(xmlString) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// 2. EXTRAÇÃO DE INSTRUMENTOS (Song Editor)
|
// 2. EXTRAÇÃO DE TODOS OS INSTRUMENTOS (RECURSIVO)
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// Pega apenas os instrumentos que estão soltos no Song Editor (não dentro de BBTracks)
|
// Aqui está a correção: Vamos buscar TODOS os instrumentos (type="0"),
|
||||||
const songInstrumentNodes = Array.from(xmlDoc.querySelectorAll('song > trackcontainer > track[type="0"]'));
|
// não importa se estão na raiz ou dentro de uma bassline.
|
||||||
|
// Isso garante que Kicker, Snare, etc., apareçam no Pattern Editor.
|
||||||
|
|
||||||
const songTracks = songInstrumentNodes
|
const allInstrumentNodes = Array.from(xmlDoc.querySelectorAll('track[type="0"]'));
|
||||||
|
|
||||||
|
const allInstruments = allInstrumentNodes
|
||||||
.map(node => parseInstrumentNode(node, sortedBBTrackNameNodes, pathMap))
|
.map(node => parseInstrumentNode(node, sortedBBTrackNameNodes, pathMap))
|
||||||
.filter(t => t !== null);
|
.filter(t => t !== null);
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// 3. EXTRAÇÃO DAS TRILHAS DE BASSLINE (E SEUS INSTRUMENTOS)
|
// 3. EXTRAÇÃO DAS TRILHAS DE BASSLINE (CONTAINERS)
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
|
// Isso garante que os blocos azuis apareçam na Playlist.
|
||||||
|
|
||||||
const basslineTracks = bbTrackNodes.map(trackNode => {
|
const basslineContainers = bbTrackNodes.map(trackNode => {
|
||||||
const trackName = trackNode.getAttribute("name") || "Beat/Bassline";
|
const trackName = trackNode.getAttribute("name") || "Beat/Bassline";
|
||||||
|
|
||||||
// A. Extrai os clipes da timeline (blocos azuis)
|
// Extrai os clipes da timeline (blocos azuis)
|
||||||
const playlistClips = Array.from(trackNode.querySelectorAll(":scope > bbtco")).map(bbtco => {
|
const playlistClips = Array.from(trackNode.querySelectorAll(":scope > bbtco")).map(bbtco => {
|
||||||
return {
|
return {
|
||||||
pos: parseInt(bbtco.getAttribute("pos"), 10) || 0,
|
pos: parseInt(bbtco.getAttribute("pos"), 10) || 0,
|
||||||
|
|
@ -250,10 +253,12 @@ export async function parseMmpContent(xmlString) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// B. Extrai os instrumentos INTERNOS desta Bassline
|
// Se não tiver clipes, não cria a trilha visual inútil na playlist
|
||||||
// Eles estão dentro de <bbtrack><trackcontainer><track type="0">
|
if (playlistClips.length === 0) return null;
|
||||||
const internalInstrumentNodes = Array.from(trackNode.querySelectorAll('bbtrack > trackcontainer > track[type="0"]'));
|
|
||||||
|
|
||||||
|
// Extrai também os instrumentos internos apenas para referência (opcional)
|
||||||
|
// mas não os usamos para renderizar no main list para evitar duplicidade de lógica
|
||||||
|
const internalInstrumentNodes = Array.from(trackNode.querySelectorAll('bbtrack > trackcontainer > track[type="0"]'));
|
||||||
const internalInstruments = internalInstrumentNodes
|
const internalInstruments = internalInstrumentNodes
|
||||||
.map(node => parseInstrumentNode(node, sortedBBTrackNameNodes, pathMap))
|
.map(node => parseInstrumentNode(node, sortedBBTrackNameNodes, pathMap))
|
||||||
.filter(t => t !== null);
|
.filter(t => t !== null);
|
||||||
|
|
@ -261,48 +266,41 @@ export async function parseMmpContent(xmlString) {
|
||||||
return {
|
return {
|
||||||
id: `bassline_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
id: `bassline_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
||||||
name: trackName,
|
name: trackName,
|
||||||
type: "bassline",
|
type: "bassline", // Tipo especial para o audio_ui.js
|
||||||
playlist_clips: playlistClips,
|
playlist_clips: playlistClips,
|
||||||
instruments: internalInstruments, // <--- AQUI ESTÁ A CORREÇÃO CRUCIAL!
|
instruments: internalInstruments, // Mantém para o recurso de Double-Click
|
||||||
// Fallbacks para evitar crashes
|
|
||||||
volume: 1,
|
volume: 1,
|
||||||
pan: 0,
|
pan: 0,
|
||||||
patterns: [],
|
patterns: [],
|
||||||
isMuted: trackNode.getAttribute("muted") === "1"
|
isMuted: trackNode.getAttribute("muted") === "1"
|
||||||
};
|
};
|
||||||
}).filter(t => t !== null && (t.playlist_clips.length > 0 || t.instruments.length > 0));
|
}).filter(t => t !== null);
|
||||||
|
|
||||||
// Une tudo: Instrumentos Soltos + Basslines
|
|
||||||
newTracks = [...songTracks, ...basslineTracks];
|
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// 4. INICIALIZAÇÃO DE ÁUDIO E ESTADO
|
// 4. COMBINAÇÃO E FINALIZAÇÃO
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
|
|
||||||
// Inicializa nós de áudio
|
// A lista final contém:
|
||||||
|
// 1. Os Instrumentos (para que os steps apareçam no Pattern Editor)
|
||||||
|
// 2. As Basslines (para que os blocos apareçam na Playlist)
|
||||||
|
|
||||||
|
// Colocamos as Basslines no final ou no início, conforme preferência.
|
||||||
|
// Geralmente, instrumentos primeiro é melhor para o Pattern Editor.
|
||||||
|
const newTracks = [...allInstruments, ...basslineContainers];
|
||||||
|
|
||||||
|
// Inicializa áudio apenas para instrumentos reais
|
||||||
newTracks.forEach((track) => {
|
newTracks.forEach((track) => {
|
||||||
// Se for instrumento normal
|
|
||||||
if (track.type !== 'bassline') {
|
if (track.type !== 'bassline') {
|
||||||
track.volumeNode = new Tone.Volume(Tone.gainToDb(track.volume));
|
track.volumeNode = new Tone.Volume(Tone.gainToDb(track.volume));
|
||||||
track.pannerNode = new Tone.Panner(track.pan);
|
track.pannerNode = new Tone.Panner(track.pan);
|
||||||
track.volumeNode.connect(track.pannerNode);
|
track.volumeNode.connect(track.pannerNode);
|
||||||
track.pannerNode.connect(getMainGainNode());
|
track.pannerNode.connect(getMainGainNode());
|
||||||
}
|
}
|
||||||
// Se for Bassline, inicializa os nós dos instrumentos INTERNOS
|
|
||||||
else if (track.instruments && track.instruments.length > 0) {
|
|
||||||
track.instruments.forEach(inst => {
|
|
||||||
inst.volumeNode = new Tone.Volume(Tone.gainToDb(inst.volume));
|
|
||||||
inst.pannerNode = new Tone.Panner(inst.pan);
|
|
||||||
inst.volumeNode.connect(inst.pannerNode);
|
|
||||||
inst.pannerNode.connect(getMainGainNode());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Configura tamanho da timeline
|
// Configura tamanho da timeline baseado nas notas dos instrumentos
|
||||||
let isFirstTrackWithNotes = true;
|
let isFirstTrackWithNotes = true;
|
||||||
newTracks.forEach(track => {
|
newTracks.forEach(track => {
|
||||||
// Verifica track normal
|
|
||||||
if (track.type !== 'bassline' && isFirstTrackWithNotes) {
|
if (track.type !== 'bassline' && isFirstTrackWithNotes) {
|
||||||
const activePattern = track.patterns[track.activePatternIndex || 0];
|
const activePattern = track.patterns[track.activePatternIndex || 0];
|
||||||
if (activePattern && activePattern.steps && activePattern.steps.length > 0) {
|
if (activePattern && activePattern.steps && activePattern.steps.length > 0) {
|
||||||
|
|
@ -312,55 +310,24 @@ export async function parseMmpContent(xmlString) {
|
||||||
isFirstTrackWithNotes = false;
|
isFirstTrackWithNotes = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Verifica dentro da bassline
|
|
||||||
else if (track.type === 'bassline' && isFirstTrackWithNotes && track.instruments.length > 0) {
|
|
||||||
const firstInst = track.instruments[0];
|
|
||||||
const activePattern = firstInst.patterns[firstInst.activePatternIndex || 0];
|
|
||||||
if (activePattern && activePattern.steps && activePattern.steps.length > 0) {
|
|
||||||
const bars = Math.ceil(activePattern.steps.length / 16);
|
|
||||||
const barsInput = document.getElementById("bars-input");
|
|
||||||
if(barsInput) barsInput.value = bars > 0 ? bars : 1;
|
|
||||||
isFirstTrackWithNotes = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Carrega samples/plugins (Async)
|
// Carrega samples/plugins
|
||||||
try {
|
try {
|
||||||
const promises = [];
|
const promises = newTracks
|
||||||
|
.filter(t => t.type !== 'bassline')
|
||||||
newTracks.forEach(track => {
|
.map(track => loadAudioForTrack(track));
|
||||||
if (track.type !== 'bassline') {
|
|
||||||
promises.push(loadAudioForTrack(track));
|
|
||||||
} else {
|
|
||||||
// Carrega áudio dos instrumentos internos da bassline
|
|
||||||
track.instruments.forEach(inst => {
|
|
||||||
promises.push(loadAudioForTrack(inst));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Ocorreu um erro ao carregar os áudios do projeto:", error);
|
console.error("Erro ao carregar áudios:", error);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Atualiza estado global
|
// Atualiza estado global
|
||||||
appState.pattern.tracks = newTracks;
|
appState.pattern.tracks = newTracks;
|
||||||
|
|
||||||
// Define faixa ativa (tenta pegar a primeira normal ou a primeira de dentro da bassline)
|
// Seleciona o primeiro instrumento real como ativo
|
||||||
let firstInstrumentId = null;
|
const firstInst = newTracks.find(t => t.type !== 'bassline');
|
||||||
const firstInstTrack = newTracks.find(t => t.type !== 'bassline');
|
appState.pattern.activeTrackId = firstInst ? firstInst.id : null;
|
||||||
if (firstInstTrack) {
|
|
||||||
firstInstrumentId = firstInstTrack.id;
|
|
||||||
} else {
|
|
||||||
const firstBassline = newTracks.find(t => t.type === 'bassline' && t.instruments.length > 0);
|
|
||||||
if (firstBassline) {
|
|
||||||
firstInstrumentId = firstBassline.instruments[0].id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
appState.pattern.activeTrackId = firstInstrumentId;
|
|
||||||
appState.pattern.activePatternIndex = 0;
|
appState.pattern.activePatternIndex = 0;
|
||||||
|
|
||||||
loadStateFromSession();
|
loadStateFromSession();
|
||||||
|
|
@ -383,7 +350,6 @@ export function generateMmpFile() {
|
||||||
|
|
||||||
function generateXmlFromState() {
|
function generateXmlFromState() {
|
||||||
if (!appState.global.originalXmlDoc) {
|
if (!appState.global.originalXmlDoc) {
|
||||||
console.log("Gerando XML a partir do template em branco...");
|
|
||||||
const parser = new DOMParser();
|
const parser = new DOMParser();
|
||||||
appState.global.originalXmlDoc = parser.parseFromString(
|
appState.global.originalXmlDoc = parser.parseFromString(
|
||||||
DEFAULT_PROJECT_XML,
|
DEFAULT_PROJECT_XML,
|
||||||
|
|
@ -401,18 +367,16 @@ function generateXmlFromState() {
|
||||||
head.setAttribute("timesig_denominator", document.getElementById("compasso-b-input").value || 4);
|
head.setAttribute("timesig_denominator", document.getElementById("compasso-b-input").value || 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lógica de exportação simplificada:
|
||||||
|
// Remove todos os tracks do container BB e recria com base no estado atual.
|
||||||
|
// Nota: Isso coloca TODOS os instrumentos dentro da Bassline 0 na exportação,
|
||||||
|
// que é o comportamento padrão simplificado para garantir que tudo seja salvo.
|
||||||
const bbTrackContainer = xmlDoc.querySelector('track[type="1"] > bbtrack > trackcontainer');
|
const bbTrackContainer = xmlDoc.querySelector('track[type="1"] > bbtrack > trackcontainer');
|
||||||
if (bbTrackContainer) {
|
if (bbTrackContainer) {
|
||||||
bbTrackContainer.querySelectorAll('track[type="0"]').forEach((node) => node.remove());
|
bbTrackContainer.querySelectorAll('track[type="0"]').forEach((node) => node.remove());
|
||||||
|
|
||||||
// Procura por instrumentos que estão dentro de objetos bassline no state
|
|
||||||
// (Esta lógica de exportação é básica e pode precisar de ajustes se você editar muito os instrumentos internos)
|
|
||||||
const tracksXml = appState.pattern.tracks
|
const tracksXml = appState.pattern.tracks
|
||||||
.flatMap(track => {
|
.filter(t => t.type !== 'bassline') // Ignora container visual
|
||||||
if (track.type === 'bassline') return track.instruments;
|
|
||||||
return [track];
|
|
||||||
})
|
|
||||||
.filter(t => t && t.type !== 'bassline')
|
|
||||||
.map((track) => createTrackXml(track))
|
.map((track) => createTrackXml(track))
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
|
|
@ -493,11 +457,7 @@ function generateNewMmp() {
|
||||||
const num_bars = document.getElementById("bars-input").value;
|
const num_bars = document.getElementById("bars-input").value;
|
||||||
|
|
||||||
const tracksXml = appState.pattern.tracks
|
const tracksXml = appState.pattern.tracks
|
||||||
.flatMap(track => {
|
.filter(t => t.type !== 'bassline')
|
||||||
if (track.type === 'bassline') return track.instruments;
|
|
||||||
return [track];
|
|
||||||
})
|
|
||||||
.filter(t => t && t.type !== 'bassline')
|
|
||||||
.map((track) => createTrackXml(track))
|
.map((track) => createTrackXml(track))
|
||||||
.join("");
|
.join("");
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue