// js/file.js import { appState, loadAudioForTrack } from "./state.js"; import { getTotalSteps } from "./utils.js"; import { renderApp, getSamplePathMap } from "./ui.js"; import { NOTE_LENGTH, TICKS_PER_BAR } from "./config.js"; import { initializeAudioContext, getAudioContext, getMainGainNode, } from "./audio.js"; export async function handleFileLoad(file) { let xmlContent = ""; try { if (file.name.toLowerCase().endsWith(".mmpz")) { const jszip = new JSZip(); const zip = await jszip.loadAsync(file); const projectFile = Object.keys(zip.files).find((name) => name.toLowerCase().endsWith(".mmp") ); if (!projectFile) throw new Error( "Não foi possível encontrar um arquivo .mmp dentro do .mmpz" ); xmlContent = await zip.files[projectFile].async("string"); } else { xmlContent = await file.text(); } await parseMmpContent(xmlContent); } catch (error) { console.error("Erro ao carregar o projeto:", error); alert(`Erro ao carregar projeto: ${error.message}`); } } export async function parseMmpContent(xmlString) { initializeAudioContext(); const parser = new DOMParser(); const xmlDoc = parser.parseFromString(xmlString, "application/xml"); appState.originalXmlDoc = xmlDoc; const newTracks = []; const head = xmlDoc.querySelector("head"); if (head) { document.getElementById("bpm-input").value = head.getAttribute("bpm") || 140; document.getElementById("bars-input").value = head.getAttribute("num_bars") || 1; document.getElementById("compasso-a-input").value = head.getAttribute("timesig_numerator") || 4; document.getElementById("compasso-b-input").value = head.getAttribute("timesig_denominator") || 4; } const sampleTrackElements = xmlDoc.querySelectorAll( 'instrument[name="audiofileprocessor"]' ); const pathMap = getSamplePathMap(); sampleTrackElements.forEach((instrumentNode) => { const afpNode = instrumentNode.querySelector("audiofileprocessor"); const instrumentTrackNode = instrumentNode.parentElement; const trackNode = instrumentTrackNode.parentElement; if (!afpNode || !instrumentTrackNode || !trackNode) return; const audioContext = getAudioContext(); const mainGainNode = getMainGainNode(); const totalSteps = getTotalSteps(); const newSteps = new Array(totalSteps).fill(false); const ticksPerStep = 12; trackNode.querySelectorAll("note").forEach((noteNode) => { const pos = parseInt(noteNode.getAttribute("pos"), 10); const stepIndex = Math.round(pos / ticksPerStep); if (stepIndex < totalSteps) { newSteps[stepIndex] = true; } }); const srcAttribute = afpNode.getAttribute("src"); const filename = srcAttribute.split("/").pop(); const finalSamplePath = pathMap[filename] || `src/samples/${srcAttribute}`; const newTrack = { id: Date.now() + Math.random(), name: filename || trackNode.getAttribute("name"), samplePath: finalSamplePath, audioBuffer: null, steps: newSteps, volume: parseFloat(instrumentTrackNode.getAttribute("vol")) / 100, pan: parseFloat(instrumentTrackNode.getAttribute("pan")) / 100, gainNode: audioContext.createGain(), pannerNode: audioContext.createStereoPanner(), }; newTrack.gainNode.connect(newTrack.pannerNode); newTrack.pannerNode.connect(mainGainNode); newTrack.gainNode.gain.value = newTrack.volume; newTrack.pannerNode.pan.value = newTrack.pan; newTracks.push(newTrack); }); try { const trackLoadPromises = newTracks.map(track => loadAudioForTrack(track)); await Promise.all(trackLoadPromises); console.log("Todos os áudios do projeto foram carregados."); } catch (error) { console.error("Ocorreu um erro ao carregar os áudios do projeto:", error); } appState.tracks = newTracks; renderApp(); console.log("Projeto carregado com sucesso!", appState); } export function generateMmpFile() { if (appState.originalXmlDoc) { modifyAndSaveExistingMmp(); } else { generateNewMmp(); } } function generateNewMmp() { console.log("Gerando novo arquivo .mmp do zero..."); const bpm = document.getElementById("bpm-input").value; const sig_num = document.getElementById("compasso-a-input").value; const sig_den = document.getElementById("compasso-b-input").value; const num_bars = document.getElementById("bars-input").value; const tracksXml = appState.tracks .map((track) => createTrackXml(track)) .join(""); const mmpContent = ` ${tracksXml}

Feito com MMPCreator no https://alice.ufsj.edu.br/MMPSearch/creator

]]>
`; downloadFile(mmpContent, "novo_projeto.mmp"); } function modifyAndSaveExistingMmp() { console.log("Modificando arquivo .mmp existente..."); const xmlDoc = appState.originalXmlDoc.cloneNode(true); const head = xmlDoc.querySelector("head"); if (head) { head.setAttribute("bpm", document.getElementById("bpm-input").value); head.setAttribute("num_bars", document.getElementById("bars-input").value); head.setAttribute( "timesig_numerator", document.getElementById("compasso-a-input").value ); head.setAttribute( "timesig_denominator", document.getElementById("compasso-b-input").value ); } const bbTrackContainer = xmlDoc.querySelector("bbtrack > trackcontainer"); if (bbTrackContainer) { const oldSampleTracks = bbTrackContainer.querySelectorAll( 'instrument[name="audiofileprocessor"]' ); oldSampleTracks.forEach((node) => node.closest("track").remove()); const tracksXml = appState.tracks .map((track) => createTrackXml(track)) .join(""); const tempDoc = new DOMParser().parseFromString( `${tracksXml}`, "application/xml" ); Array.from(tempDoc.documentElement.children).forEach((newTrackNode) => { bbTrackContainer.appendChild(newTrackNode); }); } const serializer = new XMLSerializer(); const mmpContent = serializer.serializeToString(xmlDoc); downloadFile(mmpContent, "projeto_editado.mmp"); } function createTrackXml(track) { if (!track.samplePath) return ""; const totalSteps = track.steps.length || getTotalSteps(); const ticksPerStep = 12; const lmmsVolume = Math.round(track.volume * 100); const lmmsPan = Math.round(track.pan * 100); const sampleSrc = track.samplePath.replace("src/samples/", ""); let patternsXml = ''; const stepsPerBar = 16; const numBars = Math.ceil(totalSteps / stepsPerBar); for (let i = 0; i < numBars; i++) { const patternNotes = track.steps.slice(i * stepsPerBar, (i + 1) * stepsPerBar).map((isActive, index) => { if (isActive) { const notePos = Math.round(index * ticksPerStep); return ``; } return ""; }).join("\n "); patternsXml += ` ${patternNotes} ` } return ` ${patternsXml} `; } function downloadFile(content, fileName) { const blob = new Blob([content], { type: "application/xml;charset=utf-8" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } export async function loadProjectFromServer(fileName) { try { const response = await fetch(`mmp/${fileName}`); if (!response.ok) throw new Error(`Não foi possível carregar o arquivo ${fileName}`); const xmlContent = await response.text(); await parseMmpContent(xmlContent); return true; } catch (error) { console.error("Erro ao carregar projeto do servidor:", error); alert(`Erro ao carregar projeto: ${error.message}`); return false; } }