mmpSearch/assets/js/creations/state.js

196 lines
5.9 KiB
JavaScript

// js/state.js
import { initializePatternState } from "./pattern/pattern_state.js";
import { audioState, initializeAudioState, getAudioSnapshot, applyAudioSnapshot } from "./audio/audio_state.js";
import { DEFAULT_VOLUME, DEFAULT_PAN } from "./config.js";
import { generateMmpFile } from "./file.js"; // Vamos usar uma adaptação disto ou criar um helper
import { parseMmpContent } from "./file.js"; // Importante para restaurar o XML
// Estado global da aplicação
const globalState = {
sliceToolActive: false,
isPlaying: false,
isAudioEditorPlaying: false,
playbackIntervalId: null,
currentStep: 0,
metronomeEnabled: false,
originalXmlDoc: null,
currentBeatBasslineName: "Novo Projeto",
masterVolume: DEFAULT_VOLUME,
masterPan: DEFAULT_PAN,
zoomLevelIndex: 2,
isLoopActive: false,
loopStartTime: 0,
loopEndTime: 8,
resizeMode: "trim",
selectedClipId: null,
isRecording: false,
clipboard: null,
lastRulerClickTime: 0,
justReset: false,
// Configs Globais que devem persistir
bpm: 140,
compassoA: 4,
compassoB: 4,
bars: 1,
syncMode: "global" // Adicionado para persistir a escolha
};
const patternState = {
tracks: [],
activeTrackId: null,
activePatternIndex: 0,
};
// Combina todos os estados em um único objeto namespaced
export let appState = {
global: globalState,
pattern: patternState,
audio: audioState,
};
window.appState = appState;
export function resetProjectState() {
console.log("Executando resetProjectState completo...");
Object.assign(appState.global, {
sliceToolActive: false,
isPlaying: false,
isAudioEditorPlaying: false,
playbackIntervalId: null,
currentStep: 0,
metronomeEnabled: false,
originalXmlDoc: null,
currentBeatBasslineName: "Novo Projeto",
masterVolume: DEFAULT_VOLUME,
masterPan: DEFAULT_PAN,
zoomLevelIndex: 2,
isLoopActive: false,
loopStartTime: 0,
loopEndTime: 8,
resizeMode: "trim",
selectedClipId: null,
isRecording: false,
clipboard: null,
lastRulerClickTime: 0,
justReset: false,
});
Object.assign(appState.pattern, {
tracks: [],
activeTrackId: null,
activePatternIndex: 0,
});
initializeAudioState();
}
// --- NOVA IMPLEMENTAÇÃO DE PERSISTÊNCIA ---
/**
* Helper: Gera a string XML do estado atual.
* Precisamos "emprestar" a lógica do file.js sem baixar o arquivo.
* A maneira mais limpa é importar generateXmlFromState de file.js,
* mas como ela não é exportada lá, vamos usar uma estratégia via DOM
* ou mover a lógica. Para simplificar sem quebrar o file.js,
* vamos salvar apenas o "essencial" do patternState se o XML falhar,
* mas o ideal é garantir que o XML seja gerado.
* * NOTA: Para que isso funcione perfeitamente, certifique-se de exportar
* 'generateXmlFromState' no seu file.js ou usar a lógica abaixo.
*/
import { generateXmlFromStateExported } from "./file.js"; // Vamos assumir que você vai exportar isso no file.js
export function saveStateToSession() {
if (!window.ROOM_NAME) return;
// 1. Crie uma versão "limpa" dos tracks (PATTERN)
const cleanTracks = appState.pattern.tracks.map((track) => {
return {
id: track.id,
name: track.name,
samplePath: track.samplePath,
patterns: track.patterns,
activePatternIndex: track.activePatternIndex,
volume: track.volume,
pan: track.pan,
instrumentName: track.instrumentName,
instrumentXml: track.instrumentXml,
};
});
// 2. Construa o objeto de estado final para salvar
const stateToSave = {
pattern: {
...appState.pattern,
tracks: cleanTracks,
},
// 🔥 FIX: Salvando o estado de Áudio (Tracks e Clips)
audio: {
tracks: appState.audio.tracks || [],
clips: appState.audio.clips || [],
// Adicione outras props de áudio se necessário (ex: seekTime, etc)
},
global: {
bpm: document.getElementById("bpm-input").value,
compassoA: document.getElementById("compasso-a-input").value,
compassoB: document.getElementById("compasso-b-input").value,
bars: document.getElementById("bars-input").value,
syncMode: appState.global.syncMode, // É bom salvar isso também
},
};
try {
const roomName = window.ROOM_NAME || "default_room";
sessionStorage.setItem(
`temp_state_${roomName}`,
JSON.stringify(stateToSave)
);
} catch (e) {
console.error("Falha ao salvar estado na sessão:", e);
}
}
export async function loadStateFromSession() {
const roomName = window.ROOM_NAME;
if (!roomName) return false;
try {
const raw = sessionStorage.getItem(`mmp_backup_${roomName}`);
if (!raw) return false;
const backup = JSON.parse(raw);
console.log(`[Session] Recuperando backup de ${new Date(backup.timestamp).toLocaleTimeString()}`);
// 1. Restaura Globais
if (backup.global) {
appState.global.bpm = backup.global.bpm;
appState.global.compassoA = backup.global.compassoA;
appState.global.compassoB = backup.global.compassoB;
appState.global.bars = backup.global.bars;
appState.global.syncMode = backup.global.syncMode || "global";
// Atualiza DOM imediatamente para evitar desincronia visual
const bpmInput = document.getElementById("bpm-input");
if(bpmInput) bpmInput.value = backup.global.bpm;
// ... (outros inputs se necessário, mas o renderAll cuida da maioria)
}
// 2. Restaura Pattern (XML é preferível)
if (backup.xml) {
await parseMmpContent(backup.xml);
} else if (backup.patternRaw) {
// Fallback simples (menos robusto que XML)
console.warn("Restaurando de Pattern Raw (backup antigo/incompleto)");
Object.assign(appState.pattern, backup.patternRaw);
}
// 3. Restaura Áudio (Clipes + Gravações)
if (backup.audio) {
await applyAudioSnapshot(backup.audio);
}
return true; // Sucesso
} catch (e) {
console.error("Erro ao restaurar sessão:", e);
return false;
}
}