Capture system audio from whatever device the meeting app uses - #7283
Conversation
✅ Deploy Preview for anarlog canceled.
|
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit f6a0796. Configure here.
| // the capture settings start from one view of the world. | ||
| let routing = | ||
| POLLS_OUTPUT_ROUTING.then(|| OutputRoutingTracker::new(headphone_only_output())); | ||
| let thread = std::thread::spawn(move || Self::event_loop(event_rx, actor, routing)); |
There was a problem hiding this comment.
Routing poll restarts source in a loop
High Severity
OutputRoutingTracker samples headphone_only_output() in DeviceChangeWatcher::spawn before play_silence() marks the default output running and before capture_settings() opens streams. When the default is speakers and headphones are already playing, that first sample is true, later polls see false, and the source restarts. device_change does not consume the restart budget, so the cycle repeats every few seconds for the rest of the session.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit f6a0796. Configure here.
There was a problem hiding this comment.
Fixed in e9b8df7. Two causes: on macOS running_output_devices used DeviceIsRunningSomewhere, which counted our own silence stream on the default output (Linux/Windows already skip self_pid), so a speakers default with a meeting app on headphones flipped the verdict as soon as silence started playing. It now reads Core Audio process objects and skips our pid. The watcher is also spawned after the streams open and seeded with the verdict they opened with, so there is no second sample to disagree with.
f6a0796 to
ed60341
Compare
ed60341 to
68c187a
Compare
68c187a to
f0c92c1
Compare
f0c92c1 to
f0b9a39
Compare
f0b9a39 to
627be7d
Compare
627be7d to
27cf2dc
Compare
27cf2dc to
f371cc6
Compare
a094beb to
cec7d5f
Compare
Meeting apps pick their own speaker, so recording the system default endpoint went silent whenever Zoom or Teams played through something else. macOS already taps all processes; this brings the other paths and the AEC decision in line without asking the user to pick speakers. Windows: activate WASAPI process loopback excluding our own process tree, which captures every other app regardless of endpoint (Windows 11). Older builds fall back to endpoint loopback on the device another process is actively rendering to, preferring the default. Linux: resolve the PipeWire target / PulseAudio monitor from the sink that has uncorked foreign sink inputs, preferring the default sink. macOS: decide AEC and mic isolation from every output device that is running, not just the default, so a headphone default no longer hides meeting audio leaking out of the built-in speakers.
System audio capture no longer needs a device from the user: macOS taps every process, Windows uses process loopback with an active-endpoint fallback, and Linux follows the sink other apps are playing into. The dropdown was a placebo on macOS and a footgun elsewhere, so it goes along with the plumbing that carried it. Drops speaker_device from CaptureConfig, CaptureParams, SessionParams and SourceArgs, the list_speaker_devices command and permission, the SpeakerInput device argument on every platform, the desktop setting, its UI row and tests, and the two catalog strings.
AEC and mic isolation are decided once when the streams open. If the meeting app only starts playing through the speakers afterwards, a headphone default kept AEC off while meeting audio leaked into the mic. The device watcher now polls headphone_only_output() every two seconds on macOS and restarts the source through the existing device_change path when the verdict differs from the one the streams opened with. Two consecutive polls have to agree so a one-off system sound does not bounce capture. Other platforms keep the event-only loop until their backends report running outputs.
cec7d5f to
88f916a
Compare
On macOS, running_output_devices counted our own silence stream on the default output, so a speakers default with a meeting app on headphones flipped the verdict right after startup. Use Core Audio process objects and skip our own pid, matching the Linux and Windows lookups. Spawn the device watcher after the streams open and seed the routing tracker with the verdict they opened with instead of sampling a second time.


Meeting apps pick their own speaker, so recording the system default
endpoint went silent whenever Zoom or Teams played through something
else. macOS already taps all processes; this brings the other paths
and the AEC decision in line without asking the user to pick speakers.
Windows: activate WASAPI process loopback excluding our own process
tree, which captures every other app regardless of endpoint (Windows
11). Older builds fall back to endpoint loopback on the device another
process is actively rendering to, preferring the default.
Linux: resolve the PipeWire target / PulseAudio monitor from the sink
that has uncorked foreign sink inputs, preferring the default sink.
macOS: decide AEC and mic isolation from every output device that is
running, not just the default, so a headphone default no longer hides
meeting audio leaking out of the built-in speakers.
Note
Low Risk
String-catalog cleanup only; no runtime logic in the shown diff.
Overview
Removes obsolete speaker-picker copy from desktop i18n catalogs now that General audio settings no longer exposes a Speakers output selector (microphone, retention, and Remember speakers remain).
Across locale
messages.poand compiledmessages.tsfiles, Lingui drops theaudio-settingsstrings "Speakers" and "Choose the speakers that play other participants so Anarlog records them." This keeps translations in sync with the UI change tied to the PR goal: capture meeting audio from whatever output the conferencing app uses instead of asking users to pick speakers.Reviewed by Cursor Bugbot for commit e9b8df7. Bugbot is set up for automated code reviews on this repo. Configure here.