Skip to content

Preserve Bluetooth playback quality during recording - #267

Open
yeaen wants to merge 2 commits into
kitlangton:mainfrom
yeaen:fix/bluetooth-playback-quality
Open

Preserve Bluetooth playback quality during recording#267
yeaen wants to merge 2 commits into
kitlangton:mainfrom
yeaen:fix/bluetooth-playback-quality

Conversation

@yeaen

@yeaen yeaen commented Jul 20, 2026

Copy link
Copy Markdown

I noticed two issues when using Hex with the following settings:

  • Input Device: MacBook Microphone
  • MacBook Output Device: AirPods
  • Audio Behavior While Recording: Do Nothing
  • Super Fast Mode: Off

Activating Hex would:

  1. Switch the AirPods into Bluetooth call mode—raising the volume and reducing audio quality—even though the MacBook microphone was selected as the input device.
  2. Leave the AirPods in call mode after transcription finished. Pausing and resuming audio was required to restore normal audio quality.

This PR resolves both issues in my testing.


  • Prevent Bluetooth audio from remaining in degraded call mode after recording.
  • Replace the default AVAudioEngine input tap with an on-demand, input-only Audio Queue bound directly to the selected microphone’s Core Audio UID.
  • Resolve saved microphone selections without changing macOS’s global default input device.
  • Preserve Super Fast pre-roll while capturing from the selected input.
  • Release the sound-effects engine after playback, with a timeout fallback for missed completion callbacks.
  • Treat empty input-only captures as recording failures.

Summary by CodeRabbit

  • New Features

    • Added input-only microphone recording for standard recordings, improving Bluetooth playback quality while recording.
    • Preferred microphones are now selected more reliably, including after audio-device changes.
    • Recording can continue smoothly when the active audio route changes.
  • Bug Fixes

    • Bluetooth audio now returns to normal quality promptly after recording and sound effects finish.
    • Improved sound-effect playback cleanup, including during audio-route changes.
    • Prevented outdated recording callbacks from affecting newer recordings.

Regular capture previously used an AVAudioEngine input tap that participated in the default hardware graph and changed macOS's global default input device. That could switch Bluetooth output into call mode even when Hex was configured to use the MacBook microphone, then leave playback degraded after recording.

Use an on-demand input Audio Queue with no output graph, bind it directly to the selected microphone's persistent Core Audio UID, and dispose it deterministically at stop. Resolve saved microphone selections against the current device list without changing the system default.

Bind Super Fast capture directly to the selected input while preserving pre-roll behavior. Release the sound-effects engine after playback so temporary Bluetooth routes return to normal, with a timeout fallback for missed completion callbacks.

Report empty input-only captures as recording failures and add regression coverage for selected-device resolution and stale capture callbacks.

Manual validation and automated tests were completed on the original backed-up stack.
@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Audio capture and playback

Layer / File(s) Summary
Input-only capture controller
Hex/Clients/InputOnlyCaptureController.swift, HexTests/RecordingRaceTests.swift
Adds device-bound Audio Queue recording, PCM conversion, file output, metering, timing estimates, cleanup, and stale-callback generation checks.
Device-specific capture routing
Hex/Clients/SuperFastCaptureController.swift, Hex/Clients/RecordingClient.swift, HexTests/RecordingRaceTests.swift
Resolves microphones by current Core Audio device UID, avoids changing system defaults, and binds selected devices to capture engines.
Recording lifecycle integration
Hex/Clients/RecordingClient.swift, .changeset/calm-airpods-routing.md
Integrates input-only capture into recording sessions, route changes, warm capture, start/stop handling, teardown, cleanup, and release notes.
Sound-effect playback shutdown
Hex/Clients/SoundEffect.swift
Replaces idle shutdown tracking with per-playback tokens and duration-based fallback tasks.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RecordingClient
  participant DeviceResolver
  participant InputOnlyCaptureController
  participant AudioQueue
  participant AVAudioFile
  RecordingClient->>DeviceResolver: resolve capture input device
  DeviceResolver-->>RecordingClient: selected AudioDeviceID
  RecordingClient->>InputOnlyCaptureController: begin recording
  InputOnlyCaptureController->>AudioQueue: start input capture
  AudioQueue->>InputOnlyCaptureController: deliver audio buffers
  InputOnlyCaptureController->>AVAudioFile: write converted PCM
  RecordingClient->>InputOnlyCaptureController: finish recording
  InputOnlyCaptureController-->>RecordingClient: captured or failed result
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: preserving Bluetooth playback quality during recording.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.changeset/calm-airpods-routing.md:
- Line 5: Append the relevant GitHub issue or pull-request number to the
changeset summary in .changeset/calm-airpods-routing.md, using the required
(`#123`) format. Preserve the existing user-facing description and only add the
reference.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5ba03f18-43b9-46c8-a998-5b1792ea99cb

📥 Commits

Reviewing files that changed from the base of the PR and between 881c46f and 1219ebf.

📒 Files selected for processing (6)
  • .changeset/calm-airpods-routing.md
  • Hex/Clients/InputOnlyCaptureController.swift
  • Hex/Clients/RecordingClient.swift
  • Hex/Clients/SoundEffect.swift
  • Hex/Clients/SuperFastCaptureController.swift
  • HexTests/RecordingRaceTests.swift

Comment thread .changeset/calm-airpods-routing.md Outdated
"hex-app": patch
---

Keep Bluetooth playback in high-quality mode when Hex records from another microphone, and restore normal quality promptly after recording and sound effects finish.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the GitHub issue/PR reference to the changeset summary.

The summary describes user-facing impact but omits the issue/PR number. Append the relevant reference (e.g. one of the issues this PR addresses) in the (#123) format so release notes and GitHub linking work.

As per coding guidelines: .changeset/*.md should "Include user-facing impact and GitHub issue/PR number in the format 'Improve Fn hotkey stability (#89)'".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.changeset/calm-airpods-routing.md at line 5, Append the relevant GitHub
issue or pull-request number to the changeset summary in
.changeset/calm-airpods-routing.md, using the required (`#123`) format. Preserve
the existing user-facing description and only add the reference.

Source: Coding guidelines

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