'use client'; import { useRef, useEffect, useCallback } from 'react'; import type { WorkerMessage, WorkerResponse } from '@/lib/workers/audio.worker'; /** * Hook to use the audio Web Worker for heavy computations * Automatically manages worker lifecycle and message passing */ export function useAudioWorker() { const workerRef = useRef(null); const callbacksRef = useRef void>>(new Map()); const messageIdRef = useRef(0); // Initialize worker useEffect(() => { // Create worker from the audio worker file workerRef.current = new Worker( new URL('../workers/audio.worker.ts', import.meta.url), { type: 'module' } ); // Handle messages from worker workerRef.current.onmessage = (event: MessageEvent) => { const { id, result, error } = event.data; const callback = callbacksRef.current.get(id); if (callback) { callback(result, error); callbacksRef.current.delete(id); } }; // Cleanup on unmount return () => { if (workerRef.current) { workerRef.current.terminate(); workerRef.current = null; } callbacksRef.current.clear(); }; }, []); // Send message to worker const sendMessage = useCallback( (type: WorkerMessage['type'], payload: any): Promise => { return new Promise((resolve, reject) => { if (!workerRef.current) { reject(new Error('Worker not initialized')); return; } const id = `msg-${++messageIdRef.current}`; const message: WorkerMessage = { id, type, payload }; callbacksRef.current.set(id, (result, error) => { if (error) { reject(new Error(error)); } else { resolve(result); } }); workerRef.current.postMessage(message); }); }, [] ); // API methods const generatePeaks = useCallback( async (channelData: Float32Array, width: number): Promise => { const result = await sendMessage('generatePeaks', { channelData, width, }); return new Float32Array(result); }, [sendMessage] ); const generateMinMaxPeaks = useCallback( async ( channelData: Float32Array, width: number ): Promise<{ min: Float32Array; max: Float32Array }> => { const result = await sendMessage<{ min: Float32Array; max: Float32Array }>( 'generateMinMaxPeaks', { channelData, width } ); return { min: new Float32Array(result.min), max: new Float32Array(result.max), }; }, [sendMessage] ); const normalizePeaks = useCallback( async (peaks: Float32Array, targetMax: number = 1): Promise => { const result = await sendMessage('normalizePeaks', { peaks, targetMax, }); return new Float32Array(result); }, [sendMessage] ); const analyzeAudio = useCallback( async ( channelData: Float32Array ): Promise<{ peak: number; rms: number; crestFactor: number; dynamicRange: number; }> => { return sendMessage('analyzeAudio', { channelData }); }, [sendMessage] ); const findPeak = useCallback( async (channelData: Float32Array): Promise => { return sendMessage('findPeak', { channelData }); }, [sendMessage] ); return { generatePeaks, generateMinMaxPeaks, normalizePeaks, analyzeAudio, findPeak, }; }