feat: fail over to an external mic when the MacBook lid is closed - #117
Merged
Merged
Conversation
Apple hardware disconnects the built-in microphone while the lid is closed, but macOS still lists it as an available input, so Utter recorded silence with no error. - detect lid state via IOPMrootDomain AppleClamshellState - classify inputs as built-in/external and resolve a usable device - switch to an external input mid-session when the active one is lost - fail fast with a localized message when no usable input exists - keep the wireless-remote capture path unchanged
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
On a MacBook used in clamshell mode (external display, lid closed), the built-in microphone is physically disconnected in hardware, but macOS still lists it in CoreAudio as an available input device. Utter selected it, recorded silence, and never surfaced an error — the user spoke, saw the overlay, and got no text. This is the docked-at-a-desk case, a primary usage context for a menu-bar dictation app.
Fix
AppleClamshellStatefrom the IOPMrootDomain IORegistry entry (ClamshellState). Desktops without a lid report open and are unaffected.kAudioDevicePropertyTransportType(AudioInputDevices). Continuity/iPhone, USB, Bluetooth, display, and aggregate inputs are all treated as external.AudioInputResolverkeeps the user's selected/system-default input when usable, and otherwise falls back to the first available external input;.unavailablewhen none exists.AVAudioConverterwhen the new device's format differs.Files
Sources/Audio/ClamshellState.swift,Sources/Audio/AudioInputDevice.swift,Sources/Audio/AudioInputResolver.swift,Tests/OpenTypeTests/AudioInputResolverTests.swiftSources/Audio/AudioCaptureManager.swift,Sources/App/VoicePipeline+Recording.swift,Sources/Integration/InputSessionCoordinator.swift, bothLocalizable.stringsdocs/sdlc/changes/2026-10-01-clamshell-mic-failover/Verification
bash scripts/sdlc-checks.sh— passbash scripts/ci-basic-checks.sh— pass (strings lint + key parity)swift test— 808 tests, 18 skipped, 0 failures (run via the Xcode toolchain; the CommandLineTools SDK has no XCTest)Residual risk
The SDLC verification artifact is at
Status: pending approvalpending human review.