aboutsummaryrefslogtreecommitdiff
path: root/src/device_selector.rs
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-06-22 01:41:30 +0800
committer魏曹先生 <1992414357@qq.com>2026-06-22 01:42:02 +0800
commit38d6036c1c3bff292b8f1d42c9eb0f26c34fe4a7 (patch)
treee6f324ee734a94c540b9d67eb24d1400438e3f22 /src/device_selector.rs
parente703c9376cd000161672ff225ff8e0f3c167ed9f (diff)
feat: add cpal-based auto device detection and configuration
Diffstat (limited to 'src/device_selector.rs')
-rw-r--r--src/device_selector.rs78
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()
+}