Skip to content

fix: harden USB audio sample rate detection#65

Merged
pasrom merged 6 commits intomainfrom
fix/usb-audio-sample-rate-hardening
Apr 2, 2026
Merged

fix: harden USB audio sample rate detection#65
pasrom merged 6 commits intomainfrom
fix/usb-audio-sample-rate-hardening

Conversation

@pasrom
Copy link
Copy Markdown
Owner

@pasrom pasrom commented Mar 23, 2026

Summary

  • Add SampleRateQuery pure functions that validate and cross-check sample rates from two independent CoreAudio sources (nominal rate + physical stream format)
  • Wire validated rate detection into AppAudioCapture — replaces single unchecked AudioObjectGetPropertyData call with dual-source cross-validation and IOProc first-frame rate verification
  • Add settling retry for USB device restarts — if rate query returns 0 after restart, retry after 1s to allow USB devices time to negotiate format
  • Improve rate logging in DualSourceRecorder.stop() for diagnosability
  • Verify mic hardware format rate after engine restart

Root cause: USB headsets (e.g. Jabra PRO 930) can negotiate a different sample rate than the assumed 48kHz. The aggregate device may run at 44.1kHz, but the old code fell back to 48kHz on query failure → wrong resampling ratio → ~8.7% pitch shift upward → Mickey Mouse voice.

Test Plan

  • 11 new unit tests for SampleRateQuery validation logic (zero, negative, unreasonably high, common USB rates, cross-validation matrix)
  • All 19 AudioTapLib tests pass
  • All MeetingTranscriber tests pass (29 pre-existing snapshot failures unrelated)
  • Lint clean (1 pre-existing closure_body_length warning)
  • Manual test with USB headset (Jabra PRO 930) to verify correct rate detection

@github-actions github-actions bot added the bug Something isn't working label Mar 23, 2026
@pasrom pasrom force-pushed the fix/usb-audio-sample-rate-hardening branch 3 times, most recently from 84665bb to bb255b2 Compare March 23, 2026 20:16
@pasrom pasrom changed the title fix: harden USB audio sample rate detection to prevent Mickey Mouse voice fix: harden USB audio sample rate detection Mar 24, 2026
pasrom added 5 commits April 2, 2026 17:11
Extract sample rate validation logic into pure testable functions.
Handles USB audio devices that negotiate rates different from the
requested 48kHz (e.g. Jabra PRO 930 at 44.1kHz). Includes cross-
validation between nominal and stream rates to detect mismatches.

Part of USB audio sample rate hardening to fix Mickey Mouse voice.
Query both nominal rate and physical stream format rate from the
aggregate device, cross-validate, and pick the more trustworthy
value. Also verify rate on first IOProc callback to catch late
device settling (common with USB audio like Jabra PRO 930).

Previously, a single unchecked AudioObjectGetPropertyData call
could return 0 or stale values, causing fallback to 48kHz when
the USB device actually runs at 44.1kHz → wrong resampling ratio
→ Mickey Mouse voice.
After restarting app audio capture on device change, verify that
actualSampleRate was detected (non-zero). If not, retry after 1s
to allow USB devices time to negotiate their format.
Log the actual vs requested vs target rate prominently to make
Mickey Mouse voice issues diagnosable from logs. Warn when the
actual rate differs from the requested rate.
Log and validate the hardware sample rate after recreating the
AVAudioEngine. Helps diagnose issues where USB devices haven't
settled their format by the time the engine starts.
@pasrom pasrom merged commit aadfc80 into main Apr 2, 2026
6 checks passed
@pasrom pasrom deleted the fix/usb-audio-sample-rate-hardening branch April 2, 2026 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant