diff --git a/assets/js/creations/file.js b/assets/js/creations/file.js index c993bbff..29ea2cba 100755 --- a/assets/js/creations/file.js +++ b/assets/js/creations/file.js @@ -268,18 +268,26 @@ function parseInstrumentNode( const ticksPerStep = 12; 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); + const pos = parseInt(noteNode.getAttribute("pos"), 10) || 0; - notes.push({ pos, len, key, vol, pan }); + const rawLen = parseInt(noteNode.getAttribute("len"), 10) || 0; - // Calcula qual step acender - const stepIndex = Math.floor(pos / ticksPerStep); + // ✅ LMMS costuma salvar one-shots com len negativo (ex: -192). + // Na nossa DAW, tratamos isso como 1 step (1/16) = 12 ticks. + const len = rawLen < 0 ? TICKS_PER_STEP : rawLen; + + notes.push({ + pos, + len, + key: parseInt(noteNode.getAttribute("key"), 10), + vol: parseInt(noteNode.getAttribute("vol"), 10), + pan: parseInt(noteNode.getAttribute("pan"), 10), + }); + + // stepIndex também deve usar 12 (você já corrigiu isso antes) + const stepIndex = Math.floor(pos / TICKS_PER_STEP); if (stepIndex < patternSteps) steps[stepIndex] = true; - }); + }); return { name: patternName, diff --git a/assets/js/creations/pattern/pattern_audio.js b/assets/js/creations/pattern/pattern_audio.js index 68f273eb..03515757 100755 --- a/assets/js/creations/pattern/pattern_audio.js +++ b/assets/js/creations/pattern/pattern_audio.js @@ -275,24 +275,25 @@ function schedulePianoRoll() { track.instrument ) { // Converte notas para eventos Tone.js - const events = pattern.notes.map((note) => { - // --- CORREÇÃO DE TEMPO (PPQ) --- - // LMMS usa 192 ticks por batida. Tone.js também usa 192 por padrão. - // Precisamos converter o 'pos' (ticks absolutos) para tempo musical + const bpm = parseInt(document.getElementById("bpm-input")?.value, 10) || 120; + const stepSec = 60 / (bpm * 4); // 1/16 - // 1 Beat = 192 ticks - // Tone.Time("0:0:0").toTicks() + const events = pattern.notes.map((note) => { + const posSteps = (note.pos || 0) / TICKS_PER_STEP; + + const rawLen = note.len || 0; + const lenTicks = rawLen < 0 ? TICKS_PER_STEP : rawLen; // defesa extra + const lenSteps = Math.max(1, lenTicks / TICKS_PER_STEP); return { - // Passamos 'ticks' diretamente. O Tone converte baseado no BPM. - time: - 0 + (note.pos * (Tone.Transport.PPQ / 192)) / Tone.Transport.PPQ, + time: posSteps * stepSec, // segundos midi: note.key, - duration: note.len / 192 + "n", // Converte duração baseada em semínimas (192) + duration: lenSteps * stepSec, // segundos velocity: (note.vol || 100) / 100, }; }); + const part = new Tone.Part((time, value) => { if (track.muted) return; const freq = Tone.Frequency(value.midi, "midi"); diff --git a/creation.html b/creation.html index e0fb209b..21d6f55f 100755 --- a/creation.html +++ b/creation.html @@ -809,7 +809,9 @@ CONSTANTS.TOTAL_KEYS - 1 - (note.key - CONSTANTS.START_NOTE); const y = keyIndex * CONSTANTS.NOTE_HEIGHT; const x = note.pos / CONSTANTS.TICKS_PER_PIXEL; - const width = note.len / CONSTANTS.TICKS_PER_PIXEL; + const lenTicks = note.len < 0 ? 12 : note.len; + const width = lenTicks / CONSTANTS.TICKS_PER_PIXEL; + gridCtx.fillRect(x + 1, y + 1, width - 2, CONSTANTS.NOTE_HEIGHT - 2); gridCtx.strokeRect(