diff --git a/assets/js/creations/file.js b/assets/js/creations/file.js index 02a87842..4d5590ed 100755 --- a/assets/js/creations/file.js +++ b/assets/js/creations/file.js @@ -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 (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 = []; - if (!patternNode) { - return { - name: bbTrackName, - steps: new Array(16).fill(false), - notes: [], - pos: 0, - }; - } + // Constante de conversão LMMS (192 ticks = 1 beat, 48 ticks = 1 step) + const ticksPerStep = 48; - const patternSteps = parseInt(patternNode.getAttribute("steps"), 10) || 16; - const steps = new Array(patternSteps).fill(false); - const notes = []; + patternNode.querySelectorAll("note").forEach((noteNode) => { + const pos = parseInt(noteNode.getAttribute("pos"), 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); - // === CORREÇÃO MATEMÁTICA === - // No LMMS, 1 semínima (beat) = 192 ticks. - // 1 semicolcheia (1/16 step) = 192 / 4 = 48 ticks. - const ticksPerStep = 48; + notes.push({ pos, len, key, vol, pan }); - 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), + // 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 >= 0 && stepIndex < patternSteps) { + steps[stepIndex] = true; + } }); - // Calcula qual quadradinho acender - const stepIndex = Math.round(pos / ticksPerStep); - if (stepIndex < patternSteps) steps[stepIndex] = true; + return { + name: patternName, + steps: steps, + notes: notes, + pos: parseInt(patternNode.getAttribute("pos"), 10) || 0, + }; }); - - return { - name: bbTrackName, - 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, }; }