Arrumando botões de volume e pan, criação de novos compassos.
This commit is contained in:
1 parent
e776a830c8
commit
e10621da17
8 files changed
+411
-291
No files matched your search
+67
-32
@@ -1,7 +1,7 @@
|
||||
// js/file.js
|
||||
import { appState } from "./state.js";
|
||||
import { appState, loadAudioForTrack } from "./state.js";
|
||||
import { getTotalSteps } from "./utils.js";
|
||||
import { renderApp } from "./ui.js";
|
||||
import { renderApp, getSamplePathMap } from "./ui.js";
|
||||
import { NOTE_LENGTH, TICKS_PER_BAR } from "./config.js";
|
||||
import {
|
||||
initializeAudioContext,
|
||||
@@ -26,14 +26,14 @@ export async function handleFileLoad(file) {
|
||||
} else {
|
||||
xmlContent = await file.text();
|
||||
}
|
||||
parseMmpContent(xmlContent);
|
||||
await parseMmpContent(xmlContent);
|
||||
} catch (error) {
|
||||
console.error("Erro ao carregar o projeto:", error);
|
||||
alert(`Erro ao carregar projeto: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function parseMmpContent(xmlString) {
|
||||
export async function parseMmpContent(xmlString) {
|
||||
initializeAudioContext();
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(xmlString, "application/xml");
|
||||
@@ -43,16 +43,18 @@ export function parseMmpContent(xmlString) {
|
||||
|
||||
const head = xmlDoc.querySelector("head");
|
||||
if (head) {
|
||||
document.getElementById("bpm-input").value =
|
||||
head.getAttribute("bpm") || 140;
|
||||
document.getElementById("compasso-a-input").value =
|
||||
head.getAttribute("timesig_numerator") || 4;
|
||||
document.getElementById("compasso-b-input").value =
|
||||
head.getAttribute("timesig_denominator") || 4;
|
||||
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;
|
||||
@@ -61,27 +63,34 @@ export function parseMmpContent(xmlString) {
|
||||
|
||||
const audioContext = getAudioContext();
|
||||
const mainGainNode = getMainGainNode();
|
||||
const totalSteps = parseInt(
|
||||
trackNode.querySelector("pattern")?.getAttribute("steps") ||
|
||||
getTotalSteps(),
|
||||
10
|
||||
);
|
||||
|
||||
const totalSteps = getTotalSteps();
|
||||
const newSteps = new Array(totalSteps).fill(false);
|
||||
const ticksPerStep = totalSteps > 0 ? TICKS_PER_BAR / totalSteps : 0;
|
||||
|
||||
// ==================================================================
|
||||
// (CORREÇÃO DEFINITIVA)
|
||||
// 1. Simplificamos o cálculo: cada step de 1/16 vale 12 ticks.
|
||||
const ticksPerStep = 12;
|
||||
|
||||
trackNode.querySelectorAll("pattern note").forEach((noteNode) => {
|
||||
// 2. Buscamos TODAS as notas da trilha, não importa em qual pattern elas estejam.
|
||||
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:
|
||||
afpNode.getAttribute("src").split("/").pop() ||
|
||||
trackNode.getAttribute("name"),
|
||||
samplePath: `samples/${afpNode.getAttribute("src")}`,
|
||||
name: filename || trackNode.getAttribute("name"),
|
||||
samplePath: finalSamplePath,
|
||||
audioBuffer: null,
|
||||
steps: newSteps,
|
||||
volume: parseFloat(instrumentTrackNode.getAttribute("vol")) / 100,
|
||||
pan: parseFloat(instrumentTrackNode.getAttribute("pan")) / 100,
|
||||
@@ -94,6 +103,15 @@ export function parseMmpContent(xmlString) {
|
||||
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);
|
||||
@@ -112,6 +130,7 @@ function generateNewMmp() {
|
||||
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("");
|
||||
@@ -119,7 +138,7 @@ function generateNewMmp() {
|
||||
const mmpContent = `<?xml version="1.0"?>
|
||||
<!DOCTYPE lmms-project>
|
||||
<lmms-project version="1.0" type="song" creator="MMPCreator" creatorversion="1.0">
|
||||
<head mastervol="100" timesig_denominator="${sig_den}" bpm="${bpm}" timesig_numerator="${sig_num}" masterpitch="0"/>
|
||||
<head mastervol="100" timesig_denominator="${sig_den}" bpm="${bpm}" timesig_numerator="${sig_num}" masterpitch="0" num_bars="${num_bars}"/>
|
||||
<song>
|
||||
<trackcontainer type="song">
|
||||
<track type="1" solo="0" muted="0" name="Beat/Bassline 0">
|
||||
@@ -150,6 +169,7 @@ function modifyAndSaveExistingMmp() {
|
||||
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
|
||||
@@ -185,10 +205,10 @@ function modifyAndSaveExistingMmp() {
|
||||
function createTrackXml(track) {
|
||||
if (!track.samplePath) return "";
|
||||
const totalSteps = track.steps.length || getTotalSteps();
|
||||
const ticksPerStep = totalSteps > 0 ? TICKS_PER_BAR / totalSteps : 0;
|
||||
const ticksPerStep = 12; // Valor fixo de 12 ticks por step de 1/16
|
||||
const lmmsVolume = Math.round(track.volume * 100);
|
||||
const lmmsPan = Math.round(track.pan * 100);
|
||||
const sampleSrc = track.samplePath.replace("samples/", "");
|
||||
const sampleSrc = track.samplePath.replace("src/samples/", "");
|
||||
const notesXml = track.steps
|
||||
.map((isActive, index) => {
|
||||
if (isActive) {
|
||||
@@ -198,6 +218,26 @@ function createTrackXml(track) {
|
||||
return "";
|
||||
})
|
||||
.join("\n ");
|
||||
|
||||
// Cria um pattern para cada compasso (16 steps)
|
||||
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 `<note vol="100" len="${NOTE_LENGTH}" pos="${notePos}" pan="0" key="57"/>`;
|
||||
}
|
||||
return "";
|
||||
}).join("\n ");
|
||||
|
||||
patternsXml += `<pattern type="0" pos="${i * TICKS_PER_BAR}" muted="0" steps="${stepsPerBar}" name="Pattern ${i+1}">
|
||||
${patternNotes}
|
||||
</pattern>`
|
||||
}
|
||||
|
||||
return `
|
||||
<track type="0" solo="0" muted="0" name="${track.name}">
|
||||
<instrumenttrack vol="${lmmsVolume}" pitch="0" fxch="0" pitchrange="1" basenote="57" usemasterpitch="1" pan="${lmmsPan}">
|
||||
@@ -206,9 +246,7 @@ function createTrackXml(track) {
|
||||
</instrument>
|
||||
<fxchain enabled="0" numofeffects="0"/>
|
||||
</instrumenttrack>
|
||||
<pattern type="0" pos="0" muted="0" steps="${totalSteps}" name="${track.name}">
|
||||
${notesXml}
|
||||
</pattern>
|
||||
${patternsXml}
|
||||
</track>`;
|
||||
}
|
||||
|
||||
@@ -224,20 +262,17 @@ function downloadFile(content, fileName) {
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// (ALTERADO) Adiciona export na frente da função
|
||||
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();
|
||||
parseMmpContent(xmlContent);
|
||||
// Retorna true em caso de sucesso
|
||||
await parseMmpContent(xmlContent);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Erro ao carregar projeto do servidor:", error);
|
||||
alert(`Erro ao carregar projeto: ${error.message}`);
|
||||
// Retorna false em caso de falha
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user