tidy up
All checks were successful
Build legacy Nix package on Ubuntu / build (push) Successful in 4m37s

This commit is contained in:
Max Känner 2025-01-13 00:01:34 +01:00
parent b353c943af
commit 2f60fb8ba3
3 changed files with 40 additions and 23 deletions

7
Cargo.lock generated
View File

@ -101,6 +101,7 @@ dependencies = [
name = "audio-reactive-source" name = "audio-reactive-source"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"az",
"bytemuck", "bytemuck",
"color-eyre", "color-eyre",
"cpal", "cpal",
@ -116,6 +117,12 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]]
name = "az"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
[[package]] [[package]]
name = "backtrace" name = "backtrace"
version = "0.3.71" version = "0.3.71"

View File

@ -16,5 +16,9 @@ cpal = { version = "0.15", features = ["jack"] }
realfft = "3.4" realfft = "3.4"
bytemuck = { version = "1.21", features = ["derive"] } bytemuck = { version = "1.21", features = ["derive"] }
az = "1.2"
textplots = "0.8" textplots = { version = "0.8", optional = true }
[features]
plot = ["textplots"]

View File

@ -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 bytemuck::{Pod, Zeroable};
use color_eyre::eyre::{bail, Result}; use color_eyre::eyre::{bail, Result};
use cpal::{ use cpal::{
@ -8,10 +9,11 @@ use cpal::{
}; };
use log::{debug, error, info, trace}; use log::{debug, error, info, trace};
use realfft::{ use realfft::{
num_complex::{Complex, Complex32, ComplexFloat}, num_complex::{Complex32, ComplexFloat},
RealFftPlanner, RealToComplex, RealFftPlanner, RealToComplex,
}; };
use textplots::{Chart, LabelBuilder, LabelFormat, Plot, Shape}; #[cfg(feature = "plot")]
use textplots::{Chart, Plot, Shape};
fn main() -> color_eyre::Result<()> { fn main() -> color_eyre::Result<()> {
env_logger::init(); env_logger::init();
@ -87,15 +89,18 @@ fn main() -> color_eyre::Result<()> {
error!("Unable to send packet over multicast: {e}"); error!("Unable to send packet over multicast: {e}");
} }
// let plot_data: Vec<_> = data #[cfg(feature = "plot")]
// .fft_result {
// .iter() let plot_data: Vec<_> = data
// .enumerate() .fft_result
// .map(|(i, datum)| (i as f32, f32::from(*datum))) .iter()
// .collect(); .enumerate()
// Chart::new_with_y_range(250, 50, 0.0, 16.0, 0.0, 255.0) .map(|(i, datum)| (i.az(), f32::from(*datum)))
// .lineplot(&Shape::Bars(&plot_data[..])) .collect();
// .display(); 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 magnitude = data.fft_magnitude;
let sample_raw = data.sample_raw; let sample_raw = data.sample_raw;
let sample_smth = data.sample_smth; let sample_smth = data.sample_smth;
@ -113,9 +118,9 @@ struct AudioSyncPacket {
header: [u8; 6], header: [u8; 6],
/// Optional sound pressure as fixed point (8bit integer, 8bit fractional) /// Optional sound pressure as fixed point (8bit integer, 8bit fractional)
preasure: [u8; 2], 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, 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, sample_smth: f32,
/// 0 = no peak, >= 1 = peak detected, In future, this will also provide peak Magnitude /// 0 = no peak, >= 1 = peak detected, In future, this will also provide peak Magnitude
sample_peak: u8, sample_peak: u8,
@ -168,14 +173,14 @@ impl AudioSyncPacket {
let abs_data: Vec<_> = data.iter().map(|sample| sample.abs()).collect(); let abs_data: Vec<_> = data.iter().map(|sample| sample.abs()).collect();
// calculate peak (raw level and peak level) // calculate peak (raw level and peak level)
let raw_level = abs_data.iter().sum::<f32>() / data.len() as f32 * 256.0; let raw_level = abs_data.iter().sum::<f32>() / data.len().az::<f32>() * 512.0;
let peak_level = abs_data let peak_level = abs_data
.iter() .iter()
.max_by(|lhs, rhs| lhs.partial_cmp(rhs).unwrap_or(Ordering::Equal)) .max_by(|lhs, rhs| lhs.partial_cmp(rhs).unwrap_or(Ordering::Equal))
.copied() .copied()
.unwrap_or(0.0); .unwrap_or(0.0);
// scale peak level to u8 // 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 // calculate fft
fft_input[..].copy_from_slice(data); 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(); let real_fft_output: Vec<_> = fft_output.iter().map(|value| value.abs()).collect();
// find dominant frequency // find dominant frequency
let (peak_index, peak) = real_fft_output let peak_index = real_fft_output
.iter() .iter()
.enumerate() .enumerate()
.max_by(|(_, lhs), (_, rhs)| lhs.partial_cmp(rhs).unwrap_or(Ordering::Equal)) .max_by(|(_, lhs), (_, rhs)| lhs.partial_cmp(rhs).unwrap_or(Ordering::Equal))
.map(|(i, value)| (i, *value)) .map(|(i, _)| i)
.unwrap_or_default(); .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::<usize>()).az::<f32>() / data.len().az::<f32>();
// select 16 [frequency bins](https://github.com/MoonModules/WLED/blob/fc173b3bc00694e59b653ca230133052b5476c05/usermods/audioreactive/audio_reactive.h#L733-L760) // select 16 [frequency bins](https://github.com/MoonModules/WLED/blob/fc173b3bc00694e59b653ca230133052b5476c05/usermods/audioreactive/audio_reactive.h#L733-L760)
let bins = [ let bins = [
@ -197,12 +203,12 @@ impl AudioSyncPacket {
] ]
.map(|frequency| frequency * data.len() / sample_rate as usize); .map(|frequency| frequency * data.len() / sample_rate as usize);
let mut fft_values = [0.0; 16]; let mut fft_values = [0.0; 16];
fft_values[0] = real_fft_output[1..=bins[0]].iter().sum::<f32>() / (bins[0]) as f32; fft_values[0] = real_fft_output[1..=bins[0]].iter().sum::<f32>() / (bins[0]).az::<f32>();
for i in 1..fft_values.len() { for i in 1..fft_values.len() {
fft_values[i] = real_fft_output[bins[i - 1] + 1..=bins[i]] fft_values[i] = real_fft_output[bins[i - 1] + 1..=bins[i]]
.iter() .iter()
.sum::<f32>() .sum::<f32>()
/ (bins[i] - bins[i - 1] + 1) as f32; / (bins[i] - bins[i - 1] + 1).az::<f32>();
} }
fft_values[14] *= 0.88; fft_values[14] *= 0.88;
fft_values[15] *= 0.7; fft_values[15] *= 0.7;
@ -218,7 +224,7 @@ impl AudioSyncPacket {
fft_max = 1.0; fft_max = 1.0;
} }
for i in 0..fft_values.len() { 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 // calculate fft magnitude sum