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 | |
| parent | e703c9376cd000161672ff225ff8e0f3c167ed9f (diff) | |
feat: add cpal-based auto device detection and configuration
Diffstat (limited to 'src')
| -rw-r--r-- | src/args.rs | 3 | ||||
| -rw-r--r-- | src/device_selector.rs | 78 | ||||
| -rw-r--r-- | src/main.rs | 22 |
3 files changed, 84 insertions, 19 deletions
diff --git a/src/args.rs b/src/args.rs index 9a4bd0d..1f0b7df 100644 --- a/src/args.rs +++ b/src/args.rs @@ -69,7 +69,8 @@ pub struct DMVOPArguments { long = "device", alias = "dev", allow_hyphen_values = true, - require_equals = true + require_equals = true, + default_value = "auto" )] pub device_name: Option<String>, 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() +} diff --git a/src/main.rs b/src/main.rs index 2aa68ac..87f7657 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,9 @@ pub mod post_proc; mod args; pub use args::*; +mod device_selector; mod output_protocol; + use clap::Parser; use output_protocol::OutputProtocol; use std::path::PathBuf; @@ -220,30 +222,14 @@ async fn main() { // --------------------------------------------------------------- // Find the requested device and start capture // --------------------------------------------------------------- - let device_name = match &args.device_name { - 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); - } - }; - - let device = devices - .iter() - .find(|d| d.id == device_name || d.name == device_name) - .or_else(|| devices.first()); + let device = device_selector::pick_device(&args.device_name, &devices); match &device { Some(d) => { debug_log!("[dmvop] Using input device: {} (id: {})", d.name, d.id); } None => { - eprintln!( - "[dmvop] Device '{}' not found and no fallback available.", - device_name - ); + eprintln!("[dmvop] No input devices found. Make sure a microphone is connected."); std::process::exit(1); } } |
