melhorando a leitura de projetos no mmpCreator
Deploy / Deploy (push) Successful in 2m41s Details

This commit is contained in:
JotaChina 2025-12-25 12:03:23 -03:00
parent d86348cfac
commit 08302947ad
1 changed files with 52 additions and 45 deletions

View File

@ -225,7 +225,7 @@ export async function loadProjectFromServer(fileName) {
// =================================================================
function parseInstrumentNode(
trackNode,
sortedBBTrackNameNodes,
sortedBBTrackNameNodes, // Esse argumento agora será ignorado para evitar o bug
pathMap,
parentBasslineId = null
) {
@ -237,8 +237,14 @@ function parseInstrumentNode(
const trackName = trackNode.getAttribute("name");
const instrumentName = instrumentNode.getAttribute("name");
// Lógica de Patterns
// Lógica de Patterns:
// CORREÇÃO: Iteramos diretamente sobre os patterns encontrados no XML do instrumento.
// Não tentamos mais mapear 1-para-1 com os clipes da timeline, pois instrumentos de
// Beat/Bassline reutilizam o mesmo pattern várias vezes.
const allPatternsNodeList = trackNode.querySelectorAll("pattern");
// Ordena os patterns pela posição (importante para Song Editor, neutro para BB Editor)
const allPatternsArray = Array.from(allPatternsNodeList).sort((a, b) => {
return (
(parseInt(a.getAttribute("pos"), 10) || 0) -
@ -246,55 +252,56 @@ function parseInstrumentNode(
);
});
const patternsToCreate =
sortedBBTrackNameNodes.length > 0
? sortedBBTrackNameNodes
: [{ getAttribute: () => "Pattern 1" }];
// Se não houver patterns no XML, criamos um vazio para não quebrar a UI
let patterns = [];
const patterns = patternsToCreate.map((bbTrack, index) => {
const patternNode = allPatternsArray[index];
const bbTrackName = bbTrack.getAttribute("name") || `Pattern ${index + 1}`;
if (!patternNode) {
return {
name: bbTrackName,
steps: new Array(16).fill(false),
notes: [],
pos: 0,
};
}
if (allPatternsArray.length > 0) {
patterns = allPatternsArray.map((patternNode, index) => {
// Tenta pegar o nome do atributo, ou gera um genérico
const patternName = patternNode.getAttribute("name") || `Pattern ${index}`;
const patternSteps = parseInt(patternNode.getAttribute("steps"), 10) || 16;
const steps = new Array(patternSteps).fill(false);
const notes = [];
// === CORREÇÃO MATEMÁTICA ===
// No LMMS, 1 semínima (beat) = 192 ticks.
// 1 semicolcheia (1/16 step) = 192 / 4 = 48 ticks.
// Constante de conversão LMMS (192 ticks = 1 beat, 48 ticks = 1 step)
const ticksPerStep = 48;
patternNode.querySelectorAll("note").forEach((noteNode) => {
const pos = parseInt(noteNode.getAttribute("pos"), 10);
notes.push({
pos: pos,
len: parseInt(noteNode.getAttribute("len"), 10),
key: parseInt(noteNode.getAttribute("key"), 10),
vol: parseInt(noteNode.getAttribute("vol"), 10),
pan: parseInt(noteNode.getAttribute("pan"), 10),
});
const len = parseInt(noteNode.getAttribute("len"), 10);
const vol = parseInt(noteNode.getAttribute("vol"), 10);
const pan = parseInt(noteNode.getAttribute("pan"), 10);
const key = parseInt(noteNode.getAttribute("key"), 10);
// Calcula qual quadradinho acender
notes.push({ pos, len, key, vol, pan });
// Calcula qual step acender
// Nota: B/B Editor no LMMS geralmente usa notas de len=48.
// Se a nota for longa, isso marca apenas o step de início.
const stepIndex = Math.round(pos / ticksPerStep);
if (stepIndex < patternSteps) steps[stepIndex] = true;
if (stepIndex >= 0 && stepIndex < patternSteps) {
steps[stepIndex] = true;
}
});
return {
name: bbTrackName,
name: patternName,
steps: steps,
notes: notes,
pos: parseInt(patternNode.getAttribute("pos"), 10) || 0,
};
});
} else {
// Fallback: Nenhum pattern encontrado no XML, cria um padrão vazio
patterns.push({
name: "Pattern 0",
steps: new Array(16).fill(false),
notes: [],
pos: 0
});
}
// Lógica de Sample vs Plugin
let finalSamplePath = null;
@ -326,12 +333,12 @@ function parseInstrumentNode(
type: trackType,
samplePath: finalSamplePath,
patterns: patterns,
activePatternIndex: 0,
activePatternIndex: 0, // Sempre começa mostrando o primeiro pattern disponível
volume: !isNaN(volFromFile) ? volFromFile / 100 : DEFAULT_VOLUME,
pan: !isNaN(panFromFile) ? panFromFile / 100 : DEFAULT_PAN,
instrumentName: instrumentName,
instrumentXml: instrumentNode.innerHTML,
parentBasslineId: parentBasslineId, // Guarda o ID do pai para filtragem na UI
parentBasslineId: parentBasslineId,
};
}