aboutsummaryrefslogtreecommitdiff
path: root/src/device_selector.rs
blob: 12f8a79427c9fba62d33560d299b4291b067f17b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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()
}