1use cpal::traits::{DeviceTrait, HostTrait};
4use dev_utils::{format::*, read_input};
5use std::{error::Error, time::Duration};
6
7pub mod capture;
9pub mod config;
10pub mod playback;
11pub mod signal;
12
13pub fn list_audio_devices() -> Result<(), Box<dyn std::error::Error>> {
14 let host = cpal::default_host();
15
16 println!("Input devices:");
17 let input_devices = host.input_devices()?;
18 for (i, device) in input_devices.enumerate() {
19 println!("{}: {}", i, device.name()?);
20 }
21
22 println!("Output devices:");
23 let output_devices = host.output_devices()?;
24 for (i, device) in output_devices.enumerate() {
25 println!("{}: {}", i, device.name()?);
26 }
27
28 Ok(())
29}
30
31pub fn select_device(input: bool) -> Result<cpal::Device, Box<dyn Error>> {
32 let host = cpal::default_host();
33 let mut devices = if input {
34 host.input_devices()?
35 } else {
36 host.output_devices()?
37 };
38
39 println!(
40 "\n{}",
41 format!(
42 "Available {} Devices:",
43 if input { "Input" } else { "Output" }
44 )
45 .color(BLUE)
46 .style(Style::Bold)
47 );
48
49 let device_list: Vec<_> = devices.collect();
50 for (idx, device) in device_list.iter().enumerate() {
51 println!(
52 "{}. {}",
53 idx.to_string().color(GREEN),
54 device.name().unwrap_or_default().color(WHITE)
55 );
56 }
57
58 loop {
59 let choice = read_input::<usize>(Some("Select device number: "))?;
60 if choice < device_list.len() {
61 return Ok(device_list[choice].clone());
62 }
63 println!("Invalid selection. Try again.");
64 }
65}
66
67pub fn interpolate_color(value: f32, min: f32, max: f32) -> Color {
69 let t = ((value - min) / (max - min)).clamp(0.0, 1.0);
70
71 let colors = [
72 (0.0, (0, 0, 255)), (0.3, (0, 255, 0)), (0.6, (255, 255, 0)), (1.0, (255, 0, 0)), ];
77
78 let mut color1 = colors[0];
79 let mut color2 = colors[1];
80
81 for window in colors.windows(2) {
82 if t >= window[0].0 && t <= window[1].0 {
83 color1 = window[0];
84 color2 = window[1];
85 break;
86 }
87 }
88
89 let factor = (t - color1.0) / (color2.0 - color1.0);
90
91 let r = (color1.1.0 as f32 * (1.0 - factor) + color2.1.0 as f32 * factor) as u8;
92 let g = (color1.1.1 as f32 * (1.0 - factor) + color2.1.1 as f32 * factor) as u8;
93 let b = (color1.1.2 as f32 * (1.0 - factor) + color2.1.2 as f32 * factor) as u8;
94
95 Color::from((r, g, b))
96}
97
98pub fn format_time(duration: Duration) -> String {
99 let total_secs = duration.as_secs();
100 let hours = total_secs / 3600;
101 let minutes = (total_secs % 3600) / 60;
102 let seconds = total_secs % 60;
103 let millis = duration.subsec_millis();
104
105 format!("[{:02}:{:02}:{:02}.{:03}]", hours, minutes, seconds, millis)
106 .style(Style::Dim)
107 .style(Style::Italic)
108}
109
110pub fn create_gradient_meter(value: f32, width: usize, peak_pos: Option<usize>) -> String {
111 let meter_width = (value * width as f32 * 2.0).min(width as f32) as usize;
112 let mut meter = String::with_capacity(width * 3);
113
114 for i in 0..width {
115 if i < meter_width {
116 let segment_value = i as f32 / width as f32;
117 let color = interpolate_color(segment_value, 0.0, 1.0);
118 meter.push_str(&"█".color(color));
119 } else if Some(i) == peak_pos {
120 meter.push_str(&"╎".color(WHITE).style(Style::Bold)); } else {
122 meter.push(' ');
123 }
124 }
125 format!("│{}│", meter)
126}
127
128pub fn format_signal_value(value: f32) -> String {
129 format!("{:>10.8}", value)
130 .color(interpolate_color(value, 0.0, 0.1))
131 .style(Style::Bold)
132 .to_string()
133}