Files
audio-ui/components/editor/AudioEditor.tsx

679 lines
20 KiB
TypeScript
Raw Normal View History

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
'use client';
import * as React from 'react';
import { Music, Plus, Upload, Trash2 } from 'lucide-react';
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
import { PlaybackControls } from './PlaybackControls';
import { ThemeToggle } from '@/components/layout/ThemeToggle';
import { CommandPalette } from '@/components/ui/CommandPalette';
import { Button } from '@/components/ui/Button';
import type { CommandAction } from '@/components/ui/CommandPalette';
import { useMultiTrack } from '@/lib/hooks/useMultiTrack';
import { useMultiTrackPlayer } from '@/lib/hooks/useMultiTrackPlayer';
import { useEffectChain } from '@/lib/hooks/useEffectChain';
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
import { useToast } from '@/components/ui/Toast';
import { TrackList } from '@/components/tracks/TrackList';
import { ImportTrackDialog } from '@/components/tracks/ImportTrackDialog';
import { formatDuration } from '@/lib/audio/decoder';
import { useHistory } from '@/lib/hooks/useHistory';
import { useRecording } from '@/lib/hooks/useRecording';
import {
createMultiTrackCutCommand,
createMultiTrackCopyCommand,
createMultiTrackDeleteCommand,
createMultiTrackPasteCommand,
createMultiTrackDuplicateCommand,
} from '@/lib/history/commands/multi-track-edit-command';
import { extractBufferSegment } from '@/lib/audio/buffer-utils';
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
export function AudioEditor() {
const [importDialogOpen, setImportDialogOpen] = React.useState(false);
const [selectedTrackId, setSelectedTrackId] = React.useState<string | null>(null);
feat: complete Phase 3 - Advanced waveform visualization and zoom controls Phase 3 Complete Features: ✅ Drag-to-scrub audio functionality ✅ Horizontal zoom (1x-20x) with smooth scaling ✅ Vertical amplitude zoom (0.5x-5x) ✅ Horizontal scrolling for zoomed waveform ✅ Grid lines every second for time reference ✅ Viewport culling for better performance ✅ Zoom controls UI component Components Added: - ZoomControls: Complete zoom control panel with: - Horizontal zoom slider and buttons - Amplitude zoom slider - Zoom in/out buttons - Fit to view button - Real-time zoom level display Waveform Enhancements: - Drag-to-scrub: Click and drag to scrub through audio - Zoom support: View waveform at different zoom levels - Scroll support: Navigate through zoomed waveform - Grid lines: Visual time markers every second - Viewport culling: Only render visible portions - Cursor feedback: Grabbing cursor when dragging AudioEditor Updates: - Integrated zoom and scroll state management - Auto-reset zoom on file clear - Scroll slider appears when zoomed - Smooth zoom transitions Technical Improvements: - Viewport culling: Only render visible waveform portions - Grid rendering: Time-aligned vertical grid lines - Smart scroll clamping: Prevent scrolling beyond bounds - Zoom-aware seeking: Accurate time calculation with zoom - Performance optimized rendering Features Working: ✅ Drag waveform to scrub audio ✅ Zoom in up to 20x for detailed editing ✅ Adjust amplitude for better visualization ✅ Scroll through zoomed waveform ✅ Grid lines show time markers ✅ Smooth cursor interactions Phase 3 Status: 95% complete - Completed: All major features - Optional: Measure/beat markers, OffscreenCanvas, Web Workers Build verified and working ✓ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 15:44:29 +01:00
const [zoom, setZoom] = React.useState(1);
const [masterVolume, setMasterVolume] = React.useState(0.8);
const [clipboard, setClipboard] = React.useState<AudioBuffer | null>(null);
const [recordingTrackId, setRecordingTrackId] = React.useState<string | null>(null);
const { addToast } = useToast();
// Command history for undo/redo
const { execute: executeCommand, undo, redo, state: historyState } = useHistory();
const canUndo = historyState.canUndo;
const canRedo = historyState.canRedo;
// Recording hook
const {
state: recordingState,
startRecording,
stopRecording,
requestPermission,
} = useRecording();
// Multi-track hooks
const {
tracks,
addTrack,
addTrackFromBuffer,
removeTrack,
updateTrack,
clearTracks,
} = useMultiTrack();
feat: complete Phase 7.4 - real-time track effects system Implemented comprehensive real-time effect processing for multi-track audio: Core Features: - Per-track effect chains with drag-and-drop reordering - Effect bypass/enable toggle per effect - Real-time parameter updates (filters, dynamics, time-based, distortion, bitcrusher, pitch, timestretch) - Add/remove effects during playback without interruption - Effect chain persistence via localStorage - Automatic playback stop when tracks are deleted Technical Implementation: - Effect processor with dry/wet routing for bypass functionality - Real-time effect parameter updates using AudioParam setValueAtTime - Structure change detection for add/remove/reorder operations - Stale closure fix using refs for latest track state - ScriptProcessorNode for bitcrusher, pitch shifter, and time stretch - Dual-tap delay line for pitch shifting - Overlap-add synthesis for time stretching UI Components: - EffectBrowser dialog with categorized effects - EffectDevice component with parameter controls - EffectParameters for all 19 real-time effect types - Device rack with horizontal scrolling (Ableton-style) Removed offline-only effects (normalize, fadeIn, fadeOut, reverse) as they don't fit the real-time processing model. Completed all items in Phase 7.4: - [x] Per-track effect chain - [x] Effect rack UI - [x] Effect bypass per track - [x] Real-time effect processing during playback - [x] Add/remove effects during playback - [x] Real-time parameter updates - [x] Effect chain persistence 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 12:08:33 +01:00
// Log tracks to see if they update
React.useEffect(() => {
console.log('[AudioEditor] Tracks updated:', tracks.map(t => ({
name: t.name,
effectCount: t.effectChain.effects.length,
effects: t.effectChain.effects.map(e => e.name)
})));
}, [tracks]);
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
const {
isPlaying,
currentTime,
duration,
trackLevels,
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
play,
pause,
stop,
seek,
togglePlayPause,
} = useMultiTrackPlayer(tracks, masterVolume);
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
// Master effect chain
2025-11-17 20:27:08 +01:00
const {
chain: masterEffectChain,
presets: masterEffectPresets,
toggleEffectEnabled: toggleMasterEffect,
removeEffect: removeMasterEffect,
reorder: reorderMasterEffects,
clearChain: clearMasterChain,
savePreset: saveMasterPreset,
loadPresetToChain: loadMasterPreset,
deletePreset: deleteMasterPreset,
2025-11-17 20:27:08 +01:00
} = useEffectChain();
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
// Multi-track handlers
const handleImportTracks = () => {
setImportDialogOpen(true);
};
const handleImportTrack = (buffer: AudioBuffer, name: string) => {
addTrackFromBuffer(buffer, name);
};
const handleClearTracks = () => {
clearTracks();
setSelectedTrackId(null);
addToast({
title: 'Tracks Cleared',
description: 'All tracks have been removed',
variant: 'info',
duration: 2000,
});
};
const handleRemoveTrack = (trackId: string) => {
removeTrack(trackId);
if (selectedTrackId === trackId) {
setSelectedTrackId(null);
}
};
// Per-track effect chain handlers
const handleToggleTrackEffect = (effectId: string) => {
if (!selectedTrack) return;
const updatedChain = {
...selectedTrack.effectChain,
effects: selectedTrack.effectChain.effects.map((e) =>
e.id === effectId ? { ...e, enabled: !e.enabled } : e
),
};
updateTrack(selectedTrack.id, { effectChain: updatedChain });
};
const handleRemoveTrackEffect = (effectId: string) => {
if (!selectedTrack) return;
const updatedChain = {
...selectedTrack.effectChain,
effects: selectedTrack.effectChain.effects.filter((e) => e.id !== effectId),
};
updateTrack(selectedTrack.id, { effectChain: updatedChain });
};
const handleReorderTrackEffects = (fromIndex: number, toIndex: number) => {
if (!selectedTrack) return;
const effects = [...selectedTrack.effectChain.effects];
const [removed] = effects.splice(fromIndex, 1);
effects.splice(toIndex, 0, removed);
const updatedChain = {
...selectedTrack.effectChain,
effects,
};
updateTrack(selectedTrack.id, { effectChain: updatedChain });
};
const handleClearTrackChain = () => {
if (!selectedTrack) return;
const updatedChain = {
...selectedTrack.effectChain,
effects: [],
};
updateTrack(selectedTrack.id, { effectChain: updatedChain });
};
// Selection handler
const handleSelectionChange = (trackId: string, selection: { start: number; end: number } | null) => {
updateTrack(trackId, { selection });
};
// Recording handlers
const handleToggleRecordEnable = React.useCallback((trackId: string) => {
const track = tracks.find((t) => t.id === trackId);
if (!track) return;
// Toggle record enable
updateTrack(trackId, { recordEnabled: !track.recordEnabled });
}, [tracks, updateTrack]);
const handleStartRecording = React.useCallback(async () => {
// Find first armed track
const armedTrack = tracks.find((t) => t.recordEnabled);
if (!armedTrack) {
addToast({
title: 'No Track Armed',
description: 'Please arm a track for recording first',
variant: 'warning',
duration: 3000,
});
return;
}
// Request permission if needed
const hasPermission = await requestPermission();
if (!hasPermission) {
addToast({
title: 'Microphone Access Denied',
description: 'Please allow microphone access to record',
variant: 'error',
duration: 3000,
});
return;
}
try {
await startRecording();
setRecordingTrackId(armedTrack.id);
addToast({
title: 'Recording Started',
description: `Recording to ${armedTrack.name}`,
variant: 'success',
duration: 2000,
});
} catch (error) {
console.error('Failed to start recording:', error);
addToast({
title: 'Recording Failed',
description: 'Failed to start recording',
variant: 'error',
duration: 3000,
});
}
}, [tracks, startRecording, requestPermission, addToast]);
const handleStopRecording = React.useCallback(async () => {
if (!recordingTrackId) return;
try {
const audioBuffer = await stopRecording();
if (audioBuffer) {
// Update the track with recorded audio
updateTrack(recordingTrackId, { audioBuffer });
addToast({
title: 'Recording Complete',
description: `Recorded ${audioBuffer.duration.toFixed(2)}s of audio`,
variant: 'success',
duration: 3000,
});
}
setRecordingTrackId(null);
} catch (error) {
console.error('Failed to stop recording:', error);
addToast({
title: 'Recording Error',
description: 'Failed to save recording',
variant: 'error',
duration: 3000,
});
setRecordingTrackId(null);
}
}, [recordingTrackId, stopRecording, updateTrack, addToast]);
// Edit handlers
const handleCut = React.useCallback(() => {
const track = tracks.find((t) => t.selection);
if (!track || !track.audioBuffer || !track.selection) return;
// Extract to clipboard
const extracted = extractBufferSegment(
track.audioBuffer,
track.selection.start,
track.selection.end
);
setClipboard(extracted);
// Execute cut command
const command = createMultiTrackCutCommand(
track.id,
track.audioBuffer,
track.selection,
(trackId, buffer, selection) => {
updateTrack(trackId, { audioBuffer: buffer, selection });
}
);
executeCommand(command);
addToast({
title: 'Cut',
description: 'Selection cut to clipboard',
variant: 'success',
duration: 2000,
});
}, [tracks, executeCommand, updateTrack, addToast]);
const handleCopy = React.useCallback(() => {
const track = tracks.find((t) => t.selection);
if (!track || !track.audioBuffer || !track.selection) return;
// Extract to clipboard
const extracted = extractBufferSegment(
track.audioBuffer,
track.selection.start,
track.selection.end
);
setClipboard(extracted);
// Execute copy command (doesn't modify buffer, just for undo history)
const command = createMultiTrackCopyCommand(
track.id,
track.audioBuffer,
track.selection,
(trackId, buffer, selection) => {
updateTrack(trackId, { audioBuffer: buffer, selection });
}
);
executeCommand(command);
addToast({
title: 'Copy',
description: 'Selection copied to clipboard',
variant: 'success',
duration: 2000,
});
}, [tracks, executeCommand, updateTrack, addToast]);
const handlePaste = React.useCallback(() => {
if (!clipboard || !selectedTrackId) return;
const track = tracks.find((t) => t.id === selectedTrackId);
if (!track) return;
// Paste at current time or at end of buffer
const pastePosition = currentTime || track.audioBuffer?.duration || 0;
const command = createMultiTrackPasteCommand(
track.id,
track.audioBuffer,
clipboard,
pastePosition,
(trackId, buffer, selection) => {
updateTrack(trackId, { audioBuffer: buffer, selection });
}
);
executeCommand(command);
addToast({
title: 'Paste',
description: 'Clipboard content pasted',
variant: 'success',
duration: 2000,
});
}, [clipboard, selectedTrackId, tracks, currentTime, executeCommand, updateTrack, addToast]);
const handleDelete = React.useCallback(() => {
const track = tracks.find((t) => t.selection);
if (!track || !track.audioBuffer || !track.selection) return;
const command = createMultiTrackDeleteCommand(
track.id,
track.audioBuffer,
track.selection,
(trackId, buffer, selection) => {
updateTrack(trackId, { audioBuffer: buffer, selection });
}
);
executeCommand(command);
addToast({
title: 'Delete',
description: 'Selection deleted',
variant: 'success',
duration: 2000,
});
}, [tracks, executeCommand, updateTrack, addToast]);
const handleDuplicate = React.useCallback(() => {
const track = tracks.find((t) => t.selection);
if (!track || !track.audioBuffer || !track.selection) return;
const command = createMultiTrackDuplicateCommand(
track.id,
track.audioBuffer,
track.selection,
(trackId, buffer, selection) => {
updateTrack(trackId, { audioBuffer: buffer, selection });
}
);
executeCommand(command);
addToast({
title: 'Duplicate',
description: 'Selection duplicated',
variant: 'success',
duration: 2000,
});
}, [tracks, executeCommand, updateTrack, addToast]);
feat: complete Phase 3 - Advanced waveform visualization and zoom controls Phase 3 Complete Features: ✅ Drag-to-scrub audio functionality ✅ Horizontal zoom (1x-20x) with smooth scaling ✅ Vertical amplitude zoom (0.5x-5x) ✅ Horizontal scrolling for zoomed waveform ✅ Grid lines every second for time reference ✅ Viewport culling for better performance ✅ Zoom controls UI component Components Added: - ZoomControls: Complete zoom control panel with: - Horizontal zoom slider and buttons - Amplitude zoom slider - Zoom in/out buttons - Fit to view button - Real-time zoom level display Waveform Enhancements: - Drag-to-scrub: Click and drag to scrub through audio - Zoom support: View waveform at different zoom levels - Scroll support: Navigate through zoomed waveform - Grid lines: Visual time markers every second - Viewport culling: Only render visible portions - Cursor feedback: Grabbing cursor when dragging AudioEditor Updates: - Integrated zoom and scroll state management - Auto-reset zoom on file clear - Scroll slider appears when zoomed - Smooth zoom transitions Technical Improvements: - Viewport culling: Only render visible waveform portions - Grid rendering: Time-aligned vertical grid lines - Smart scroll clamping: Prevent scrolling beyond bounds - Zoom-aware seeking: Accurate time calculation with zoom - Performance optimized rendering Features Working: ✅ Drag waveform to scrub audio ✅ Zoom in up to 20x for detailed editing ✅ Adjust amplitude for better visualization ✅ Scroll through zoomed waveform ✅ Grid lines show time markers ✅ Smooth cursor interactions Phase 3 Status: 95% complete - Completed: All major features - Optional: Measure/beat markers, OffscreenCanvas, Web Workers Build verified and working ✓ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 15:44:29 +01:00
// Zoom controls
const handleZoomIn = () => {
setZoom((prev) => Math.min(20, prev + 1));
};
const handleZoomOut = () => {
setZoom((prev) => Math.max(1, prev - 1));
};
const handleFitToView = () => {
setZoom(1);
};
// Keyboard shortcuts
React.useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
// Spacebar for play/pause - only if not interacting with form elements
if (e.code === 'Space') {
const target = e.target as HTMLElement;
// Don't trigger if user is typing or interacting with buttons/form elements
if (
target instanceof HTMLInputElement ||
target instanceof HTMLTextAreaElement ||
target instanceof HTMLButtonElement ||
target.getAttribute('role') === 'button'
) {
return;
}
e.preventDefault();
togglePlayPause();
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [togglePlayPause]);
// Find selected track
const selectedTrack = tracks.find((t) => t.id === selectedTrackId);
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
// Command palette actions
const commandActions: CommandAction[] = React.useMemo(() => {
const actions: CommandAction[] = [
// Playback
{
id: 'play',
label: 'Play',
description: 'Start playback',
shortcut: 'Space',
category: 'playback',
action: play,
},
{
id: 'pause',
label: 'Pause',
description: 'Pause playback',
shortcut: 'Space',
category: 'playback',
action: pause,
},
{
id: 'stop',
label: 'Stop',
description: 'Stop playback',
category: 'playback',
action: stop,
},
// View
{
id: 'zoom-in',
label: 'Zoom In',
description: 'Zoom in on waveforms',
category: 'view',
action: handleZoomIn,
},
{
id: 'zoom-out',
label: 'Zoom Out',
description: 'Zoom out on waveforms',
category: 'view',
action: handleZoomOut,
},
{
id: 'fit-to-view',
label: 'Fit to View',
description: 'Reset zoom to fit all tracks',
category: 'view',
action: handleFitToView,
},
// Tracks
{
id: 'add-track',
label: 'Add Empty Track',
description: 'Create a new empty track',
category: 'tracks',
action: () => addTrack(),
},
{
id: 'import-tracks',
label: 'Import Audio Files',
description: 'Import multiple audio files as tracks',
category: 'tracks',
action: handleImportTracks,
},
{
id: 'clear-tracks',
label: 'Clear All Tracks',
description: 'Remove all tracks',
category: 'tracks',
action: handleClearTracks,
},
];
return actions;
}, [play, pause, stop, handleZoomIn, handleZoomOut, handleFitToView, handleImportTracks, handleClearTracks, addTrack]);
// Keyboard shortcuts
React.useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
// Prevent shortcuts if typing in an input
const isTyping = e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement;
// Spacebar: Play/Pause (always, unless typing in an input)
if (e.code === 'Space' && !isTyping) {
e.preventDefault();
togglePlayPause();
return;
}
if (isTyping) return;
// Ctrl/Cmd+Z: Undo
if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) {
e.preventDefault();
if (canUndo) {
undo();
}
return;
}
// Ctrl/Cmd+Shift+Z or Ctrl/Cmd+Y: Redo
if (((e.ctrlKey || e.metaKey) && e.key === 'z' && e.shiftKey) || ((e.ctrlKey || e.metaKey) && e.key === 'y')) {
e.preventDefault();
if (canRedo) {
redo();
}
return;
}
// Ctrl/Cmd+X: Cut
if ((e.ctrlKey || e.metaKey) && e.key === 'x') {
e.preventDefault();
handleCut();
return;
}
// Ctrl/Cmd+C: Copy
if ((e.ctrlKey || e.metaKey) && e.key === 'c') {
e.preventDefault();
handleCopy();
return;
}
// Ctrl/Cmd+V: Paste
if ((e.ctrlKey || e.metaKey) && e.key === 'v') {
e.preventDefault();
handlePaste();
return;
}
// Ctrl/Cmd+D: Duplicate
if ((e.ctrlKey || e.metaKey) && e.key === 'd') {
e.preventDefault();
handleDuplicate();
return;
}
// Delete or Backspace: Delete selection
if (e.key === 'Delete' || e.key === 'Backspace') {
e.preventDefault();
handleDelete();
return;
}
// Escape: Clear selection
if (e.key === 'Escape') {
e.preventDefault();
setSelectedTrackId(null);
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [togglePlayPause, canUndo, canRedo, undo, redo, handleCut, handleCopy, handlePaste, handleDelete, handleDuplicate]);
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
return (
<>
{/* Compact Header */}
<header className="flex items-center justify-between px-4 py-2 border-b border-border bg-card flex-shrink-0 gap-4">
{/* Left: Logo */}
<div className="flex items-center gap-4 flex-shrink-0">
<div className="flex items-center gap-2">
<Music className="h-5 w-5 text-primary" />
<h1 className="text-lg font-bold text-foreground">Audio UI</h1>
</div>
{/* Track Actions */}
<div className="flex items-center gap-2 border-l border-border pl-4">
feat: complete Phase 7.4 - real-time track effects system Implemented comprehensive real-time effect processing for multi-track audio: Core Features: - Per-track effect chains with drag-and-drop reordering - Effect bypass/enable toggle per effect - Real-time parameter updates (filters, dynamics, time-based, distortion, bitcrusher, pitch, timestretch) - Add/remove effects during playback without interruption - Effect chain persistence via localStorage - Automatic playback stop when tracks are deleted Technical Implementation: - Effect processor with dry/wet routing for bypass functionality - Real-time effect parameter updates using AudioParam setValueAtTime - Structure change detection for add/remove/reorder operations - Stale closure fix using refs for latest track state - ScriptProcessorNode for bitcrusher, pitch shifter, and time stretch - Dual-tap delay line for pitch shifting - Overlap-add synthesis for time stretching UI Components: - EffectBrowser dialog with categorized effects - EffectDevice component with parameter controls - EffectParameters for all 19 real-time effect types - Device rack with horizontal scrolling (Ableton-style) Removed offline-only effects (normalize, fadeIn, fadeOut, reverse) as they don't fit the real-time processing model. Completed all items in Phase 7.4: - [x] Per-track effect chain - [x] Effect rack UI - [x] Effect bypass per track - [x] Real-time effect processing during playback - [x] Add/remove effects during playback - [x] Real-time parameter updates - [x] Effect chain persistence 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 12:08:33 +01:00
<Button variant="outline" size="sm" onClick={() => addTrack()}>
<Plus className="h-4 w-4 mr-1.5" />
Add Track
</Button>
<Button variant="outline" size="sm" onClick={handleImportTracks}>
<Upload className="h-4 w-4 mr-1.5" />
Import
</Button>
{tracks.length > 0 && (
<Button variant="outline" size="sm" onClick={handleClearTracks}>
<Trash2 className="h-4 w-4 mr-1.5 text-destructive" />
Clear All
</Button>
)}
</div>
</div>
{/* Right: Command Palette + Theme Toggle */}
<div className="flex items-center gap-2 flex-shrink-0">
<CommandPalette actions={commandActions} />
<ThemeToggle />
</div>
</header>
{/* Main content area */}
<div className="flex flex-1 overflow-hidden">
{/* Main canvas area */}
<main className="flex-1 flex flex-col overflow-hidden bg-background">
{/* Multi-Track View */}
<div className="flex-1 flex flex-col overflow-hidden">
<TrackList
tracks={tracks}
zoom={zoom}
currentTime={currentTime}
duration={duration}
selectedTrackId={selectedTrackId}
onSelectTrack={setSelectedTrackId}
onAddTrack={addTrack}
onImportTrack={handleImportTrack}
onRemoveTrack={handleRemoveTrack}
onUpdateTrack={updateTrack}
onSeek={seek}
onSelectionChange={handleSelectionChange}
onToggleRecordEnable={handleToggleRecordEnable}
recordingTrackId={recordingTrackId}
recordingLevel={recordingState.inputLevel}
trackLevels={trackLevels}
/>
</div>
</main>
</div>
{/* Transport Controls */}
<div className="border-t border-border bg-card p-3 flex justify-center">
<PlaybackControls
isPlaying={isPlaying}
isPaused={!isPlaying}
currentTime={currentTime}
duration={duration}
volume={masterVolume}
onPlay={play}
onPause={pause}
onStop={stop}
onSeek={seek}
onVolumeChange={setMasterVolume}
currentTimeFormatted={formatDuration(currentTime)}
durationFormatted={formatDuration(duration)}
isRecording={recordingState.isRecording}
onStartRecording={handleStartRecording}
onStopRecording={handleStopRecording}
/>
</div>
{/* Import Track Dialog */}
<ImportTrackDialog
open={importDialogOpen}
onClose={() => setImportDialogOpen(false)}
onImportTrack={handleImportTrack}
/>
</>
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
);
}