correção de estado circular na sessão
Deploy / Deploy (push) Successful in 1m40s
Details
Deploy / Deploy (push) Successful in 1m40s
Details
This commit is contained in:
parent
0947156d7e
commit
5e331243b7
|
|
@ -1,5 +1,5 @@
|
|||
// js/file.js
|
||||
import { appState, saveStateToSession, resetProjectState } from "./state.js";
|
||||
import { appState, saveStateToSession, resetProjectState, loadStateFromSession } from "./state.js";
|
||||
import { loadAudioForTrack } from "./pattern/pattern_state.js";
|
||||
import { renderAll, getSamplePathMap } from "./ui.js";
|
||||
import { DEFAULT_PAN, DEFAULT_VOLUME, NOTE_LENGTH } from "./config.js";
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -3,7 +3,7 @@
|
|||
// -------------------relómm------------------------------------------------------
|
||||
// IMPORTS & STATE
|
||||
// -----------------------------------------------------------------------------
|
||||
import { appState, saveStateToSession } from "./state.js";
|
||||
import { appState, saveStateToSession, loadStateFromSession } from "./state.js";
|
||||
import {
|
||||
addTrackToState,
|
||||
removeLastTrackFromState,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
// js/state.js
|
||||
import { initializePatternState } from "./pattern/pattern_state.js";
|
||||
import { audioState, initializeAudioState } from "./audio/audio_state.js";
|
||||
import { DEFAULT_VOLUME, DEFAULT_PAN } from "./config.js";
|
||||
import * as Tone from "https://esm.sh/tone"; // Adicione esta importação
|
||||
import * as Tone from "https://esm.sh/tone";
|
||||
|
||||
// --- DEFINIÇÃO DOS ESTADOS INICIAIS ---
|
||||
|
||||
const patternState = {
|
||||
tracks: [],
|
||||
activeTrackId: null,
|
||||
activePatternIndex: 0,
|
||||
};
|
||||
|
||||
// Estado global da aplicação
|
||||
const globalState = {
|
||||
sliceToolActive: false,
|
||||
isPlaying: false,
|
||||
|
|
@ -26,16 +32,9 @@ const globalState = {
|
|||
clipboard: null,
|
||||
lastRulerClickTime: 0,
|
||||
justReset: false,
|
||||
syncMode: "global",
|
||||
};
|
||||
|
||||
// Define o ESTADO INICIAL para o pattern module
|
||||
const patternState = {
|
||||
tracks: [],
|
||||
activeTrackId: null,
|
||||
activePatternIndex: 0,
|
||||
};
|
||||
|
||||
// Combina todos os estados em um único objeto namespaced
|
||||
export let appState = {
|
||||
global: globalState,
|
||||
pattern: patternState,
|
||||
|
|
@ -44,9 +43,9 @@ export let appState = {
|
|||
window.appState = appState;
|
||||
|
||||
export function resetProjectState() {
|
||||
console.log("Executando resetProjectState completo...");
|
||||
console.log("Executando resetProjectState (Limpeza Profunda)...");
|
||||
|
||||
// 1. Reseta o estado global
|
||||
// 1. Reseta Global
|
||||
Object.assign(appState.global, {
|
||||
sliceToolActive: false,
|
||||
isPlaying: false,
|
||||
|
|
@ -68,52 +67,68 @@ export function resetProjectState() {
|
|||
clipboard: null,
|
||||
lastRulerClickTime: 0,
|
||||
justReset: false,
|
||||
// syncMode mantemos o que estava
|
||||
});
|
||||
|
||||
// 2. Reseta o estado do pattern
|
||||
// 2. Reseta Pattern
|
||||
Object.assign(appState.pattern, {
|
||||
tracks: [],
|
||||
activeTrackId: null,
|
||||
activePatternIndex: 0,
|
||||
});
|
||||
|
||||
// 3. Reseta o estado de áudio (Força bruta para garantir limpeza na memória)
|
||||
// 3. Reseta Áudio (Remove tudo da memória)
|
||||
if (appState.audio) {
|
||||
appState.audio.tracks = [];
|
||||
appState.audio.clips = [];
|
||||
appState.audio.audioEditorSeekTime = 0;
|
||||
}
|
||||
|
||||
initializeAudioState();
|
||||
}
|
||||
|
||||
/**
|
||||
* SALVAR (Serialization):
|
||||
* Transforma o estado complexo em um JSON leve (apenas strings e números).
|
||||
* Remove objetos cíclicos como AudioBuffers e Nodes do Tone.js.
|
||||
*/
|
||||
export function saveStateToSession() {
|
||||
if (!window.ROOM_NAME) return;
|
||||
|
||||
// 1. Crie uma versão "limpa" dos tracks
|
||||
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,
|
||||
};
|
||||
});
|
||||
// 1. Sanitiza Tracks do Pattern (Instrumentos)
|
||||
const cleanPatternTracks = appState.pattern.tracks.map((track) => ({
|
||||
id: track.id,
|
||||
name: track.name,
|
||||
samplePath: track.samplePath, // Caminho do arquivo (String)
|
||||
patterns: track.patterns,
|
||||
activePatternIndex: track.activePatternIndex,
|
||||
volume: track.volume,
|
||||
pan: track.pan,
|
||||
instrumentName: track.instrumentName,
|
||||
instrumentXml: track.instrumentXml,
|
||||
// Note: NÃO salvamos volumeNode, pannerNode, etc.
|
||||
}));
|
||||
|
||||
// 2. Sanitiza Clipes de Áudio (Timeline)
|
||||
// AQUI ESTÁ O SEGREDO: Salvamos apenas o 'filePath', não o buffer de áudio.
|
||||
const cleanAudioClips = (appState.audio.clips || []).map((clip) => ({
|
||||
id: clip.id,
|
||||
trackId: clip.trackId,
|
||||
name: clip.name,
|
||||
filePath: clip.filePath, // O endereço do áudio
|
||||
startTimeInSeconds: clip.startTimeInSeconds,
|
||||
durationInSeconds: clip.durationInSeconds,
|
||||
offset: clip.offset,
|
||||
pitch: clip.pitch,
|
||||
originalDuration: clip.originalDuration,
|
||||
patternData: clip.patternData, // Visualização dos steps (leve)
|
||||
// Note: NÃO salvamos clip.buffer (que é o áudio pesado)
|
||||
}));
|
||||
|
||||
// 2. Construa o objeto de estado final para salvar
|
||||
const stateToSave = {
|
||||
pattern: {
|
||||
...appState.pattern,
|
||||
tracks: cleanTracks,
|
||||
},
|
||||
pattern: { ...appState.pattern, tracks: cleanPatternTracks },
|
||||
audio: {
|
||||
tracks: appState.audio.tracks || [],
|
||||
clips: appState.audio.clips || [],
|
||||
tracks: appState.audio.tracks || [], // Tracks são apenas containers leves
|
||||
clips: cleanAudioClips
|
||||
},
|
||||
global: {
|
||||
bpm: document.getElementById("bpm-input")?.value || 140,
|
||||
|
|
@ -126,78 +141,98 @@ export function saveStateToSession() {
|
|||
|
||||
try {
|
||||
const roomName = window.ROOM_NAME || "default_room";
|
||||
sessionStorage.setItem(
|
||||
`temp_state_${roomName}`,
|
||||
JSON.stringify(stateToSave)
|
||||
);
|
||||
// Agora o JSON.stringify funciona porque só tem dados simples
|
||||
sessionStorage.setItem(`temp_state_${roomName}`, JSON.stringify(stateToSave));
|
||||
} catch (e) {
|
||||
console.error("Falha ao salvar estado na sessão:", e);
|
||||
console.error("Erro ao salvar sessão:", e);
|
||||
}
|
||||
}
|
||||
|
||||
// --- NOVA FUNÇÃO PARA CORRIGIR O ERRO ---
|
||||
export function loadStateFromSession() {
|
||||
/**
|
||||
* CARREGAR (Hydration):
|
||||
* Lê o JSON leve e RECONSTRÓI os objetos pesados (carrega os arquivos via HTTP).
|
||||
*/
|
||||
export async function loadStateFromSession() {
|
||||
const roomName = window.ROOM_NAME || "default_room";
|
||||
const tempStateJSON = sessionStorage.getItem(`temp_state_${roomName}`);
|
||||
|
||||
if (!tempStateJSON) return false;
|
||||
|
||||
console.log("Restaurando estado da sessão...");
|
||||
console.log("Hidratando estado da sessão...");
|
||||
try {
|
||||
const tempState = JSON.parse(tempStateJSON);
|
||||
|
||||
// 1. Restaura Pattern Tracks
|
||||
// Precisamos recriar os Nodes do Tone.js que não foram salvos
|
||||
appState.pattern.tracks.forEach((liveTrack) => {
|
||||
const savedTrack = tempState.pattern.tracks.find(
|
||||
(t) => t.id === liveTrack.id
|
||||
);
|
||||
const savedTrack = tempState.pattern.tracks.find(t => t.id === liveTrack.id);
|
||||
if (savedTrack) {
|
||||
liveTrack.name = savedTrack.name;
|
||||
liveTrack.patterns = savedTrack.patterns;
|
||||
liveTrack.activePatternIndex = savedTrack.activePatternIndex;
|
||||
liveTrack.volume = savedTrack.volume;
|
||||
liveTrack.pan = savedTrack.pan;
|
||||
// Copia dados simples
|
||||
Object.assign(liveTrack, {
|
||||
name: savedTrack.name,
|
||||
patterns: savedTrack.patterns,
|
||||
activePatternIndex: savedTrack.activePatternIndex,
|
||||
volume: savedTrack.volume,
|
||||
pan: savedTrack.pan
|
||||
});
|
||||
|
||||
if (liveTrack.volumeNode) {
|
||||
liveTrack.volumeNode.volume.value = Tone.gainToDb(savedTrack.volume);
|
||||
}
|
||||
if (liveTrack.pannerNode) {
|
||||
liveTrack.pannerNode.pan.value = savedTrack.pan;
|
||||
}
|
||||
// Reconecta Nodes de Áudio (Hidratação)
|
||||
if (liveTrack.volumeNode) liveTrack.volumeNode.volume.value = Tone.gainToDb(savedTrack.volume);
|
||||
if (liveTrack.pannerNode) liveTrack.pannerNode.pan.value = savedTrack.pan;
|
||||
}
|
||||
});
|
||||
|
||||
// Filtra tracks que não existem mais no salvo
|
||||
appState.pattern.tracks = appState.pattern.tracks.filter((liveTrack) =>
|
||||
tempState.pattern.tracks.some((t) => t.id === liveTrack.id)
|
||||
// Sincroniza lista de tracks (remove deletadas)
|
||||
appState.pattern.tracks = appState.pattern.tracks.filter(liveTrack =>
|
||||
tempState.pattern.tracks.some(t => t.id === liveTrack.id)
|
||||
);
|
||||
|
||||
// 2. Restaura Áudio
|
||||
// 2. Restaura Áudio Timeline (A parte mais importante)
|
||||
if (tempState.audio) {
|
||||
appState.audio.tracks = tempState.audio.tracks || [];
|
||||
appState.audio.clips = tempState.audio.clips || [];
|
||||
|
||||
const clipsMetadata = tempState.audio.clips || [];
|
||||
const loadedClips = [];
|
||||
|
||||
console.log(`Recarregando ${clipsMetadata.length} clips de áudio...`);
|
||||
|
||||
// Para cada clipe salvo, baixamos o áudio novamente usando o filePath
|
||||
for (const metaClip of clipsMetadata) {
|
||||
let buffer = null;
|
||||
if (metaClip.filePath) {
|
||||
try {
|
||||
// Tone.Buffer carrega o arquivo .wav/.mp3 da URL
|
||||
buffer = await new Tone.Buffer(metaClip.filePath).loaded;
|
||||
} catch (err) {
|
||||
console.warn(`Arquivo não encontrado: ${metaClip.filePath}`, err);
|
||||
}
|
||||
}
|
||||
|
||||
// Recria o objeto completo na memória
|
||||
loadedClips.push({
|
||||
...metaClip, // Pega id, start, duration, pitch...
|
||||
buffer: buffer // Anexa o áudio pesado recém-carregado
|
||||
});
|
||||
}
|
||||
appState.audio.clips = loadedClips;
|
||||
}
|
||||
|
||||
// 3. Restaura Global
|
||||
// 3. Restaura Global UI
|
||||
if (tempState.global) {
|
||||
const bpmInput = document.getElementById("bpm-input");
|
||||
if (bpmInput) bpmInput.value = tempState.global.bpm;
|
||||
const g = tempState.global;
|
||||
const setVal = (id, val) => { const el = document.getElementById(id); if(el) el.value = val; };
|
||||
|
||||
const compassoA = document.getElementById("compasso-a-input");
|
||||
if (compassoA) compassoA.value = tempState.global.compassoA;
|
||||
setVal("bpm-input", g.bpm);
|
||||
setVal("compasso-a-input", g.compassoA);
|
||||
setVal("compasso-b-input", g.compassoB);
|
||||
setVal("bars-input", g.bars);
|
||||
|
||||
const compassoB = document.getElementById("compasso-b-input");
|
||||
if (compassoB) compassoB.value = tempState.global.compassoB;
|
||||
|
||||
const barsInput = document.getElementById("bars-input");
|
||||
if (barsInput) barsInput.value = tempState.global.bars;
|
||||
|
||||
if (tempState.global.syncMode) {
|
||||
appState.global.syncMode = tempState.global.syncMode;
|
||||
const syncBtn = document.getElementById("sync-mode-btn");
|
||||
if (syncBtn) {
|
||||
syncBtn.classList.toggle("active", tempState.global.syncMode === "global");
|
||||
syncBtn.textContent = tempState.global.syncMode === "global" ? "Global" : "Local";
|
||||
if (g.syncMode) {
|
||||
appState.global.syncMode = g.syncMode;
|
||||
const btn = document.getElementById("sync-mode-btn");
|
||||
if (btn) {
|
||||
btn.classList.toggle("active", g.syncMode === "global");
|
||||
btn.textContent = g.syncMode === "global" ? "Global" : "Local";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -205,7 +240,7 @@ export function loadStateFromSession() {
|
|||
appState.pattern.activeTrackId = tempState.pattern.activeTrackId;
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error("Erro ao carregar estado da sessão:", e);
|
||||
console.error("Erro crítico ao carregar sessão:", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue