1use anyhow::{Context, Result};
2use ffmpeg_sidecar::{
3 command::FfmpegCommand,
4 event::{FfmpegEvent, LogLevel},
5};
6use std::{cmp::max, iter::repeat};
7
8pub fn main() -> Result<()> {
11 if cfg!(not(windows)) {
12 eprintln!("Note: Methods for capturing audio are platform-specific and this demo is intended for Windows.");
13 eprintln!("On Linux or Mac, you need to switch from the `dshow` format to a different one supported on your platform.");
14 eprintln!("Make sure to also include format-specific arguments such as `-audio_buffer_size`.");
15 eprintln!("Pull requests are welcome to make this demo cross-platform!");
16 }
17
18 let audio_device = FfmpegCommand::new()
23 .hide_banner()
24 .args(&["-list_devices", "true"])
25 .format("dshow")
26 .input("dummy")
27 .spawn()?
28 .iter()?
29 .into_ffmpeg_stderr()
30 .find(|line| line.contains("(audio)"))
31 .map(|line| line.split('\"').nth(1).map(|s| s.to_string()))
32 .context("No audio device found")?
33 .context("Failed to parse audio device")?;
34
35 println!("Listening to audio device: {}", audio_device);
36
37 let iter = FfmpegCommand::new()
42 .format("dshow")
43 .args("-audio_buffer_size 50".split(' ')) .input(format!("audio={audio_device}"))
45 .args("-af ebur128=metadata=1,ametadata=print".split(' '))
46 .format("null")
47 .output("-")
48 .spawn()?
49 .iter()?;
50
51 let mut first_volume_event = true;
59 for event in iter {
60 match event {
61 FfmpegEvent::Error(e) | FfmpegEvent::Log(LogLevel::Error | LogLevel::Fatal, e) => {
62 eprintln!("{e}");
63 }
64 FfmpegEvent::Log(LogLevel::Info, msg) if msg.contains("lavfi.r128.M=") => {
65 if let Some(volume) = msg.split("lavfi.r128.M=").last() {
66 let volume_f32 = volume.parse::<f32>().context("Failed to parse volume")?;
71 let volume_normalized: usize = max(((volume_f32 / 5.0).round() as i32) + 14, 0) as usize;
72 let volume_percent = ((volume_normalized as f32 / 14.0) * 100.0).round();
73
74 if !first_volume_event {
76 print!("\x1b[1A\x1b[2K");
77 } else {
78 first_volume_event = false;
79 }
80
81 let time = std::time::SystemTime::now()
83 .duration_since(std::time::UNIX_EPOCH)
84 .unwrap()
85 .as_secs();
86 let recording_indicator = if time % 2 == 0 { "🔴" } else { " " };
87
88 println!(
89 "{} {} {}%",
90 recording_indicator,
91 repeat('â–ˆ').take(volume_normalized).collect::<String>(),
92 volume_percent
93 );
94 }
95 }
96 _ => {}
97 }
98 }
99
100 Ok(())
101}