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>
This commit is contained in:
2025-11-18 12:08:33 +01:00
parent cbcd38b1ed
commit beb7085c89
11 changed files with 2180 additions and 100 deletions

View File

@@ -33,6 +33,15 @@ export function AudioEditor() {
clearTracks,
} = useMultiTrack();
// 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]);
const {
isPlaying,
currentTime,
@@ -280,7 +289,7 @@ export function AudioEditor() {
{/* Track Actions */}
<div className="flex items-center gap-2 border-l border-border pl-4">
<Button variant="outline" size="sm" onClick={addTrack}>
<Button variant="outline" size="sm" onClick={() => addTrack()}>
<Plus className="h-4 w-4 mr-1.5" />
Add Track
</Button>