250 lines
6.2 KiB
JavaScript
250 lines
6.2 KiB
JavaScript
// state.js (versão conceitual nova)
|
|
import {
|
|
audioState,
|
|
initializeAudioState,
|
|
getAudioSnapshot,
|
|
applyAudioSnapshot,
|
|
} from "./audio/audio_state.js";
|
|
import { DEFAULT_VOLUME, DEFAULT_PAN } from "./config.js";
|
|
import * as Tone from "https://esm.sh/tone";
|
|
|
|
// ---------------- ESTADOS INICIAIS ----------------
|
|
|
|
const patternState = {
|
|
tracks: [],
|
|
activeTrackId: null,
|
|
activePatternIndex: 0,
|
|
};
|
|
|
|
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,
|
|
syncMode: "global",
|
|
};
|
|
|
|
export let appState = {
|
|
global: globalState,
|
|
pattern: patternState,
|
|
audio: audioState, // compartilhado com módulo de áudio
|
|
};
|
|
|
|
window.appState = appState;
|
|
|
|
// ---------------- HELPERS ----------------
|
|
|
|
function makePatternSnapshot() {
|
|
return {
|
|
tracks: appState.pattern.tracks.map((t) => ({
|
|
id: t.id,
|
|
name: t.name,
|
|
type: t.type,
|
|
samplePath: t.samplePath,
|
|
patterns: (t.patterns || []).map((p) => ({
|
|
name: p.name,
|
|
steps: p.steps,
|
|
notes: p.notes,
|
|
pos: p.pos,
|
|
})),
|
|
activePatternIndex: t.activePatternIndex || 0,
|
|
volume: t.volume,
|
|
pan: t.pan,
|
|
instrumentName: t.instrumentName,
|
|
instrumentXml: t.instrumentXml,
|
|
})),
|
|
activeTrackId: appState.pattern.activeTrackId,
|
|
activePatternIndex: appState.pattern.activePatternIndex,
|
|
};
|
|
}
|
|
|
|
|
|
// ----------------------
|
|
// Helper: existe snapshot local com áudio?
|
|
// ----------------------
|
|
export function hasLocalAudioSnapshot() {
|
|
if (!window.ROOM_NAME) return false;
|
|
const raw = sessionStorage.getItem(`temp_state_${window.ROOM_NAME}`);
|
|
if (!raw) return false;
|
|
|
|
try {
|
|
const data = JSON.parse(raw);
|
|
|
|
// se tem tracks ou clips, consideramos snapshot válido
|
|
const audio = data.audioSnapshot;
|
|
if (!audio) return false;
|
|
|
|
const hasTracks = audio.tracks && audio.tracks.length > 0;
|
|
const hasClips = audio.clips && audio.clips.length > 0;
|
|
|
|
return hasTracks || hasClips;
|
|
} catch (e) {
|
|
console.warn("hasLocalAudioSnapshot: erro ao analisar snapshot", e);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// ----------------------
|
|
// Helper: checa se um snapshot de áudio é "não vazio"
|
|
// ----------------------
|
|
function hasAudioInSnapshot(audioSnapshot) {
|
|
if (!audioSnapshot) return false;
|
|
|
|
const hasTracks =
|
|
Array.isArray(audioSnapshot.tracks) && audioSnapshot.tracks.length > 0;
|
|
const hasClips =
|
|
Array.isArray(audioSnapshot.clips) && audioSnapshot.clips.length > 0;
|
|
|
|
return hasTracks || hasClips;
|
|
}
|
|
|
|
// ---------------- RESET GERAL ----------------
|
|
|
|
export function resetProjectState() {
|
|
console.log("Executando resetProjectState (Limpeza Profunda)...");
|
|
|
|
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,
|
|
syncMode: appState.global.syncMode ?? "global",
|
|
});
|
|
|
|
Object.assign(appState.pattern, {
|
|
tracks: [],
|
|
activeTrackId: null,
|
|
activePatternIndex: 0,
|
|
});
|
|
|
|
initializeAudioState();
|
|
}
|
|
|
|
// ---------------- SALVAR SESSÃO LOCAL ----------------
|
|
|
|
export function saveStateToSession() {
|
|
if (!window.ROOM_NAME) return;
|
|
|
|
try {
|
|
// Pattern “puro”
|
|
const patternSnapshot = makePatternSnapshot();
|
|
|
|
// XML original em string
|
|
let originalXmlString = null;
|
|
if (appState.global.originalXmlDoc) {
|
|
const serializer = new XMLSerializer();
|
|
try {
|
|
originalXmlString = serializer.serializeToString(
|
|
appState.global.originalXmlDoc
|
|
);
|
|
} catch (e) {
|
|
console.warn("Não consegui serializar originalXmlDoc:", e);
|
|
}
|
|
}
|
|
|
|
const audioSnapshot = getAudioSnapshot();
|
|
|
|
const snapshot = {
|
|
version: 1,
|
|
global: {
|
|
...appState.global,
|
|
playbackIntervalId: null,
|
|
originalXmlDoc: originalXmlString,
|
|
},
|
|
pattern: patternSnapshot,
|
|
audioSnapshot,
|
|
};
|
|
|
|
sessionStorage.setItem(
|
|
`temp_state_${window.ROOM_NAME}`,
|
|
JSON.stringify(snapshot)
|
|
);
|
|
} catch (e) {
|
|
console.error("Erro salvando sessão:", e);
|
|
}
|
|
}
|
|
|
|
// ---------------- CARREGAR SESSÃO LOCAL ----------------
|
|
// mode:
|
|
// - "full" (global + pattern + audio)
|
|
// - "audioOnly" (apenas áudio, usado como fallback
|
|
// depois de carregar XML do servidor)
|
|
|
|
export async function loadStateFromSession(mode = "full") {
|
|
if (!window.ROOM_NAME) return false;
|
|
|
|
const raw = sessionStorage.getItem(`temp_state_${window.ROOM_NAME}`);
|
|
if (!raw) return false;
|
|
|
|
try {
|
|
const data = JSON.parse(raw);
|
|
|
|
// GLOBAL & PATTERN só se for "full"
|
|
if (mode === "full") {
|
|
if (data.global) {
|
|
const { originalXmlDoc: xmlString, ...rest } = data.global;
|
|
Object.assign(appState.global, rest);
|
|
|
|
if (xmlString) {
|
|
try {
|
|
const parser = new DOMParser();
|
|
appState.global.originalXmlDoc = parser.parseFromString(
|
|
xmlString,
|
|
"application/xml"
|
|
);
|
|
} catch (e) {
|
|
console.warn("Falha ao reconstituir originalXmlDoc:", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (data.pattern) {
|
|
Object.assign(appState.pattern, data.pattern);
|
|
}
|
|
}
|
|
|
|
// ÁUDIO em qualquer modo, se existir
|
|
if (hasAudioInSnapshot(data.audioSnapshot)) {
|
|
initializeAudioState();
|
|
await applyAudioSnapshot(data.audioSnapshot);
|
|
}
|
|
|
|
return true;
|
|
} catch (e) {
|
|
console.error("Erro carregando sessão:", e);
|
|
return false;
|
|
}
|
|
}
|