- Add TimeScale component with canvas-based rendering
- Use 5 pixels per second base scale (duration * zoom * 5)
- Implement viewport-based rendering for performance
- Add scroll synchronization with waveforms
- Add 240px padding for alignment with track controls and master area
- Apply custom scrollbar styling
- Update all waveform width calculations to match timeline
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented three major medium effort features to enhance the audio editor:
**1. Region Markers System**
- Add marker type definitions supporting point markers and regions
- Create useMarkers hook for marker state management
- Build MarkerTimeline component for visual marker display
- Create MarkerDialog component for adding/editing markers
- Add keyboard shortcuts: M (add marker), Shift+M (next), Shift+Ctrl+M (previous)
- Support marker navigation, editing, and deletion
**2. Web Worker for Computations**
- Create audio worker for offloading heavy computations
- Implement worker functions: generatePeaks, generateMinMaxPeaks, normalizePeaks, analyzeAudio, findPeak
- Build useAudioWorker hook for easy worker integration
- Integrate worker into Waveform component with peak caching
- Significantly improve UI responsiveness during waveform generation
**3. Bezier Curve Automation**
- Enhance interpolateAutomationValue to support Bezier curves
- Implement cubic Bezier interpolation with control handles
- Add createSmoothHandles for auto-smooth curve generation
- Add generateBezierCurvePoints for smooth curve rendering
- Support bezier alongside existing linear and step curves
All features are type-safe and integrate seamlessly with the existing codebase.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented variable playback speed functionality:
- Added playbackRate state and ref to useMultiTrackPlayer (0.25x - 2x range)
- Applied playback rate to AudioBufferSourceNode.playbackRate
- Updated timing calculations to account for playback rate
- Real-time playback speed adjustment for active playback
- Dropdown UI control in PlaybackControls with preset speeds
- Integrated changePlaybackRate function through AudioEditor
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added complete loop functionality with UI controls:
- Loop state management in useMultiTrackPlayer (loopEnabled, loopStart, loopEnd)
- Automatic restart from loop start when reaching loop end during playback
- Loop toggle button in PlaybackControls with Repeat icon
- Loop points UI showing when loop is enabled (similar to punch in/out)
- Manual loop point adjustment with number inputs
- Quick set buttons to set loop points to current time
- Wired loop functionality through AudioEditor component
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed the defaultTrackHeight setting from UI preferences as it doesn't need
to be user-configurable. All tracks now use DEFAULT_TRACK_HEIGHT constant (400px).
Changes:
- Removed defaultTrackHeight from UISettings interface
- Removed track height slider from GlobalSettingsDialog
- Updated AudioEditor to use DEFAULT_TRACK_HEIGHT constant directly
- Simplified dependency arrays in addTrack callbacks
This simplifies the settings interface while maintaining the same visual behavior.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Removed the AudioWorklet compatibility check warning since it's an optional
feature that doesn't affect core functionality. The app works fine without
AudioWorklet support, using standard Web Audio API nodes instead.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive mobile support for Phase 15 (Polish & Optimization):
**Mobile Layout Enhancements:**
- Track controls now collapsible on mobile with two states:
- Collapsed: minimal controls with expand chevron, R/M/S buttons, horizontal level meter
- Expanded: full height fader, pan control, all buttons
- Track collapse buttons added to mobile view (left chevron for track collapse, right chevron for control collapse)
- Master controls collapse button hidden on desktop (lg:hidden)
- Automation and effects bars now available on mobile layout
- Both bars collapsible with eye/eye-off icons, horizontally scrollable when zoomed
- Mobile vertical stacking: controls → waveform → automation → effects per track
**Bug Fixes:**
- Fixed track controls and waveform container height matching on desktop
- Fixed Modal component prop: isOpen → open in all dialog components
- Fixed TypeScript null check for audioBuffer.duration
- Fixed keyboard shortcut category: 'help' → 'view'
**Technical Improvements:**
- Consistent height calculation using trackHeight variable
- Proper responsive breakpoints with Tailwind (sm:640px, lg:1024px)
- Progressive disclosure pattern for mobile controls
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Modified createTrack and createTrackFromBuffer to accept height parameter
- Updated useMultiTrack to pass height when creating tracks
- Applied settings.ui.defaultTrackHeight when adding new tracks
- Applied settings.editor.defaultZoom for initial zoom state
- Removed duplicate useSettings hook declaration in AudioEditor
- New tracks now use configured default height from settings
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Removed waveformColor from UISettings interface
- Removed waveform color picker from Interface settings tab
- Preserves dynamic per-track waveform coloring system
- Cleaner settings UI with one less option
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed from single JSON file to ZIP archive format to avoid string
length limits when serializing large audio buffers.
New ZIP structure:
- project.json (metadata, track info, effects, automation)
- track_0.wav, track_1.wav, etc. (audio files in WAV format)
Benefits:
- No more RangeError: Invalid string length
- Smaller file sizes (WAV compression vs base64 JSON)
- Human-readable audio files in standard format
- Can extract and inspect individual tracks
- Easier to edit/debug project metadata
Added jszip dependency for ZIP file handling.
Changed file picker to accept .zip files.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added full export/import functionality for projects:
Export Features:
- Export button for each project in Projects dialog
- Downloads project as JSON file with all data
- Includes tracks, audio buffers, effects, automation, settings
- Filename format: {project_name}_{timestamp}.json
Import Features:
- Import button in Projects dialog header
- File picker for .json files
- Automatically generates new project ID to avoid conflicts
- Appends "(Imported)" to project name
- Preserves all project data
This enables:
- Backup of projects outside the browser
- Sharing projects with collaborators
- Migration between computers/browsers
- Version snapshots at different stages
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Deep clone automation data using JSON.parse(JSON.stringify()) to remove
any functions before saving to IndexedDB. This prevents DataCloneError
when trying to store non-serializable data.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Effects contain non-serializable data like functions and audio nodes that
cannot be stored in IndexedDB. Added proper serialization.
Changes:
- Added serializeEffects function to strip non-serializable data
- Uses JSON parse/stringify to deep clone parameters
- Preserves effect type, name, enabled state, and parameters
- Fixes DataCloneError when saving projects with effects
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
**useMultiTrack.ts:**
- Removed localStorage persistence (tracks, effects, automation)
- Now relies entirely on IndexedDB via project management system
- Simpler state management without dual persistence
**AudioEditor.tsx:**
- Store last project ID in localStorage when saving
- Auto-load last project on page mount
- Only runs once per session with hasAutoLoaded flag
- Falls back to empty state if project can't be loaded
**Benefits:**
- No more conflicts between localStorage and IndexedDB
- Effects and automation properly persisted
- Seamless experience - reload page and your project is ready
- Single source of truth (IndexedDB)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed project loading to restore all track properties and added inline
project name editing in header.
**AudioEditor.tsx:**
- Added `loadTracks` from useMultiTrack hook
- Fixed `handleLoadProject` to use `loadTracks()` instead of recreating tracks
- Now properly restores all track properties (effects, automation, volume, pan, etc.)
- Shows track count in success toast message
- Added editable project name input in header
- Positioned between logo and track actions
- Auto-sizes based on text length
- Saves on blur (triggers auto-save)
- Smooth hover/focus transitions
- Muted color that brightens on interaction
**useMultiTrack.ts:**
- Added `loadTracks()` method to replace all tracks at once
- Enables proper project loading with full state restoration
- Maintains all track properties during load
**Fixes:**
- Projects now load correctly with all tracks and their audio buffers
- Track properties (effects, automation, volume, pan, etc.) fully restored
- Project name can be edited inline in header
- Auto-save triggers when project name changes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Integrated complete project management system with auto-save:
**AudioEditor.tsx - Full Integration:**
- Added "Projects" button in header toolbar (FolderOpen icon)
- Project state management (currentProjectId, currentProjectName, projects list)
- Comprehensive project handlers:
- `handleOpenProjectsDialog` - Opens dialog and loads project list
- `handleSaveProject` - Saves current project to IndexedDB
- `handleNewProject` - Creates new project with confirmation
- `handleLoadProject` - Loads project and restores all tracks/settings
- `handleDeleteProject` - Deletes project with cleanup
- `handleDuplicateProject` - Creates project copy
- Auto-save effect: Saves project every 30 seconds when tracks exist
- ProjectsDialog component integrated with all handlers
- Toast notifications for all operations
**lib/storage/projects.ts:**
- Re-exported ProjectMetadata type for easier importing
- Fixed type exports
**Key Features:**
- **Auto-save**: Automatically saves every 30 seconds
- **Project persistence**: Full track state, audio buffers, effects, automation
- **Smart loading**: Restores zoom, track order, and all track properties
- **Safety confirmations**: Warns before creating new project with unsaved changes
- **User feedback**: Toast messages for all operations (save, load, delete, duplicate)
- **Seamless workflow**: Projects → Import → Export in logical toolbar order
**User Flow:**
1. Click "Projects" to open project manager
2. Create new project or load existing
3. Work on tracks (auto-saves every 30s)
4. Switch between projects anytime
5. Duplicate projects for experimentation
6. Delete old projects to clean up
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added complete project save/load system using IndexedDB:
**New Files:**
- `lib/storage/db.ts` - IndexedDB database schema and operations
- ProjectMetadata interface for project metadata
- SerializedAudioBuffer and SerializedTrack for storage
- Database initialization with projects object store
- Audio buffer serialization/deserialization functions
- CRUD operations for projects
- `lib/storage/projects.ts` - High-level project management service
- Save/load project state with tracks and settings
- List all projects sorted by last updated
- Delete and duplicate project operations
- Track serialization with proper type conversions
- Audio buffer and effect chain handling
- `components/dialogs/ProjectsDialog.tsx` - Project list UI
- Grid view of all projects with metadata
- Project actions: Open, Duplicate, Delete
- Create new project button
- Empty state with call-to-action
- Confirmation dialog for deletions
**Key Features:**
- IndexedDB stores complete project state (tracks, audio buffers, settings)
- Efficient serialization of AudioBuffer channel data
- Preserves all track properties (effects, automation, volume, pan)
- Sample rate and duration tracking
- Created/updated timestamps
- Type-safe with full TypeScript support
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Switched from low-level Lame API to Mp3Encoder class which:
- Properly initializes all required modules (Lame, BitStream, etc.)
- Handles module dependencies via setModules() calls
- Provides a simpler encodeBuffer/flush API
- Resolves "init_bit_stream_w is not defined" error
Updated TypeScript declarations to export Mp3Encoder and WavHeader
from lamejs/src/js/index.js
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed MP3 export by importing lamejs modules directly from source:
- Import MPEGMode, Lame, and BitStream from individual source files
- Use Lame API directly instead of Mp3Encoder wrapper
- Updated TypeScript declarations for each module
- Resolves "MPEGMode is not defined" error
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Switched from npm package to GitHub repo (github:zhuker/lamejs) which
includes the proper browser build. Reverted to simple dynamic import.
Fixes MP3 export MPEGMode reference error.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed MP3 export to dynamically load the pre-built lame.min.js
bundle from node_modules instead of trying to import the CommonJS
module. The browser bundle properly bundles all dependencies including
MPEGMode and exposes a global lamejs object.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed MP3 export error by handling both default and named exports
from lamejs module to support CommonJS/ESM interop.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented Phase 11.1 export format support:
- Added MP3 export using lamejs library
- Added FLAC export using fflate DEFLATE compression
- Updated ExportDialog with format selector and format-specific options
- MP3: bitrate selector (128/192/256/320 kbps)
- FLAC: compression quality slider (0-9)
- WAV: bit depth selector (16/24/32-bit)
- Updated AudioEditor to route export based on selected format
- Created TypeScript declarations for lamejs
- Fixed AudioStatistics to use audioBuffer instead of buffer property
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added real-time audio analysis visualizations to master column:
- FrequencyAnalyzer component with FFT bar display
- Canvas-based rendering with requestAnimationFrame
- Color gradient from cyan to green based on frequency
- Frequency axis labels (20Hz, 1kHz, 20kHz)
- Spectrogram component with time-frequency waterfall display
- Scrolling visualization with ImageData pixel manipulation
- Color mapping: black → blue → cyan → green → yellow → red
- Vertical frequency axis with labels
- Master column redesign
- Fixed width layout (280px)
- Toggle buttons to switch between FFT and Spectrum views
- Integrated above master controls with 300px minimum height
- Exposed masterAnalyser from useMultiTrackPlayer hook
- Analyser node now accessible to visualization components
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Track levels were being converted to dB scale twice:
1. First in useMultiTrackPlayer via linearToDbScale()
2. Again in TrackFader via linearToDb()
This caused tracks to show incorrect meter levels compared to master.
Now both track and master levels store raw linear values (0-1) and
let the fader components handle the single dB conversion for display.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Previously, master meters received raw linear values (0-1) while track
meters received dB-normalized values, causing inconsistent metering display.
Now both master peak and RMS levels are converted using linearToDbScale()
for accurate comparison between track and master levels.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive master output level monitoring:
- Created MasterMeter component with dual vertical bars (peak + RMS)
- Implemented real-time level monitoring in useMultiTrackPlayer hook
- Added master analyser node connected to audio output
- Displays color-coded levels (green/yellow/red) based on dB thresholds
- Shows numerical dB readouts for both peak and RMS
- Includes clickable clip indicator with reset functionality
- Integrated into PlaybackControls next to master volume
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implemented comprehensive automation recording system for volume, pan, and effect parameters:
- Added automation recording modes:
- Write: Records continuously during playback when values change
- Touch: Records only while control is being touched/moved
- Latch: Records from first touch until playback stops
- Implemented value change detection (0.001 threshold) to prevent infinite loops
- Fixed React setState-in-render errors by:
- Using queueMicrotask() to defer state updates
- Moving lane creation logic to useEffect
- Properly memoizing touch handlers with useMemo
- Added proper value ranges for effect parameters:
- Frequency: 20-20000 Hz
- Q: 0.1-20
- Gain: -40-40 dB
- Enhanced automation lane auto-creation with parameter-specific ranges
- Added touch callbacks to all parameter controls (volume, pan, effects)
- Implemented throttling (100ms) to avoid excessive automation points
Technical improvements:
- Used tracksRef and onRecordAutomationRef to ensure latest values in animation loops
- Added proper cleanup on playback stop
- Optimized recording to only trigger when values actually change
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Phase 9.3 - Automation Playback:
- Added real-time automation evaluation during playback
- Automation values are applied continuously via requestAnimationFrame
- Volume automation: Interpolates between points and applies to gain nodes
- Pan automation: Converts 0-1 values to -1 to 1 for StereoPannerNode
Implementation details:
- New applyAutomation() function runs in RAF loop alongside level monitoring
- Evaluates automation at current playback time using evaluateAutomationLinear
- Applies values using setValueAtTime for smooth Web Audio API parameter changes
- Automation loop lifecycle matches playback (start/pause/stop/cleanup)
- Respects automation mode (only applies when mode !== 'read')
Technical improvements:
- Added automationFrameRef for RAF management
- Proper cleanup in pause(), unmount, and playback end scenarios
- Integrated with existing effect chain restart mechanism
- Volume automation multiplied with track gain (mute/solo state)
Next steps:
- Effect parameter automation (TODO in code)
- Automation recording (write mode implementation)
- Touch and latch modes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added showEffects property to Track type
- Added "E" button with Sparkles icon to toggle per-track effects
- Effects panel now appears below each track when toggled
- Removed global EffectsPanel from AudioEditor
- Updated useMultiTrack to persist showEffects state
- Streamlined workflow: both automation and effects are now per-track
This aligns the UX with professional DAWs like Ableton Live, where
effects and automation are track-scoped rather than global.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive automation lane UI with Ableton-style design:
**Automation Components:**
- AutomationLane: Canvas-based rendering with grid lines, curves, and points
- AutomationHeader: Parameter name, mode selector, value display
- AutomationPoint: Interactive draggable points with hover states
**Automation Utilities:**
- createAutomationLane/Point: Factory functions
- evaluateAutomationLinear: Interpolation between points
- formatAutomationValue: Display formatting with unit support
- addAutomationPoint/updateAutomationPoint/removeAutomationPoint
**Track Integration:**
- Added "A" toggle button in track control panel
- Automation lanes render below waveform
- Default lanes for Volume (orange) and Pan (green)
- Support for add/edit/delete automation points
- Click to add, drag to move, double-click to delete
**Visual Design:**
- Dark background with horizontal grid lines
- Colored curves with semi-transparent fill (20% opacity)
- 4-6px automation points, 8px on hover
- Mode indicators (Read/Write/Touch/Latch) with colors
- Value labels and current value display
Ready for playback integration in next step.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Major improvements:
- Fixed multi-file import (FileList to Array conversion)
- Auto-select first track when adding to empty project
- Global effects panel folding state (independent of track selection)
- Effects panel collapsed/disabled when no track selected
- Effect device expansion state persisted per-device
- Effect browser with searchable descriptions
Visual refinements:
- Removed center dot from pan knob for cleaner look
- Simplified fader: removed volume fill overlay, dynamic level meter visible through semi-transparent handle
- Level meter capped at fader position (realistic mixer behavior)
- Solid background instead of gradient for fader track
- Subtle volume overlay up to fader handle
- Fixed track control width flickering (consistent 4px border)
- Effect devices: removed shadows/rounded corners for flatter DAW-style look, added consistent border-radius
- Added border between track control and waveform area
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Converted level meters from linear to logarithmic (dB) scale to
match professional audio software behavior and human hearing.
The Problem:
- Linear scale (0-100%) doesn't match perceived loudness
- Doesn't match professional DAW meter behavior
- Half-volume audio appears at 50% but sounds much quieter
- No industry-standard dB reference
The Solution:
- Convert linear amplitude to dB: 20 * log10(linear)
- Normalize -60dB to 0dB range to 0-100% display
- Matches professional audio metering standards
dB Scale Mapping:
0 dB (linear 1.0) = 100% (full scale, clipping)
-6 dB (linear ~0.5) = 90% (loud)
-12 dB (linear ~0.25) = 80% (normal)
-20 dB (linear ~0.1) = 67% (moderate)
-40 dB (linear ~0.01) = 33% (quiet)
-60 dB (linear ~0.001) = 0% (silence threshold)
Implementation:
- Added linearToDbScale() function to both hooks
- useMultiTrackPlayer: playback level monitoring
- useRecording: input level monitoring
- Formula: (dB - minDb) / (maxDb - minDb)
- Range: -60dB (min) to 0dB (max)
Benefits:
✅ Professional audio metering standards
✅ Matches human perception of loudness
✅ Consistent with DAWs (Pro Tools, Logic, Ableton)
✅ Better visual feedback for mixing/mastering
✅ More responsive in useful range (-20dB to 0dB)
Now properly mastered tracks will show levels in the
90-100% range, matching what you'd see in professional software.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Switched from Uint8Array to Float32Array for level monitoring
to get accurate, full-precision audio measurements.
The Problem:
- getByteTimeDomainData() uses Uint8Array (0-255)
- Byte conversion: (value - 128) / 128 has asymmetric range
- Positive peaks: (255-128)/128 = 0.992 (not full 1.0)
- Precision loss from byte quantization
- Mastered tracks with peaks at 0dBFS only showed ~50%
The Solution:
- Switched to getFloatTimeDomainData() with Float32Array
- Returns actual sample values directly in -1.0 to +1.0 range
- No conversion needed, no precision loss
- Accurate representation of audio peaks
Changes Applied:
- useMultiTrackPlayer: Float32Array with analyser.fftSize samples
- useRecording: Float32Array with analyser.fftSize samples
- Peak detection: Math.abs() on float values directly
Benefits:
✅ Full 0-100% range for properly mastered audio
✅ Higher precision (32-bit float vs 8-bit byte)
✅ Symmetric range (-1.0 to +1.0, not -1.0 to ~0.992)
✅ Accurate metering for professional audio files
Now mastered tracks with peaks at 0dBFS will correctly show
~100% on the meters instead of being capped at 50%.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Repositioned analyser nodes in audio graph to measure raw audio
levels before volume/gain adjustments.
The Problem:
- Analyser was after gain node in signal chain
- Track volume defaults to 0.8 (80%)
- Audio was scaled down before measurement
- Meters only showed ~50% of actual audio peaks
The Solution:
- Moved analyser to immediately after source
- Now measures raw audio before any processing
- Shows true audio content independent of fader position
Audio Graph Changes:
Before: source -> gain -> pan -> effects -> analyser -> master
After: source -> analyser -> gain -> pan -> effects -> master
Benefits:
✅ Meters show full 0-100% range based on audio content
✅ Meter reading independent of volume fader position
✅ Accurate representation of track audio levels
✅ Increased smoothingTimeConstant to 0.8 for smoother motion
This is how professional DAWs work - level meters show the
audio content, not the output level after the fader.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Changed level calculation from RMS to peak detection to show
more realistic and responsive meter values.
The Problem:
- RMS calculation produced values typically in 0-30% range
- Audio signals have low average RMS (0.1-0.3 for music)
- Meters appeared broken, never reaching higher levels
The Solution:
- Switched to peak detection (max absolute value)
- Peaks now properly show 0-100% range
- More responsive to transients and dynamics
- Matches typical DAW meter behavior
Algorithm Change:
Before (RMS):
rms = sqrt(sum(normalized²) / length)
After (Peak):
peak = max(abs(normalized))
Applied to Both:
- Recording input level monitoring (useRecording)
- Playback output level monitoring (useMultiTrackPlayer)
Benefits:
✅ Full 0-100% range utilization
✅ More responsive visual feedback
✅ Accurate representation of audio peaks
✅ Consistent with professional audio software
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed playback level meters staying at 0% by resolving React closure
issue in the monitoring loop - same pattern as the recording fix.
The Problem:
- monitorPlaybackLevels callback checked stale `isPlaying` state
- Animation loop would run once and never continue
- Dependency on isPlaying caused callback recreation on every state change
The Solution:
- Added isMonitoringLevelsRef to track state independent of React
- Removed isPlaying dependency from callback (now has empty deps [])
- Set ref to true when starting playback
- Set ref to false when pausing, stopping, or ending playback
- Animation loop checks ref instead of stale closure state
Monitoring State Management:
- Start: play() sets isMonitoringLevelsRef.current = true
- Stop: pause(), stop(), onended, and cleanup set it to false
- Loop: continues while ref is true, stops when false
This ensures the requestAnimationFrame loop runs continuously
during playback and calculates real-time RMS levels for all tracks.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive playback level monitoring system that shows
real-time audio levels during playback for each track.
useMultiTrackPlayer Hook:
- Added AnalyserNode for each track in audio graph
- Implemented RMS-based level calculation with requestAnimationFrame
- Added trackLevels state (Record<string, number>) tracking levels by track ID
- Insert analysers after effects chain, before master gain
- Monitor levels continuously during playback
- Clean up level monitoring on pause/stop
Audio Graph Chain:
source -> gain -> pan -> effects -> analyser -> master gain -> destination
AudioEditor Integration:
- Extract trackLevels from useMultiTrackPlayer hook
- Pass trackLevels down to TrackList component
TrackList & Track Components:
- Accept and forward trackLevels prop
- Pass playbackLevel to individual Track components
- Track component displays appropriate level:
* Recording level (with "Input" label) when armed/recording
* Playback level (with "Level" label) during normal playback
Visual Feedback:
- Color-coded meters: green -> yellow (70%) -> red (90%)
- Real-time percentage display
- Seamless switching between input and output modes
This completes Phase 8 (Recording) with full bidirectional level monitoring!
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed the input level meter staying at 0% during recording by:
Closure Issue Resolution:
- Added isMonitoringRef to track monitoring state independent of React state
- Removed state dependencies from monitorInputLevel callback
- Animation loop now checks ref instead of stale closure state
Changes:
- Set isMonitoringRef.current = true when starting recording
- Set isMonitoringRef.current = false when stopping/pausing recording
- Animation frame continues while ref is true, stops when false
- Proper cleanup in stopRecording, pauseRecording, and unmount effect
This ensures the requestAnimationFrame loop continues properly and
updates the RMS level calculation in real-time during recording.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added migration logic to useMultiTrack hook:
- When loading tracks from localStorage, check if height < DEFAULT_TRACK_HEIGHT
- Automatically upgrade old heights (120px, 150px) to new default (180px)
- Preserves custom heights that are already >= 180px
This fixes the inline style issue where existing tracks had
style="height: 120px" that was cutting off the level meter.
After this update, refreshing the page will automatically upgrade
all existing tracks to the new height without losing any data.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added recording capabilities to the multi-track editor:
- useRecording hook with MediaRecorder API integration
- Audio input device enumeration and selection
- Microphone permission handling
- Input level monitoring with RMS calculation
- InputLevelMeter component with visual feedback
- Record-enable button per track with pulsing indicator
- Real-time input level display when recording
Recording infrastructure is complete. Next: integrate into AudioEditor
for global recording control and buffer storage.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Added comprehensive selection and editing capabilities to multi-track editor:
- Visual selection overlay with Shift+drag interaction on waveforms
- Multi-track edit commands (cut, copy, paste, delete, duplicate)
- Full keyboard shortcut support (Ctrl+X/C/V/D, Delete, Ctrl+Z/Y)
- Complete undo/redo integration via command pattern
- Per-track selection state with localStorage persistence
- Audio buffer manipulation utilities (extract, insert, delete, duplicate segments)
- Toast notifications for all edit operations
- Red playhead to distinguish from blue selection overlay
All edit operations are fully undoable and integrated with the existing
history manager system.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Architecture:
- Each track now has its own effect chain stored in track.effectChain
- Separate master effect chain for the final mix output
- SidePanel has 3 tabs: Tracks, Track FX, Master FX
Changes:
- types/track.ts: Add effectChain field to Track interface
- lib/audio/track-utils.ts: Initialize effect chain when creating tracks
- lib/hooks/useMultiTrack.ts: Exclude effectChain from localStorage, recreate on load
- components/editor/AudioEditor.tsx:
- Add master effect chain state using useEffectChain hook
- Add handlers for per-track effect chain manipulation
- Pass both track and master effect chains to SidePanel
- components/layout/SidePanel.tsx:
- Update to 3-tab interface (Tracks | Track FX | Master FX)
- Track FX tab shows effects for currently selected track
- Master FX tab shows master bus effects with preset management
- Different icons for track vs master effects tabs
Note: Effect processing in Web Audio API not yet implemented.
This commit sets up the data structures and UI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- 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>