/** * Audio Interop - Exposes AudioPlayer to Blazor via window.DeepDrftAudio */ import { AudioPlayer, AudioResult, StreamingResult, AudioState } from './AudioPlayer.js'; // Player instances by ID const audioPlayers = new Map(); // .NET interop type interface DotNetObjectReference { invokeMethodAsync(methodName: string, ...args: unknown[]): Promise; } // Global API exposed to Blazor const DeepDrftAudio = { createPlayer: async (playerId: string): Promise => { try { const player = new AudioPlayer(); const result = await player.initialize(); if (result.success) { audioPlayers.set(playerId, player); } return result; } catch (error) { return { success: false, error: (error as Error).message }; } }, initializeStreaming: (playerId: string, totalStreamLength: number): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.initializeStreaming(totalStreamLength); }, processStreamingChunk: async (playerId: string, chunk: Uint8Array): Promise => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.processStreamingChunk(chunk); }, startStreamingPlayback: async (playerId: string): Promise => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.startStreamingPlayback(); }, markStreamComplete: async (playerId: string): Promise => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.markStreamComplete(); }, ensureAudioContextReady: async (playerId: string): Promise => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.ensureAudioContextReady(); }, play: async (playerId: string): Promise => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.play(); }, pause: (playerId: string): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.pause(); }, stop: (playerId: string): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.stop(); }, unload: (playerId: string): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.unload(); }, seek: (playerId: string, position: number): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.seek(position); }, // New methods for seek-beyond-buffer support getBufferedDuration: (playerId: string): number => { const player = audioPlayers.get(playerId); return player?.getBufferedDuration() ?? 0; }, calculateByteOffset: (playerId: string, positionSeconds: number): number => { const player = audioPlayers.get(playerId); return player?.calculateByteOffset(positionSeconds) ?? 0; }, reinitializeFromOffset: (playerId: string, totalStreamLength: number, seekPosition: number): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.reinitializeFromOffset(totalStreamLength, seekPosition); }, setVolume: (playerId: string, volume: number): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.setVolume(volume); }, getCurrentTime: (playerId: string): number => { const player = audioPlayers.get(playerId); return player?.getCurrentTime() ?? 0; }, getState: (playerId: string): AudioState | null => { const player = audioPlayers.get(playerId); return player?.getState() ?? null; }, setOnProgressCallback: ( playerId: string, dotNetRef: DotNetObjectReference, methodName: string ): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; player.setOnProgressCallback((currentTime: number) => { dotNetRef.invokeMethodAsync(methodName, currentTime); }); return { success: true }; }, setOnEndCallback: ( playerId: string, dotNetRef: DotNetObjectReference, methodName: string ): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; player.setOnEndCallback(() => { dotNetRef.invokeMethodAsync(methodName); }); return { success: true }; }, // Spectrum analyzer methods getSpectrumData: (playerId: string): number[] | null => { const player = audioPlayers.get(playerId); return player?.getSpectrumData() ?? null; }, setSpectrumHighPass: (playerId: string, freq: number): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.setSpectrumHighPass(freq); }, setSpectrumLowPass: (playerId: string, freq: number): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.setSpectrumLowPass(freq); }, setSpectrumSlope: (playerId: string, dbPerDecade: number): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; return player.setSpectrumSlope(dbPerDecade); }, startSpectrumAnimation: ( playerId: string, callbackId: string, dotNetRef: DotNetObjectReference, methodName: string ): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; player.startSpectrumAnimation(callbackId, (data: number[]) => { dotNetRef.invokeMethodAsync(methodName, data); }); return { success: true }; }, stopSpectrumAnimation: (playerId: string, callbackId: string): AudioResult => { const player = audioPlayers.get(playerId); if (!player) return { success: false, error: 'Player not found' }; player.stopSpectrumAnimation(callbackId); return { success: true }; }, disposePlayer: (playerId: string): AudioResult => { const player = audioPlayers.get(playerId); if (player) { player.dispose(); audioPlayers.delete(playerId); return { success: true }; } return { success: false, error: 'Player not found' }; }, // Legacy compatibility - these may not be needed but kept for safety initializeBufferedPlayer: (_playerId: string): AudioResult => { return { success: true }; // No-op for streaming mode }, appendAudioBlock: (_playerId: string, _audioBlock: Uint8Array): AudioResult => { return { success: true }; // No-op - use processStreamingChunk instead }, finalizeAudioBuffer: async (_playerId: string): Promise => { return { success: true }; // No-op for streaming mode } }; // Expose to window declare global { interface Window { DeepDrftAudio: typeof DeepDrftAudio; } } window.DeepDrftAudio = DeepDrftAudio; export { DeepDrftAudio };