fix: correct sample rate detection for Bluetooth devices#85
Merged
Conversation
67ecc2f to
a6b36b4
Compare
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.
a6b36b4 to
900fab2
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
kAudioStreamPropertyPhysicalFormatquery that returned BT HFP rate (24kHz) instead of tap delivery rate (48kHz) on AirPodskAudioTapPropertyFormatquery on tap directly — authoritative source for actual data ratekAudioDevicePropertyActualSampleRateverification at first IOProc callbackDualSourceRecorder.stop()Fixes #82
Root Cause
queryStreamSampleRatequeriedkAudioStreamPropertyPhysicalFormatwithkAudioObjectPropertyScopeOutputon 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, causingAudioMixer.resample(from: 24000, to: 16000)on data that was actually at 48kHz → 2x speedup.Fix (3 layers)
kAudioTapPropertyFormaton tap object — same approach as AudioCap and RecapkAudioDevicePropertyActualSampleRateat first IOProc callback — hardware-measured rateTest plan
snapToStandardRateandinferRateFromDuration(SampleRateQuery)crossCheckAppRate(DualSourceRecorder)