From 2f60fb8ba3d236b9a1e1eabfbbdbb0442809e0db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Max=20K=C3=A4nner?= Date: Mon, 13 Jan 2025 00:01:34 +0100 Subject: [PATCH] tidy up --- Cargo.lock | 7 +++++++ Cargo.toml | 6 +++++- src/main.rs | 50 ++++++++++++++++++++++++++++---------------------- 3 files changed, 40 insertions(+), 23 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8ed0c93..95ef137 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,6 +101,7 @@ dependencies = [ name = "audio-reactive-source" version = "0.1.0" dependencies = [ + "az", "bytemuck", "color-eyre", "cpal", @@ -116,6 +117,12 @@ version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + [[package]] name = "backtrace" version = "0.3.71" diff --git a/Cargo.toml b/Cargo.toml index 7045454..ee23016 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,5 +16,9 @@ cpal = { version = "0.15", features = ["jack"] } realfft = "3.4" bytemuck = { version = "1.21", features = ["derive"] } +az = "1.2" -textplots = "0.8" +textplots = { version = "0.8", optional = true } + +[features] +plot = ["textplots"] diff --git a/src/main.rs b/src/main.rs index 2e3d627..feae976 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ -use std::{cmp::Ordering, net::UdpSocket, sync::mpsc, thread::sleep, time::Duration}; +use std::{cmp::Ordering, net::UdpSocket, sync::mpsc}; +use az::{Az, SaturatingAs}; use bytemuck::{Pod, Zeroable}; use color_eyre::eyre::{bail, Result}; use cpal::{ @@ -8,10 +9,11 @@ use cpal::{ }; use log::{debug, error, info, trace}; use realfft::{ - num_complex::{Complex, Complex32, ComplexFloat}, + num_complex::{Complex32, ComplexFloat}, RealFftPlanner, RealToComplex, }; -use textplots::{Chart, LabelBuilder, LabelFormat, Plot, Shape}; +#[cfg(feature = "plot")] +use textplots::{Chart, Plot, Shape}; fn main() -> color_eyre::Result<()> { env_logger::init(); @@ -87,15 +89,18 @@ fn main() -> color_eyre::Result<()> { error!("Unable to send packet over multicast: {e}"); } - // let plot_data: Vec<_> = data - // .fft_result - // .iter() - // .enumerate() - // .map(|(i, datum)| (i as f32, f32::from(*datum))) - // .collect(); - // Chart::new_with_y_range(250, 50, 0.0, 16.0, 0.0, 255.0) - // .lineplot(&Shape::Bars(&plot_data[..])) - // .display(); + #[cfg(feature = "plot")] + { + let plot_data: Vec<_> = data + .fft_result + .iter() + .enumerate() + .map(|(i, datum)| (i.az(), f32::from(*datum))) + .collect(); + Chart::new_with_y_range(250, 50, 0.0, 16.0, 0.0, 255.0) + .lineplot(&Shape::Bars(&plot_data[..])) + .display(); + } let magnitude = data.fft_magnitude; let sample_raw = data.sample_raw; let sample_smth = data.sample_smth; @@ -113,9 +118,9 @@ struct AudioSyncPacket { header: [u8; 6], /// Optional sound pressure as fixed point (8bit integer, 8bit fractional) preasure: [u8; 2], - /// either "sample_raw" or "raw_sample_agc" depending on sound_agc setting + /// either `sample_raw` or `raw_sample_agc` depending on `sound_agc` setting sample_raw: f32, - /// either "sample_avg" or "sample_agc" depending on sound_agc setting + /// either `sample_avg` or `sample_agc` depending on `sound_agc` setting sample_smth: f32, /// 0 = no peak, >= 1 = peak detected, In future, this will also provide peak Magnitude sample_peak: u8, @@ -168,14 +173,14 @@ impl AudioSyncPacket { let abs_data: Vec<_> = data.iter().map(|sample| sample.abs()).collect(); // calculate peak (raw level and peak level) - let raw_level = abs_data.iter().sum::() / data.len() as f32 * 256.0; + let raw_level = abs_data.iter().sum::() / data.len().az::() * 512.0; let peak_level = abs_data .iter() .max_by(|lhs, rhs| lhs.partial_cmp(rhs).unwrap_or(Ordering::Equal)) .copied() .unwrap_or(0.0); // scale peak level to u8 - let peak_level = (peak_level * f32::from(u8::MAX)) as u8; + let peak_level = (peak_level * f32::from(u8::MAX)).saturating_as(); // calculate fft fft_input[..].copy_from_slice(data); @@ -183,13 +188,14 @@ impl AudioSyncPacket { let real_fft_output: Vec<_> = fft_output.iter().map(|value| value.abs()).collect(); // find dominant frequency - let (peak_index, peak) = real_fft_output + let peak_index = real_fft_output .iter() .enumerate() .max_by(|(_, lhs), (_, rhs)| lhs.partial_cmp(rhs).unwrap_or(Ordering::Equal)) - .map(|(i, value)| (i, *value)) + .map(|(i, _)| i) .unwrap_or_default(); - let peak_frequency = (peak_index * sample_rate as usize) as f32 / data.len() as f32; + let peak_frequency = + (peak_index * sample_rate.az::()).az::() / data.len().az::(); // select 16 [frequency bins](https://github.com/MoonModules/WLED/blob/fc173b3bc00694e59b653ca230133052b5476c05/usermods/audioreactive/audio_reactive.h#L733-L760) let bins = [ @@ -197,12 +203,12 @@ impl AudioSyncPacket { ] .map(|frequency| frequency * data.len() / sample_rate as usize); let mut fft_values = [0.0; 16]; - fft_values[0] = real_fft_output[1..=bins[0]].iter().sum::() / (bins[0]) as f32; + fft_values[0] = real_fft_output[1..=bins[0]].iter().sum::() / (bins[0]).az::(); for i in 1..fft_values.len() { fft_values[i] = real_fft_output[bins[i - 1] + 1..=bins[i]] .iter() .sum::() - / (bins[i] - bins[i - 1] + 1) as f32; + / (bins[i] - bins[i - 1] + 1).az::(); } fft_values[14] *= 0.88; fft_values[15] *= 0.7; @@ -218,7 +224,7 @@ impl AudioSyncPacket { fft_max = 1.0; } for i in 0..fft_values.len() { - fft_values_u8[i] = (fft_values[i] * f32::from(u8::MAX) / fft_max) as u8; + fft_values_u8[i] = (fft_values[i] * f32::from(u8::MAX) / fft_max).saturating_as(); } // calculate fft magnitude sum