Skip to content

Capture system audio from whatever device the meeting app uses - #7283

Merged
ComputelessComputer merged 4 commits into
mainfrom
fix/system-audio-follows-meeting-app
Sep 3, 2026
Merged

Capture system audio from whatever device the meeting app uses#7283
ComputelessComputer merged 4 commits into
mainfrom
fix/system-audio-follows-meeting-app

Conversation

@ComputelessComputer

@ComputelessComputer ComputelessComputer commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

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.po and compiled messages.ts files, Lingui drops the audio-settings strings "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.

@netlify

netlify Bot commented Sep 3, 2026

Copy link
Copy Markdown

Deploy Preview for anarlog canceled.

Name Link
🔨 Latest commit e9b8df7
🔍 Latest deploy log https://app.netlify.com/projects/anarlog/deploys/6a9958fbfee9cd0009a8a23d

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.

Fix All in Cursor

❌ 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));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f6a0796. Configure here.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

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.

@ComputelessComputer
ComputelessComputer force-pushed the fix/system-audio-follows-meeting-app branch from f6a0796 to ed60341 Compare September 3, 2026 08:13
@ComputelessComputer
ComputelessComputer force-pushed the fix/system-audio-follows-meeting-app branch from ed60341 to 68c187a Compare September 3, 2026 08:25
@ComputelessComputer
ComputelessComputer force-pushed the fix/system-audio-follows-meeting-app branch from 68c187a to f0c92c1 Compare September 3, 2026 09:28
@ComputelessComputer
ComputelessComputer force-pushed the fix/system-audio-follows-meeting-app branch from f0c92c1 to f0b9a39 Compare September 3, 2026 09:39
@ComputelessComputer
ComputelessComputer force-pushed the fix/system-audio-follows-meeting-app branch from f0b9a39 to 627be7d Compare September 3, 2026 10:00
@ComputelessComputer
ComputelessComputer force-pushed the fix/system-audio-follows-meeting-app branch from 627be7d to 27cf2dc Compare September 3, 2026 10:06
@ComputelessComputer
ComputelessComputer force-pushed the fix/system-audio-follows-meeting-app branch from 27cf2dc to f371cc6 Compare September 3, 2026 10:18
Base automatically changed from fix/name-last-remote-speaker-by-elimination to main September 3, 2026 10:28
@ComputelessComputer
ComputelessComputer force-pushed the fix/system-audio-follows-meeting-app branch 3 times, most recently from a094beb to cec7d5f Compare September 3, 2026 10:52
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.
@ComputelessComputer
ComputelessComputer force-pushed the fix/system-audio-follows-meeting-app branch from cec7d5f to 88f916a Compare September 3, 2026 11:13
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.
@ComputelessComputer
ComputelessComputer merged commit 66d159b into main Sep 3, 2026
22 checks passed
@ComputelessComputer
ComputelessComputer deleted the fix/system-audio-follows-meeting-app branch September 3, 2026 11:41
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