// js/pattern_state.js import { DEFAULT_VOLUME, DEFAULT_PAN } from "../config.js"; import { getAudioContext, getMainGainNode } from "../audio.js"; import { renderPatternEditor } from "./pattern_ui.js"; import { getTotalSteps } from "../utils.js"; const initialState = { tracks: [], activeTrackId: null, activePatternIndex: 0, }; export let patternState = { ...initialState }; export function initializePatternState() { Object.assign(patternState, initialState, { tracks: [] }); } // --- FUNÇÃO CORRIGIDA --- // Agora, esta função cria e pré-carrega um Tone.Player para a faixa. export async function loadAudioForTrack(track) { if (!track.samplePath) return track; try { // Se já existir um player antigo, o descartamos para liberar memória. if (track.player) { track.player.dispose(); } // Cria um novo Tone.Player e o conecta à cadeia de áudio da faixa. // O 'await' garante que o áudio seja totalmente carregado antes de prosseguirmos. track.player = await new Tone.Player(track.samplePath).toDestination(); track.player.chain(track.gainNode, track.pannerNode, getMainGainNode()); } catch (error) { console.error(`Falha ao carregar áudio para a trilha ${track.name}:`, error); track.player = null; } return track; } export function addTrackToState() { const mainGainNode = getMainGainNode(); const totalSteps = getTotalSteps(); const referenceTrack = patternState.tracks[0]; const newTrack = { id: Date.now(), name: "novo instrumento", samplePath: null, player: null, // <-- ADICIONADO: O player começará como nulo patterns: referenceTrack ? referenceTrack.patterns.map(p => ({ name: p.name, steps: new Array(p.steps.length).fill(false), pos: p.pos })) : [{ name: "Pattern 1", steps: new Array(totalSteps).fill(false), pos: 0 }], activePatternIndex: 0, volume: DEFAULT_VOLUME, pan: DEFAULT_PAN, gainNode: new Tone.Gain(Tone.gainToDb(DEFAULT_VOLUME)), pannerNode: new Tone.Panner(DEFAULT_PAN), }; newTrack.gainNode.chain(newTrack.pannerNode, mainGainNode); patternState.tracks.push(newTrack); renderPatternEditor(); } export function removeLastTrackFromState() { if (patternState.tracks.length > 0) { const trackToRemove = patternState.tracks[patternState.tracks.length - 1]; if (trackToRemove.player) trackToRemove.player.dispose(); if (trackToRemove.pannerNode) trackToRemove.pannerNode.dispose(); if (trackToRemove.gainNode) trackToRemove.gainNode.dispose(); patternState.tracks.pop(); renderPatternEditor(); } } export async function updateTrackSample(trackId, samplePath) { const track = patternState.tracks.find((t) => t.id == trackId); if (track) { track.samplePath = samplePath; track.name = samplePath.split("/").pop(); await loadAudioForTrack(track); // Carrega o novo player renderPatternEditor(); } } export function toggleStepState(trackId, stepIndex) { const track = patternState.tracks.find((t) => t.id == trackId); if (track && track.patterns && track.patterns.length > 0) { const activePattern = track.patterns[track.activePatternIndex]; if (activePattern && activePattern.steps.length > stepIndex) { activePattern.steps[stepIndex] = !activePattern.steps[stepIndex]; } } }