diff options
| author | skal <pascal.massimino@gmail.com> | 2026-02-15 14:45:15 +0100 |
|---|---|---|
| committer | skal <pascal.massimino@gmail.com> | 2026-02-15 14:45:15 +0100 |
| commit | b464969cd1f5dd4dceb996ad8410e2695ab477c4 (patch) | |
| tree | e763d7e782241ec26b8a32eb4ece350421d08ca8 /tools/timeline_editor | |
| parent | 5b0d020241c8268c45ec0e197d60bfcfc2b7966b (diff) | |
docs: document audio WAV drift bug investigation
Root cause: audio_render_ahead() over-renders by 366ms per 10s, causing
progressive timing drift in WAV files. Events appear early in viewer.
Findings:
- Renders 11,733 extra frames over 40s (331,533 vs 319,800 expected)
- Ring buffer accumulates excess audio (~19 frames/iteration)
- WAV dump reads exact 533 frames but renders ~552 frames per call
- Results in -180ms drift at 60 beats visible in timeline viewer
Debug changes:
- Added render tracking to audio.cc to measure actual vs expected
- Added drift printf to tracker.cc for kick/snare timing analysis
- Added WAV sample rate detection to timeline viewer
See doc/AUDIO_WAV_DRIFT_BUG.md for complete analysis and proposed fixes.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Diffstat (limited to 'tools/timeline_editor')
| -rw-r--r-- | tools/timeline_editor/timeline-playback.js | 21 |
1 files changed, 20 insertions, 1 deletions
diff --git a/tools/timeline_editor/timeline-playback.js b/tools/timeline_editor/timeline-playback.js index 1bcdcd0..bfeb75a 100644 --- a/tools/timeline_editor/timeline-playback.js +++ b/tools/timeline_editor/timeline-playback.js @@ -67,17 +67,36 @@ export class PlaybackController { async loadAudioFile(file) { try { const arrayBuffer = await file.arrayBuffer(); + + // Detect original WAV sample rate before decoding + const dataView = new DataView(arrayBuffer); + let originalSampleRate = 32000; // Default assumption + + // Parse WAV header to get original sample rate + // "RIFF" at 0, "WAVE" at 8, "fmt " at 12, sample rate at 24 + if (dataView.getUint32(0, false) === 0x52494646 && // "RIFF" + dataView.getUint32(8, false) === 0x57415645) { // "WAVE" + originalSampleRate = dataView.getUint32(24, true); // Little-endian + console.log(`Detected WAV sample rate: ${originalSampleRate}Hz`); + } + if (!this.state.audioContext) { this.state.audioContext = new (window.AudioContext || window.webkitAudioContext)(); } + this.state.audioBuffer = await this.state.audioContext.decodeAudioData(arrayBuffer); this.state.audioDuration = this.state.audioBuffer.duration; + this.state.originalSampleRate = originalSampleRate; + this.state.resampleRatio = this.state.audioContext.sampleRate / originalSampleRate; + + console.log(`AudioContext rate: ${this.state.audioContext.sampleRate}Hz, resample ratio: ${this.state.resampleRatio.toFixed(3)}x`); + this.renderWaveform(); this.dom.playbackControls.style.display = 'flex'; this.dom.playbackIndicator.style.display = 'block'; this.dom.clearAudioBtn.disabled = false; this.dom.replayBtn.disabled = false; - this.showMessage(`Audio loaded: ${this.state.audioDuration.toFixed(2)}s`, 'success'); + this.showMessage(`Audio loaded: ${this.state.audioDuration.toFixed(2)}s @ ${originalSampleRate}Hz`, 'success'); this.renderCallback('audioLoaded'); } catch (err) { this.showMessage(`Error loading audio: ${err.message}`, 'error'); |
