diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-06-22 01:41:30 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-06-22 01:42:02 +0800 |
| commit | 38d6036c1c3bff292b8f1d42c9eb0f26c34fe4a7 (patch) | |
| tree | e6f324ee734a94c540b9d67eb24d1400438e3f22 /src/device_selector.rs | |
| parent | e703c9376cd000161672ff225ff8e0f3c167ed9f (diff) | |
feat: add cpal-based auto device detection and configuration
Diffstat (limited to 'src/device_selector.rs')
| -rw-r--r-- | src/device_selector.rs | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/src/device_selector.rs b/src/device_selector.rs new file mode 100644 index 0000000..12f8a79 --- /dev/null +++ b/src/device_selector.rs @@ -0,0 +1,78 @@ +use cpal::traits::{DeviceTrait, HostTrait}; + +/// Pick the best input device from the available list. +/// +/// - `"auto"` → query OS default input device via cpal, match against list +/// - `"<name>"` → match by device ID or name; exits if not found +/// - `None` → exits with error +pub fn pick_device<'a>( + name: &Option<String>, + devices: &'a [vtx_engine::AudioDevice], +) -> Option<&'a vtx_engine::AudioDevice> { + let name = match name { + Some(n) if n == "auto" || n.is_empty() => return pick_auto(devices), + Some(n) => n.as_str(), + None => { + eprintln!( + "[dmvop] No device specified. Use --device=<name> or --list-devices to see available devices." + ); + std::process::exit(1); + } + }; + + // Try exact match by id or name + if let Some(d) = devices.iter().find(|d| d.id == name || d.name == name) { + return Some(d); + } + + eprintln!( + "[dmvop] Device '{}' not found. Use --list-devices to see available devices.", + name + ); + std::process::exit(1); +} + +/// Use cpal to find the system's default input device, then match it +/// against the vtx-engine device list by WASAPI ID. +fn pick_auto<'a>(devices: &'a [vtx_engine::AudioDevice]) -> Option<&'a vtx_engine::AudioDevice> { + let host = cpal::default_host(); + let device = match host.default_input_device() { + Some(d) => d, + None => { + eprintln!("[dmvop] No default input device found."); + return devices.first(); + } + }; + + // Get the WASAPI device ID from cpal + let cpal_device_id = match device.id() { + Ok(id) => id, + Err(_) => return devices.first(), + }; + let cpal_raw_id = cpal_device_id.id().to_string(); + + // Match against vtx-engine devices by ID (substring — cpal might omit braces) + for dev in devices { + let dev_id_clean = dev.id.trim_matches('{').trim_matches('}'); + let cpal_id_clean = cpal_raw_id.trim_matches('{').trim_matches('}'); + if dev_id_clean.contains(cpal_id_clean) || cpal_id_clean.contains(dev_id_clean) { + return Some(dev); + } + } + + // Fallback: match by name (cpal DeviceId Display includes host:raw_id) + let cpal_display = cpal_device_id.to_string().to_lowercase(); + for dev in devices { + if cpal_display.contains(&dev.name.to_lowercase()) + || dev.name.to_lowercase().contains(&cpal_display) + { + return Some(dev); + } + } + + eprintln!( + "[dmvop] Default device ({}) not matched, using first available.", + cpal_raw_id + ); + devices.first() +} |
