Skip to content

Investigate external mic capture with Bluetooth output#64

Open
ntdkhang wants to merge 1 commit into
watzon:mainfrom
ntdkhang:fix/external-mic-bluetooth-output
Open

Investigate external mic capture with Bluetooth output#64
ntdkhang wants to merge 1 commit into
watzon:mainfrom
ntdkhang:fix/external-mic-bluetooth-output

Conversation

@ntdkhang
Copy link
Copy Markdown

@ntdkhang ntdkhang commented May 23, 2026

Important note: AI-generated candidate fix

This PR was written entirely by AI. I am opening it because I hit this bug locally and asked an AI coding assistant to investigate and produce a potential fix. Please treat the code primarily as a reference implementation, a reproduction report, and a possible direction for maintainers to evaluate, rather than as a patch that should be merged as-is without careful review.

I do not want to misrepresent authorship or review confidence: the implementation should be reviewed by someone familiar with the app's macOS audio architecture before merging.

Problem observed

When Pindrop is configured to use an explicit external microphone, switching macOS output to Bluetooth headphones can cause Pindrop dictation to capture no microphone audio.

My setup:

  • Pindrop microphone selection: external microphone
  • macOS output: Bluetooth headphones, used for music
  • Expected: Pindrop continues recording from the external microphone
  • Actual before this patch: Pindrop appears to record silence / no useful input
  • Other dictation apps, such as Whisper Flow, did not show the same behavior in this setup

Candidate fix

This patch changes the production microphone capture backend from AVAudioEngine.inputNode to a CoreAudio input-device capture path. The idea is to capture directly from the resolved AudioDeviceID for the selected microphone so input capture is not coupled to the current output/Bluetooth route.

The existing AudioCaptureBackend abstraction and test injection path remain in place.

Local validation

  • Built unsigned Debug app successfully with Xcode 26.5
  • Ran unit test plan successfully: 687 tests passed
  • Manually launched the local debug build and confirmed the original setup works after the change: external microphone selected in Pindrop + Bluetooth headphones selected as macOS output + music playing + Pindrop dictation captures speech

Commands used:

DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild -project Pindrop.xcodeproj -scheme Pindrop -configuration Debug -derivedDataPath DerivedData CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO build
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild test -project Pindrop.xcodeproj -scheme Pindrop -testPlan Unit -destination 'platform=macOS' CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO

Suggested maintainer review areas

  • Whether replacing the default microphone backend with CoreAudio direct capture is the right architectural direction
  • Whether the input stream format handling is robust across USB, aggregate, Bluetooth, and virtual input devices
  • Whether this should instead be fixed by configuring AVAudioEngine / AUAudioUnit differently
  • Whether additional tests or a smaller targeted change would be preferable

Again: this is AI-generated code submitted transparently as a candidate fix and diagnostic reference.

@ntdkhang ntdkhang marked this pull request as ready for review May 23, 2026 06:17
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a90f2a6a0d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +531 to +535
destroyCaptureDevice()
do {
try startResolvedCapture(onBuffer: activeOnBuffer, onAudioLevel: activeOnAudioLevel)
} catch {
Log.audio.error("Failed to switch input device: \(error.localizedDescription)")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Surface capture restart failures when switching input devices

When setPreferredInputDeviceUID runs during an active recording, it destroys the current device first and then attempts to start a new one; if restart fails, the error is only logged and execution continues. That leaves this backend in a non-capturing state while AudioRecorder can still report an active recording, so the next stop path can fail with notRecording and discard the in-progress session. This should either propagate failure to the caller or restore the previous capture device before returning.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant