import { useState, useCallback, useEffect } from 'react'; import type { Track } from '@/types/track'; import { createTrack, createTrackFromBuffer } from '@/lib/audio/track-utils'; import { createEffectChain } from '@/lib/audio/effects/chain'; const STORAGE_KEY = 'audio-ui-multi-track'; export function useMultiTrack() { const [tracks, setTracks] = useState(() => { if (typeof window === 'undefined') return []; try { const saved = localStorage.getItem(STORAGE_KEY); if (saved) { const parsed = JSON.parse(saved); // Clear corrupted data immediately if we detect issues const hasInvalidData = parsed.some((t: any) => typeof t.name !== 'string' || t.name === '[object Object]' ); if (hasInvalidData) { console.warn('Detected corrupted track data in localStorage, clearing...'); localStorage.removeItem(STORAGE_KEY); return []; } // Note: AudioBuffers can't be serialized, but EffectChains can return parsed.map((t: any) => ({ ...t, name: String(t.name || 'Untitled Track'), // Ensure name is always a string audioBuffer: null, // Will need to be reloaded effectChain: t.effectChain || createEffectChain(`${t.name} Effects`), // Restore effect chain or create new selection: t.selection || null, // Initialize selection })); } } catch (error) { console.error('Failed to load tracks from localStorage:', error); // Clear corrupted data localStorage.removeItem(STORAGE_KEY); } return []; }); // Save tracks to localStorage (without audio buffers) useEffect(() => { if (typeof window === 'undefined') return; try { // Only save serializable fields, excluding audioBuffer and any DOM references const trackData = tracks.map((track) => ({ id: track.id, name: String(track.name || 'Untitled Track'), color: track.color, height: track.height, volume: track.volume, pan: track.pan, mute: track.mute, solo: track.solo, recordEnabled: track.recordEnabled, collapsed: track.collapsed, selected: track.selected, effectChain: track.effectChain, // Save effect chain })); localStorage.setItem(STORAGE_KEY, JSON.stringify(trackData)); } catch (error) { console.error('Failed to save tracks to localStorage:', error); } }, [tracks]); const addTrack = useCallback((name?: string) => { const track = createTrack(name); setTracks((prev) => [...prev, track]); return track; }, []); const addTrackFromBuffer = useCallback((buffer: AudioBuffer, name?: string) => { const track = createTrackFromBuffer(buffer, name); setTracks((prev) => [...prev, track]); return track; }, []); const removeTrack = useCallback((trackId: string) => { setTracks((prev) => prev.filter((t) => t.id !== trackId)); }, []); const updateTrack = useCallback((trackId: string, updates: Partial) => { setTracks((prev) => prev.map((track) => track.id === trackId ? { ...track, ...updates } : track ) ); }, []); const clearTracks = useCallback(() => { setTracks([]); }, []); const reorderTracks = useCallback((fromIndex: number, toIndex: number) => { setTracks((prev) => { const newTracks = [...prev]; const [movedTrack] = newTracks.splice(fromIndex, 1); newTracks.splice(toIndex, 0, movedTrack); return newTracks; }); }, []); const setTrackBuffer = useCallback((trackId: string, buffer: AudioBuffer) => { setTracks((prev) => prev.map((track) => track.id === trackId ? { ...track, audioBuffer: buffer } : track ) ); }, []); return { tracks, addTrack, addTrackFromBuffer, removeTrack, updateTrack, clearTracks, reorderTracks, setTrackBuffer, }; }