Skip to content

fix: correct sample rate detection for Bluetooth devices#85

Merged
pasrom merged 3 commits intomainfrom
fix/bluetooth-sample-rate
Apr 8, 2026
Merged

fix: correct sample rate detection for Bluetooth devices#85
pasrom merged 3 commits intomainfrom
fix/bluetooth-sample-rate

Conversation

@pasrom
Copy link
Copy Markdown
Owner

@pasrom pasrom commented Apr 3, 2026

Summary

  • Fix wrong-scope kAudioStreamPropertyPhysicalFormat query that returned BT HFP rate (24kHz) instead of tap delivery rate (48kHz) on AirPods
  • Add kAudioTapPropertyFormat query on tap directly — authoritative source for actual data rate
  • Add kAudioDevicePropertyActualSampleRate verification at first IOProc callback
  • Add post-hoc mic-duration cross-check as safety net in DualSourceRecorder.stop()
  • On nominal/stream rate mismatch, prefer nominal over stream (stream scope reflects BT HFP, not tap rate)

Fixes #82

Root Cause

queryStreamSampleRate queried kAudioStreamPropertyPhysicalFormat with kAudioObjectPropertyScopeOutput on the aggregate device. Tap audio arrives as an input stream — output scope returns the physical output device's format, which on BT HFP/SCO drops to 24kHz. The cross-validation then incorrectly preferred this 24kHz, causing AudioMixer.resample(from: 24000, to: 16000) on data that was actually at 48kHz → 2x speedup.

Fix (3 layers)

  1. Primary: kAudioTapPropertyFormat on tap object — same approach as AudioCap and Recap
  2. Secondary: kAudioDevicePropertyActualSampleRate at first IOProc callback — hardware-measured rate
  3. Tertiary: Post-hoc cross-check comparing raw file size to mic WAV duration, snapped to nearest standard rate

Test plan

  • 12 new unit tests for snapToStandardRate and inferRateFromDuration (SampleRateQuery)
  • 5 new unit tests for crossCheckAppRate (DualSourceRecorder)
  • All 31 AudioTapLib tests pass
  • All 800 app tests pass
  • All 53 E2E/integration tests pass
  • Manual test with meeting-simulator: detection → recording → transcription → protocol (correct duration, no Mickey Mouse)
  • Lint clean

@github-actions github-actions bot added the bug Something isn't working label Apr 3, 2026
@pasrom pasrom force-pushed the fix/bluetooth-sample-rate branch from 67ecc2f to a6b36b4 Compare April 3, 2026 20:35
pasrom added 3 commits April 8, 2026 22:15
Pure functions for post-hoc sample rate cross-check. inferRateFromDuration
back-calculates the actual IOProc data rate from raw file size and known
mic recording duration. snapToStandardRate resolves measurement jitter
to the nearest standard audio rate (44100, 48000, etc.).

Part of Bluetooth sample rate fix for Issue #82.
Replace wrong-scope kAudioStreamPropertyPhysicalFormat (output scope
returns BT HFP rate on AirPods, not the tap's delivery rate) with
kAudioTapPropertyFormat on the tap object — the authoritative source.

Also add kAudioDevicePropertyActualSampleRate check at first IOProc
callback, which returns the hardware-measured rate from the running
device. Falls back to nominal rate on mismatch instead of stream rate.

Fixes the 2x speedup / Mickey Mouse effect on Bluetooth devices where
the aggregate device's output-scope stream format reports the HFP
input rate (24kHz) instead of the actual IOProc data rate (48kHz).

Fixes #82.
After recording stops, compare the app raw file size to the mic WAV
duration to back-calculate the actual IOProc data rate. If it differs
significantly (>5%) from the device-reported rate, override with the
inferred rate snapped to the nearest standard audio rate.

This catches cases where CoreAudio device property queries return
the wrong rate (e.g. BT HFP rate instead of aggregate IOProc rate).
Only active in dual-source mode (mic available).

Part of #82.
@pasrom pasrom force-pushed the fix/bluetooth-sample-rate branch from a6b36b4 to 900fab2 Compare April 8, 2026 20:21
@pasrom pasrom merged commit 10e6136 into main Apr 8, 2026
6 checks passed
@pasrom pasrom deleted the fix/bluetooth-sample-rate branch April 8, 2026 20:27
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.

Bug: Sample rate mismatch causes accelerated app audio (Mickey Mouse effect) in multi-device setups

1 participant