fix: show one linux audio backend

This commit is contained in:
Edison Jwa
2026-05-25 18:31:20 +09:00
parent 487aff4e7c
commit 2896f14ec9
@@ -124,14 +124,14 @@ fn pulse_output_name_from_id(id: &str) -> Option<&str> {
}
pub(crate) fn list_input_devices(default_label: Option<&str>) -> Vec<AudioDeviceInfo> {
visible_devices(list_devices(LinuxDirection::Input))
list_devices(LinuxDirection::Input)
.iter()
.map(|device| device.to_audio_device_info(default_label))
.collect()
}
pub(crate) fn list_output_devices(default_label: Option<&str>) -> Vec<AudioDeviceInfo> {
visible_devices(list_devices(LinuxDirection::Output))
list_devices(LinuxDirection::Output)
.iter()
.map(|device| device.to_audio_device_info(default_label))
.collect()
@@ -237,39 +237,22 @@ fn select_device(
}
fn list_devices(direction: LinuxDirection) -> Vec<LinuxDevice> {
let mut devices = Vec::new();
match list_pipewire_nodes(direction) {
Ok(mut pipewire) => devices.append(&mut pipewire),
Ok(pipewire) if !pipewire.is_empty() => return pipewire,
Ok(_) => {
warn!(target: "chanora_audio", "PipeWire device enumeration returned no devices; trying PulseAudio fallback")
}
Err(error) => {
warn!(target: "chanora_audio", error = %error, "PipeWire device enumeration failed")
}
}
match list_pulse_devices(direction) {
Ok(mut pulse) => devices.append(&mut pulse),
Ok(pulse) => pulse,
Err(error) => {
warn!(target: "chanora_audio", error = %error, "PulseAudio device enumeration failed")
warn!(target: "chanora_audio", error = %error, "PulseAudio device enumeration failed");
Vec::new()
}
}
devices
}
fn visible_devices(devices: Vec<LinuxDevice>) -> Vec<LinuxDevice> {
let pipewire_labels = devices
.iter()
.filter(|device| device.backend == LinuxBackend::PipeWire)
.map(|device| normalized_device_label(&device.label))
.collect::<std::collections::HashSet<_>>();
devices
.into_iter()
.filter(|device| {
device.backend == LinuxBackend::PipeWire
|| !pipewire_labels.contains(&normalized_device_label(&device.label))
})
.collect()
}
fn normalized_device_label(label: &str) -> String {
label.split_whitespace().collect::<Vec<_>>().join(" ").to_lowercase()
}
fn list_pipewire_nodes(direction: LinuxDirection) -> Result<Vec<LinuxDevice>, String> {
@@ -951,8 +934,7 @@ mod tests {
use super::{
input_device_id, input_device_node_id_from_id, output_device_id,
output_device_node_id_from_id, pulse_input_device_id, pulse_input_name_from_id,
pulse_output_device_id, pulse_output_name_from_id, visible_devices, LinuxBackend,
LinuxDevice, LinuxDirection,
pulse_output_device_id, pulse_output_name_from_id,
};
#[test]
@@ -982,32 +964,4 @@ mod tests {
Some("alsa_output.pci-0000_00_1f.3")
);
}
#[test]
fn visible_devices_hides_pulse_duplicates_when_pipewire_matches() {
let devices = visible_devices(vec![
LinuxDevice {
backend: LinuxBackend::PipeWire,
direction: LinuxDirection::Output,
raw_id: "42".to_string(),
label: "Built-in Audio Analog Stereo".to_string(),
},
LinuxDevice {
backend: LinuxBackend::PulseAudio,
direction: LinuxDirection::Output,
raw_id: "alsa_output.pci-0000".to_string(),
label: " built-in audio analog stereo ".to_string(),
},
LinuxDevice {
backend: LinuxBackend::PulseAudio,
direction: LinuxDirection::Output,
raw_id: "bluez_output.headset".to_string(),
label: "Headset".to_string(),
},
]);
assert_eq!(devices.len(), 2);
assert!(devices.iter().any(|device| device.backend == LinuxBackend::PipeWire));
assert!(devices.iter().any(|device| device.label == "Headset"));
}
}