Address Windows output endpoints by ID, not by name - #213
Conversation
Two silent bugs in the audio-output picker, both on Windows. Identical hardware gives two endpoints the same friendly name, so storing that name meant picking the second of a pair resolved to the first: the loopback recorded the wrong output and clips came out with no system audio. The picker now stores the endpoint ID and shows the friendly name. Existing configs keep working through the friendly-name match the resolver already tries after the ID. The resolver also fell back to the default output whenever a configured render endpoint matched nothing, which is right for the user's pick and wrong for the communications loopback: if that endpoint went inactive between being read and being resolved, the extra capture landed on the console default, which the system capture already records, and the mixer summed the same audio twice. Captures now say which they mean - AudioDevice::Preferred may be substituted, Exact may not - so the comms capture simply does not start instead of doubling the mix.
A Windows config written before this change holds a friendly name where the picker now expects an endpoint ID, which gave the saved device two identical rows: the real endpoint plus the "offline device" row that keeps an unlisted value visible. Matching a listed endpoint's label as well as its id collapses that back to one row, and the value becomes an ID as soon as anything is picked. Migrating it outright is deliberately left alone: the settings window only writes on Save, so rewriting it on open would show unsaved edits nobody made.
|
@coderabbitai review |
|
Warning Review limit reachedNext included review available in 9 minutes. View limit detailsLimit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (8)
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. Comment |
|
Closes #212. Both bugs are Windows-only, both silent, both from the output picker in #210.
The wrong endpoint
list_audio_outputsstoredPKEY_Device_FriendlyNameas the value the capture backend resolves. Identical hardware gives two endpoints the same friendly name, so picking the second of a pair resolved to the first and the loopback recorded an output nobody was listening to: clips with no system audio and nothing in the log to explain it. The picker now storesGetId()and shows the friendly name, which is whatendpoint()already tried first.The doubled mix
endpoint()fell back to the console default whenever a configured render endpoint matched nothing, deliberately: recording the current output beats recording silence when the user's pick disappears. That fallback also covered the internal communications-endpoint capture. If the comms endpoint went inactive betweendefault_render_endpoints()reading it and the capture thread resolving its opaque ID (headset unplugged, comms default changed), the extra capture opened a second loopback on the console default — which the system capture is already recording, since comms is only spawned when the user picked no output. Both fedAudioMixer::add, so clips carried system audio at roughly double amplitude, clamped on drain into distortion, behind a single info log.The fallback is right for one caller and wrong for the other, so the call now says which it means:
capture_audiotakes it in place ofOption<&str>on all three backends. Windows falls back only forPreferredon the render flow; the comms capture passesExact, so when its endpoint is gone the capture simply does not start — which main already tolerates, since it is spawned as an optional extra. Linux and macOS treatPreferredandExactalike (PipeWire's session manager and SCK decide substitution themselves), documented at the enum.Upgraded configs
A config written by an earlier build holds a friendly name. Capture still resolves it, through the same name match as before, so recording is unaffected. In the picker that value would have produced two identical rows (the real endpoint plus the "offline device" row that keeps an unlisted value visible), so the picker now matches a listed endpoint's label as well as its id; the stored value becomes an ID the next time anything is picked. Rewriting it on open is deliberately not done — the settings window only writes on Save, so that would show unsaved edits nobody made.
Verified
cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warnings,cargo build --workspace --all-targets,cargo test --workspace(23 test binaries, no failures) — plus three new tests covering the selector and which variants may be substituted.cargo check --target x86_64-pc-windows-msvc -p rewynd-capture -p rewynd-configfor the two crates that hold the actual fixes. The app crate cannot cross-compile here (C build scripts want the MSVC toolchain), so its Windows block is on CI's windows job.