Files
audio-ui/lib/audio/decoder.ts
Sebastian Krüger ebfb4d3fff feat: implement Phase 2 - Web Audio API engine and waveform visualization
Phase 2 Complete Features:
- Web Audio API context management with browser compatibility
- Audio file upload with drag-and-drop support
- Audio decoding for multiple formats (WAV, MP3, OGG, FLAC, AAC, M4A)
- AudioPlayer class with full playback control
- Waveform visualization using Canvas API
- Real-time waveform rendering with progress indicator
- Playback controls (play, pause, stop, seek)
- Volume control with mute/unmute
- Timeline scrubbing
- Audio file information display

Components:
- AudioEditor: Main editor container
- FileUpload: Drag-and-drop file upload component
- AudioInfo: Display audio file metadata
- Waveform: Canvas-based waveform visualization
- PlaybackControls: Transport controls with volume slider

Audio Engine:
- lib/audio/context.ts: AudioContext management
- lib/audio/decoder.ts: Audio file decoding utilities
- lib/audio/player.ts: AudioPlayer class for playback
- lib/waveform/peaks.ts: Waveform peak generation

Hooks:
- useAudioPlayer: Complete audio player state management

Types:
- types/audio.ts: TypeScript definitions for audio types

Features Working:
✓ Load audio files via drag-and-drop or file picker
✓ Display waveform with real-time progress
✓ Play/pause/stop controls
✓ Seek by clicking on waveform or using timeline slider
✓ Volume control with visual feedback
✓ Audio file metadata display (duration, sample rate, channels)
✓ Toast notifications for user feedback
✓ SSR-safe audio context initialization
✓ Dark/light theme support

Tech Stack:
- Web Audio API for playback
- Canvas API for waveform rendering
- React 19 hooks for state management
- TypeScript for type safety

Build verified and working ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 15:32:00 +01:00

80 lines
1.8 KiB
TypeScript

/**
* Audio file decoding utilities
*/
import { getAudioContext } from './context';
/**
* Decode an audio file to AudioBuffer
*/
export async function decodeAudioFile(file: File): Promise<AudioBuffer> {
const arrayBuffer = await file.arrayBuffer();
const audioContext = getAudioContext();
try {
const audioBuffer = await audioContext.decodeAudioData(arrayBuffer);
return audioBuffer;
} catch (error) {
throw new Error(`Failed to decode audio file: ${error}`);
}
}
/**
* Get audio file metadata without decoding the entire file
*/
export async function getAudioFileMetadata(file: File): Promise<{
name: string;
size: number;
type: string;
}> {
return {
name: file.name,
size: file.size,
type: file.type,
};
}
/**
* Check if a file is a supported audio format
*/
export function isSupportedAudioFormat(file: File): boolean {
const supportedFormats = [
'audio/wav',
'audio/wave',
'audio/x-wav',
'audio/mpeg',
'audio/mp3',
'audio/ogg',
'audio/webm',
'audio/flac',
'audio/aac',
'audio/m4a',
'audio/x-m4a',
];
return supportedFormats.includes(file.type) ||
/\.(wav|mp3|ogg|webm|flac|aac|m4a)$/i.test(file.name);
}
/**
* Format duration in seconds to MM:SS format
*/
export function formatDuration(seconds: number): string {
const mins = Math.floor(seconds / 60);
const secs = Math.floor(seconds % 60);
return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
/**
* Format file size to human-readable format
*/
export function formatFileSize(bytes: number): string {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
}