aboutsummaryrefslogtreecommitdiff
path: root/src
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
parente703c9376cd000161672ff225ff8e0f3c167ed9f (diff)
feat: add cpal-based auto device detection and configuration
Diffstat (limited to 'src')
-rw-r--r--src/args.rs3
-rw-r--r--src/device_selector.rs78
-rw-r--r--src/main.rs22
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);
}
}