diff --git a/src/args.rs b/src/args.rs index 37eea1d..9c5e056 100644 --- a/src/args.rs +++ b/src/args.rs @@ -15,8 +15,8 @@ pub struct CLIArgs { fft_fps: u32, /// Smoothing factor for spatial interpolation between bars - #[clap(long = "bar-smoothness", default_value_t = 1)] - bar_smoothness: u32, + #[clap(long = "smoothness", default_value_t = 1)] + smoothness: u32, /// Number of individual frequencies detected by the FFT #[arg(long = "freq-resolution", default_value_t = 90)] @@ -30,10 +30,6 @@ pub struct CLIArgs { #[arg(long = "max-freq", default_value_t = 5000.0)] max_freq: f32, - /// Size of averaging window (larger = less movement) - #[arg(long = "averaging-window", default_value_t = 1)] - averaging_window: u32, - /// Window width #[arg(long = "width", default_value_t = 1000.0)] window_width: f32, @@ -81,10 +77,9 @@ pub fn cli_args_to_fft_args(cli_args: CLIArgs) -> FFTArgs { std::process::exit(1); } - bar_smoothness_constraint(cli_args.bar_smoothness); + bar_smoothness_constraint(cli_args.smoothness); fft_fps_constraint(cli_args.fft_fps); freq_resolution_constraint(cli_args.freq_resolution); - averaging_window_constraint(cli_args.averaging_window); FFTArgs { file_path: Path::new(&cli_args.file_path).to_path_buf(), @@ -95,12 +90,11 @@ pub fn cli_args_to_fft_args(cli_args: CLIArgs) -> FFTArgs { text_color: Color::hex(cli_args.text_color).unwrap(), font_size: cli_args.font_size as i32, background_color: Color::hex(cli_args.background_color).unwrap(), - bar_smoothness: cli_args.bar_smoothness, + smoothness: cli_args.smoothness, fft_fps: cli_args.fft_fps, freq_resolution: cli_args.freq_resolution, window_height: cli_args.window_height, window_width: cli_args.window_width, - averaging_window: cli_args.averaging_window, min_freq: cli_args.min_freq, max_freq: cli_args.max_freq, display_gui: cli_args.display_gui @@ -113,7 +107,7 @@ pub fn parse_cli_args() -> FFTArgs { // Value constraints pub fn bar_smoothness_constraint(v: u32) { if v > 3 { - println!("bar-smoothness must be between 0 and 3 inclusive."); + println!("smoothness must be between 0 and 3 inclusive."); std::process::exit(1); } } @@ -131,10 +125,3 @@ fn freq_resolution_constraint(v: u32) { std::process::exit(1); } } - -fn averaging_window_constraint(v: u32) { - if v < 1 || v > 5 { - println!("averaging-window must be between 1 and 5 inclusive."); - std::process::exit(1); - } -} diff --git a/src/fft.rs b/src/fft.rs index dc7b4ad..9a0853d 100644 --- a/src/fft.rs +++ b/src/fft.rs @@ -40,7 +40,6 @@ pub fn space_interpolate(v: &mut Vec, num_new_frames: u32) { } } -#[allow(dead_code)] pub fn smooth_fft(mut fft: FFT, alpha: u32) -> FFT { let mut new_fft = Vec::new(); for i in (alpha as usize)..(fft.num_frames - alpha as usize) { @@ -58,7 +57,7 @@ pub fn smooth_fft(mut fft: FFT, alpha: u32) -> FFT { fft } -pub fn normalize_fft(mut fft: FFT, bounds: &[f32], scaling_factor: &[f32]) -> FFT { +pub fn intensity_normalize_fft(mut fft: FFT, bounds: &[f32], scaling_factor: &[f32]) -> FFT { let min_max_scale = fft.max - fft.min; let rescale = |mut x: Vec| -> Vec { for i in x.iter_mut() { @@ -76,6 +75,20 @@ pub fn normalize_fft(mut fft: FFT, bounds: &[f32], scaling_factor: &[f32]) -> FF fft } +pub fn frequency_normalize_fft(mut fft: FFT, scaling_factor: &[f32]) -> FFT { + let n_freq_buckets = scaling_factor.len(); + let n_bars = fft.fft[0].len(); + let bars_per_bucket = n_bars / n_freq_buckets; + let rescale = |mut x: Vec| -> Vec { + for (i, v) in x.iter_mut().enumerate() { + *v *= scaling_factor[(i / bars_per_bucket).min(n_freq_buckets - 1)]; + } + x + }; + fft.fft = fft.fft.into_par_iter().map(|x| rescale(x)).collect(); + fft +} + #[allow(dead_code)] pub fn write_fft_to_binary_file(filepath: &PathBuf, fft: &FFT) -> io::Result<()> { let mut file = File::create(filepath)?; diff --git a/src/main.rs b/src/main.rs index 48370cc..b84b593 100644 --- a/src/main.rs +++ b/src/main.rs @@ -30,7 +30,7 @@ use std::io::BufReader; use std::path::PathBuf; use std::time::Duration; -// TODO : Add to brew, and whatever linux/windows uses +// TODO : Add to other package managers // TODO : Add yaml config file for changing default settings // TODO : Add a button to gui to write current state to config file // TODO : Add a button to gui to reset to default @@ -38,7 +38,9 @@ use std::time::Duration; // Constants const RENDERING_FPS: u32 = 60; const RESCALING_THRESHOLDS: &[f32] = &[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]; -const RESCALING_FACTOR: &[f32] = &[0.4, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6, 0.5]; +const INTENSITY_RESCALING: &[f32] = &[0.4, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.6, 0.5]; +const FREQ_RESCALING: &[f32] = &[0.9, 1.2, 1.2, 1.2, 1.0]; +const AVERAGING_WINDOW: u32 = 1; const MIN_BAR_HEIGHT: f32 = 0.001; const MAX_BAR_HEIGHT: f32 = 0.45; @@ -54,11 +56,10 @@ struct FFTArgs { font_size: i32, background_color: Color, fft_fps: u32, - bar_smoothness: u32, + smoothness: u32, freq_resolution: u32, window_width: f32, window_height: f32, - averaging_window: u32, min_freq: f32, max_freq: f32, display_gui: bool, @@ -84,11 +85,13 @@ fn compute_and_preprocess_fft(fp: &PathBuf, args: &FFTArgs) -> Vec> { args.max_freq, ); - fft = smooth_fft(fft, args.averaging_window); - fft = normalize_fft(fft, RESCALING_THRESHOLDS, RESCALING_FACTOR); + fft = smooth_fft(fft, AVERAGING_WINDOW); + fft = intensity_normalize_fft(fft, RESCALING_THRESHOLDS, INTENSITY_RESCALING); + fft = frequency_normalize_fft(fft, FREQ_RESCALING); let mut fft_vec = fft.fft; + // Reverses bar order and prepends for c in fft_vec.iter_mut() { let mut reversed = c.clone(); reversed.reverse(); @@ -98,7 +101,7 @@ fn compute_and_preprocess_fft(fp: &PathBuf, args: &FFTArgs) -> Vec> { fft_vec .par_iter_mut() - .for_each(|x| space_interpolate(x, args.bar_smoothness)); + .for_each(|x| space_interpolate(x, args.smoothness)); fft_vec }