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>
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>
- 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 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>