Fix: microphone capture dies on input format/route change (Meet, earbuds)#30
Open
morellid wants to merge 1 commit into
Open
Fix: microphone capture dies on input format/route change (Meet, earbuds)#30morellid wants to merge 1 commit into
morellid wants to merge 1 commit into
Conversation
The mic captured the input format once, created an AVAudioFile in that format, and wrote raw tap buffers directly. When a conferencing app reconfigures the shared input device mid-stream (e.g. Google Meet's 3-channel voice-processing mode), or the default input device changes (plugging in earbuds), the tap buffers stop matching the file format, every write fails with CoreAudio -50, and the mic track dies silently. Two independent safety nets, mirroring the existing SystemAudioCapture: - writeBuffer() normalises every tap buffer to the file's fixed processingFormat via AVAudioConverter (block-based form, handles sample-rate/channel changes), with a direct-write fast path when the format is unchanged. - An AVAudioEngineConfigurationChange observer (delivered on .main, so it serialises with stop()) rebinds the input and reinstalls the tap on a route change, with a bounded retry for a transient zero sample-rate while the route settles. applyInputDevice() no longer force-unwraps inputNode.audioUnit (can be nil during route churn). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Problem
MicCapturereads the input format once at start, creates theAVAudioFilein that format, and writes raw tap buffers directly. If the input device is reconfigured mid-stream, the tap buffers stop matching the file's format and everywrite(from:)fails with CoreAudio-50(paramErr), so the microphone track dies silently for the rest of the recording.Two common triggers:
In both cases
--miccapture produced only the audio recorded before the change.Reproduction
ownscribe ... --mic(start recording).-50write errors.Fix
Two independent safety nets, both mirroring patterns already used by
SystemAudioCapturein the same file:writeBuffer(_:)converts every tap buffer to the file's fixedprocessingFormatthrough anAVAudioConverter(rebuilt when the incoming format changes), using the block-basedconvert(to:error:withInputFrom:)form so sample-rate / channel-count changes are handled. A fast path writes directly when the format is unchanged, so the steady-state path is unchanged from before.AVAudioEngineConfigurationChangeobserver (delivered on.main, so it serialises withstop()) removes the tap, re-applies a named device if one was requested, reinstalls the tap, and restarts the engine. A transient zero sample-rate while the route settles is retried on.main(bounded) rather than relying on a second notification, so one transient change cannot leave the mic dead.Also removed an
inputNode.audioUnit!force-unwrap that could crash during route churn.Testing
swift/build.sh(Swift 6.2, macOS 26).--mic, joined a Google Meet call mid-recording, and confirmed both the pre-call and post-call speech are present in the merged output (previously only pre-call survived).Notes / deliberate tradeoffs
🤖 Generated with Claude Code