Padronização da interface e atualização do sync entre clientes

This commit is contained in:
JotaChina committed 2025-11-27 22:02:02 -03:00
1 parent 3f714c88de
commit 7d9d2b0f63
10 files changed
+796 -377

No files matched your search

+23 -31
View File
@@ -1,4 +1,6 @@
// js/file.js
//--------------------------------------------------------------
// IMPORTS NECESSÁRIOS
//--------------------------------------------------------------
import { appState, saveStateToSession, resetProjectState, loadStateFromSession } from "./state.js";
import { loadAudioForTrack } from "./pattern/pattern_state.js";
import { renderAll, getSamplePathMap } from "./ui.js";
@@ -7,21 +9,13 @@ import {
initializeAudioContext,
getMainGainNode,
} from "./audio.js";
import { DEFAULT_PROJECT_XML } from "./utils.js"
import * as Tone from "https://esm.sh/tone";
import { sendAction } from "./socket.js";
const BLANK_PROJECT_XML = `<?xml version="1.0"?>
<!DOCTYPE lmms-project>
<lmms-project type="song" version="1.0" creatorversion="1.2.2" creator="LMMS">
<head bpm="140" mastervol="100" timesig_numerator="4" timesig_denominator="4" masterpitch="0"/>
<song>
<trackcontainer width="600" height="300" type="song" visible="1" maximized="0" x="5" y="5" minimized="0">
</trackcontainer>
<timeline lp0pos="0" lp1pos="192" lpstate="0"/>
<controllers/>
</song>
</lmms-project>`;
//--------------------------------------------------------------
//
//--------------------------------------------------------------
export function handleLocalProjectReset() {
console.log("Recebido comando de reset. Limpando estado local...");
@@ -89,7 +83,7 @@ export async function loadProjectFromServer(fileName) {
export async function parseMmpContent(xmlString) {
resetProjectState();
initializeAudioContext();
appState.global.justReset = xmlString === BLANK_PROJECT_XML;
appState.global.justReset = xmlString === DEFAULT_PROJECT_XML;
const audioContainer = document.getElementById("audio-track-container");
if (audioContainer) {
@@ -305,10 +299,7 @@ export async function parseMmpContent(xmlString) {
appState.pattern.activeTrackId = appState.pattern.tracks[0]?.id || null;
appState.pattern.activePatternIndex = 0;
// --- SUBSTUIÇÃO DO BLOCO DE RESTAURAÇÃO ---
// Em vez daquele bloco try/catch gigante, apenas chamamos a função:
loadStateFromSession();
// ------------------------------------------
await Promise.resolve();
renderAll();
@@ -324,17 +315,19 @@ export function generateMmpFile() {
function generateXmlFromState() {
if (!appState.global.originalXmlDoc) {
console.warn("Não há XML original. Retornando vazio.");
return "";
console.log("Gerando XML a partir do template em branco...");
const parser = new DOMParser();
appState.global.originalXmlDoc = parser.parseFromString(DEFAULT_PROJECT_XML, "application/xml");
}
const xmlDoc = appState.global.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);
head.setAttribute("bpm", document.getElementById("bpm-input").value || 140);
head.setAttribute("num_bars", document.getElementById("bars-input").value || 1);
head.setAttribute("timesig_numerator", document.getElementById("compasso-a-input").value || 4);
head.setAttribute("timesig_denominator", document.getElementById("compasso-b-input").value || 4);
}
const bbTrackContainer = xmlDoc.querySelector('track[type="1"] > bbtrack > trackcontainer');
@@ -344,7 +337,6 @@ function generateXmlFromState() {
.map((track) => createTrackXml(track))
.join("");
// Gambiarra para inserir o XML gerado como nós DOM
const tempDoc = new DOMParser().parseFromString(`<root>${tracksXml}</root>`, "application/xml");
Array.from(tempDoc.documentElement.children).forEach((newTrackNode) => {
bbTrackContainer.appendChild(newTrackNode);
@@ -369,21 +361,22 @@ function createTrackXml(track) {
const lmmsVolume = Math.round(track.volume * 100);
const lmmsPan = Math.round(track.pan * 100);
// 🔥 PROTEÇÃO: Se não tiver instrumento definido, usa Kicker padrão
const instrName = track.instrumentName || "kicker";
const instrXml = track.instrumentXml || `<kicker><env amt="0" attack="0.01" hold="0.1" decay="0.1" release="0.1" sustain="0.5" sync_mode="0"/></kicker>`;
const patternsXml = track.patterns.map((pattern) => {
let patternNotesXml = "";
// SE for plugin e tiver notas detalhadas, usa elas
if (track.type === "plugin" && pattern.notes && pattern.notes.length > 0) {
patternNotesXml = pattern.notes.map(note => {
return `<note vol="${note.vol}" len="${note.len}" pos="${note.pos}" pan="${note.pan}" key="${note.key}"/>`;
}).join("\n ");
}
// SE for sampler (ou plugin sem notas detalhadas), usa os steps convertidos em notas simples
else {
patternNotesXml = pattern.steps.map((isActive, index) => {
if (isActive) {
const notePos = Math.round(index * ticksPerStep);
// Key 57 é o padrão do LMMS para samples (Lá)
return `<note vol="100" len="${NOTE_LENGTH}" pos="${notePos}" pan="0" key="57"/>`;
}
return "";
@@ -398,8 +391,8 @@ function createTrackXml(track) {
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}">
<instrument name="${track.instrumentName}">
${track.instrumentXml}
<instrument name="${instrName}">
${instrXml}
</instrument>
<fxchain enabled="0" numofeffects="0"/>
</instrumenttrack>
@@ -461,5 +454,4 @@ function downloadFile(content, fileName) {
URL.revokeObjectURL(url);
}
export { generateXmlFromState as generateXmlFromStateExported };
export { BLANK_PROJECT_XML }; // Mantenha o que já estava
export { generateXmlFromState as generateXmlFromStateExported };