feat(audio): experimental speaker rate-trim to chase the crackle (#7)

The DS5 DAC and the USB host audio clock are independent ~48kHz crystals; the residual ppm drift slowly underruns the controller's audio buffer = the periodic crackle. The speaker path is 1:1 (no resampler), so it delivered exactly the host's 48kHz; this adds a fine rate trim that delivers 48000+(trim) samples/s via a zero-order-hold duplicate/drop accumulator to null the drift.

New speaker_rate_trim config field (stored 0..200 = -100..+100 Hz, default 100 = 0 Hz = exact no-op since 48000/48000 is exact in double). Swept on the OLED Settings screen (new 'SpkTrim' item) with the D-pad <</>> at 1 Hz/step. Reset/Wipe items shifted to idx 16/17 (named constants, action handlers auto-follow).

Diagnostic intent: if one value silences the crackle and HOLDS, drift was a static offset and this is the fix; if it nulls then creeps back, that proves dynamic drift needing adaptive resampling. Tune with scripts/sine_ch12.py + time the crackle interval.

NOT YET BUILT OR HIL-TESTED. Experimental branch only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
MarcelineVPQ
2026-06-04 19:30:43 -06:00
co-authored by Claude Opus 4.8
parent 878a742bfc
commit 3b157cb9a1
5 changed files with 62 additions and 17 deletions
+4
View File
@@ -8,6 +8,10 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Version
## [Unreleased]
### Added (experimental — `audio/speaker-rate-trim` branch)
- **`SpkTrim` setting — fine speaker clock-drift trim to chase the periodic crackle (#7).** The DS5's DAC and the USB host's audio clock are independent ~48 kHz crystals; the residual ppm mismatch slowly drifts the controller's audio buffer, which underruns and crackles. The speaker path now delivers `48000 + trim` samples/s (via a zero-order-hold duplicate/drop accumulator) instead of exactly 48000, to null that drift. New `speaker_rate_trim` config field (stored 0200 = **100…+100 Hz**, default **0 Hz** = exact no-op), swept on the OLED **Settings** screen with the D-pad ▶◀ at 1 Hz/step. **Experimental / diagnostic:** if one value silences the crackle and *holds*, the drift was a static offset and this is the fix; if it nulls then creeps back, that proves the drift is dynamic and needs adaptive resampling. Tune with `scripts/sine_ch12.py` playing a steady tone and time the interval between crackles.
---
## [0.6.11-oled-edition] — 2026-06-02