- Add masterVolume state to AudioEditor (default 0.8)
- Pass masterVolume to useMultiTrackPlayer hook
- Create master gain node in audio graph
- Connect all tracks through master gain before destination
- Update master gain in real-time when volume changes
- Wire up PlaybackControls volume slider and mute button
- Clean up master gain node on unmount
Fixes global volume and mute controls not working in transport controls.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed issue where currentTime wasn't updating during playback:
- Removed 'isPlaying' from updatePlaybackPosition dependencies
- This was causing the RAF loop to stop when state changed
- Now animation frame continues running throughout playback
- Playhead now updates smoothly in waveform and timeline slider
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added type checking to prevent event objects from being used as track names.
When onClick handlers pass events, we now explicitly check for string type.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed [object Object] display in Effect Chain section of SidePanel
by adding String() conversion to selectedTrack.name.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Explicitly whitelist track fields when saving to localStorage to prevent
DOM element references (HTMLButtonElement with React fiber) from being
serialized. This fixes the circular structure JSON error.
Changes:
- Changed from spread operator exclusion to explicit field whitelisting
- Ensured track.name is always converted to string
- Only serialize Track interface fields that should be persisted
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added string conversion and error handling when loading tracks from localStorage to prevent corrupted data from causing React rendering errors. If localStorage data is corrupted, it will be cleared automatically.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed the "Converting circular structure to JSON" error in useMultiTrack by properly destructuring audioBuffer from track objects before serializing to localStorage.
Changed from spreading the entire track object (which could have circular refs) to explicitly excluding audioBuffer using destructuring.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added real-time multi-track audio mixing and playback:
**useMultiTrackPlayer Hook:**
- Real-time multi-track audio mixing with Web Audio API
- Synchronized playback across all tracks
- Dynamic gain control respecting solo/mute states
- Per-track panning with constant power panning
- Seek functionality with automatic resume
- Playback position tracking with requestAnimationFrame
- Automatic duration calculation from longest track
- Clean resource management and cleanup
**Features:**
- ✅ Play/Pause/Stop controls for multi-track
- ✅ Solo/Mute handling (if any track is soloed, only soloed tracks play)
- ✅ Per-track volume control (0-1 range)
- ✅ Per-track pan control (-1 left to +1 right)
- ✅ Real-time parameter updates during playback
- ✅ Seamless seek with playback state preservation
- ✅ Automatic stop when reaching end of longest track
**Audio Graph Architecture:**
For each track: BufferSource → GainNode → StereoPannerNode → Destination
The mixer applies:
- Volume attenuation based on track volume setting
- Solo/Mute logic (getTrackGain utility)
- Constant power panning for smooth stereo positioning
Next: Integrate multi-track UI into AudioEditor
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed TypeScript build error by importing FilterOptions instead of
non-existent FilterParameters from filters module.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Effect Chain System:
- Create comprehensive effect chain types and state management
- Implement EffectRack component with drag-and-drop reordering
- Add enable/disable toggle for individual effects
- Build PresetManager for save/load/import/export functionality
- Create useEffectChain hook with localStorage persistence
UI Integration:
- Add Chain tab to SidePanel with effect rack
- Integrate preset manager dialog
- Add chain management controls (clear, presets)
- Update SidePanel with chain props and handlers
Features:
- Drag-and-drop effect reordering with visual feedback
- Effect bypass/enable toggle with power icons
- Save effect chains as presets with descriptions
- Import/export presets as JSON files
- localStorage persistence for chains and presets
- Visual status indicators for enabled/disabled effects
- Preset timestamp and effect count display
Components Created:
- /lib/audio/effects/chain.ts - Effect chain types and utilities
- /components/effects/EffectRack.tsx - Visual effect chain component
- /components/effects/PresetManager.tsx - Preset management dialog
- /lib/hooks/useEffectChain.ts - Effect chain state hook
Updated PLAN.md:
- Mark Phase 6.6 as complete
- Update current status to Phase 6.6 Complete
- Add effect chain features to working features list
- Update Next Steps to show Phase 6 complete
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 6.5 Advanced Effects:
- Add Pitch Shifter with semitones and cents adjustment
- Add Time Stretch with pitch preservation using overlap-add
- Add Distortion with soft/hard/tube types and tone control
- Add Bitcrusher with bit depth and sample rate reduction
- Add AdvancedParameterDialog with real-time waveform visualization
- Add 4 professional presets per effect type
Improvements:
- Fix undefined parameter errors by adding nullish coalescing operators
- Add global custom scrollbar styling with color-mix transparency
- Add custom-scrollbar utility class for side panel
- Improve theme-aware scrollbar appearance in light/dark modes
- Fix parameter initialization when switching effect types
Integration:
- All advanced effects support undo/redo via EffectCommand
- Effects accessible via command palette and side panel
- Selection-based processing support
- Toast notifications for all effects
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed critical bug where seeking backwards during playback would
jump to position 0 instead of the desired position.
Root cause:
- When stop() was called on a playing source node, it triggered
the onended event callback
- The onended callback would set pauseTime = 0 (thinking playback
naturally ended)
- This interfered with the seek operation which was trying to set
a new position
Solution:
- Clear sourceNode.onended callback BEFORE calling stop()
- This prevents the ended event from firing when we manually stop
- Seeking now works correctly in all directions
The fix ensures clean state transitions during seeking operations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Complete rewrite of seeking logic to support both scrubbing and
click-to-play functionality with proper state management.
Changes:
1. Added autoPlay parameter to seek() methods across the stack
2. Waveform behavior:
- Click and drag → scrubs WITHOUT auto-play during drag
- Mouse up after drag → auto-plays from release position
- This allows smooth scrubbing while dragging
3. Timeline slider behavior:
- onChange → seeks WITHOUT auto-play (smooth dragging)
- onMouseUp/onTouchEnd → auto-plays from final position
4. Transport button state now correctly syncs with playback state
Technical implementation:
- player.seek(time, autoPlay) - autoPlay defaults to false
- If autoPlay=true OR was already playing → continues playback
- If autoPlay=false AND wasn't playing → just seeks (isPaused=true)
- useAudioPlayer.seek() now reads actual player state after seeking
User experience:
✓ Click on waveform → music plays from that position
✓ Drag on waveform → scrubs smoothly, plays on release
✓ Drag timeline slider → scrubs smoothly, plays on release
✓ Transport buttons show correct state (Play/Pause)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed issue where Play/Pause buttons didn't update after clicking on
waveform or timeline slider.
Problem:
- seek() in useAudioPlayer hook only updated currentTime
- But player.seek() now auto-starts playback
- React state (isPlaying, isPaused) wasn't updated
- Transport buttons showed wrong state (Play instead of Pause)
Solution:
- Update isPlaying = true and isPaused = false in seek callback
- Now transport buttons correctly show Pause icon when seeking
- UI state matches actual playback state
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed seek behavior to match user expectations:
- Clicking on waveform now immediately starts playing from that position
- Dragging timeline slider starts playback at the selected position
- No need to click Play button after seeking
Previous behavior:
- seek() only continued playback if music was already playing
- Otherwise it just set isPaused = true
- User had to seek AND THEN click play button
New behavior:
- seek() always starts playback automatically
- Click anywhere → music plays from that position
- Much more intuitive UX matching common audio editors
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed critical bug where playback always started from 0:00 regardless
of seek position or pause state.
Root cause:
- play() method called stop() which reset pauseTime to 0
- Then it checked isPaused (now false) and pauseTime (now 0)
- Result: always played from beginning
Solution:
- Calculate offset BEFORE calling stop()
- Preserve the paused position or start offset
- Then stop and recreate source node with correct offset
Now playback correctly starts from:
- Seeked position (waveform click or timeline slider)
- Paused position (when resuming after pause)
- Specified start offset (when provided)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive undo/redo functionality:
- Command pattern interface and base classes
- HistoryManager with 50-operation stack
- EditCommand for all edit operations (cut, delete, paste, trim)
- Full keyboard shortcuts (Ctrl+Z undo, Ctrl+Y/Ctrl+Shift+Z redo)
- HistoryControls UI component with visual feedback
- Integrated history system with all edit operations
- Toast notifications for undo/redo actions
- History state tracking and display
New files:
- lib/history/command.ts - Command interface and BaseCommand
- lib/history/history-manager.ts - HistoryManager class
- lib/history/commands/edit-command.ts - EditCommand and factory functions
- lib/hooks/useHistory.ts - React hook for history management
- components/editor/HistoryControls.tsx - History UI component
Modified files:
- components/editor/AudioEditor.tsx - Integrated history system
- components/editor/EditControls.tsx - Updated keyboard shortcuts display
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
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>