From cd6b22623878453ef3648db8a03d04c0b325d1e2 Mon Sep 17 00:00:00 2001 From: gursi26 Date: Wed, 13 Mar 2024 23:01:31 -0400 Subject: [PATCH 1/5] possibly fixed out of sync frames --- src/main.rs | 19 +++++++++++++----- src/systems/egui.rs | 14 +++++++------- src/systems/mod.rs | 1 + src/systems/update_fft.rs | 16 ++++----------- src/systems/update_frame_counters.rs | 29 ++++++++++++++++++++++++++++ 5 files changed, 55 insertions(+), 24 deletions(-) create mode 100644 src/systems/update_frame_counters.rs diff --git a/src/main.rs b/src/main.rs index 1117500..499de24 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,10 +8,13 @@ mod config; use args::*; use fft::*; use config::*; +use stopwatch::Stopwatch; use systems::get_keyboard_input::*; use systems::egui::*; use systems::startup::*; use systems::update_fft::*; +use systems::update_frame_counters; +use systems::update_frame_counters::*; use systems::update_view_settings::*; use bevy::render::mesh::VertexAttributeValues; @@ -22,7 +25,6 @@ use bevy::{ prelude::*, sprite::{MaterialMesh2dBundle, Mesh2dHandle}, }; -use stopwatch::{Stopwatch}; use bevy_egui::{egui, EguiContexts, EguiPlugin}; use clap::{ArgAction, Parser}; use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; @@ -39,11 +41,13 @@ use std::time::Duration; // Constants const RENDERING_FPS: u32 = 60; +const TIME_BETWEEN_FRAMES: f64 = 1.0 / RENDERING_FPS as f64; const RESCALING_THRESHOLDS: &[f32] = &[0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]; 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 FFT_FPS: u32 = 12; +const TIME_BETWEEN_FFT_FRAMES: f64 = 1.0 / FFT_FPS as f64; const MIN_BAR_HEIGHT: f32 = 0.001; const MAX_BAR_HEIGHT: f32 = 0.45; @@ -78,7 +82,9 @@ struct AppState { despawn_handles: Vec, total_frame_counter: usize, fft_frame_counter: usize, - stopwatch: Stopwatch, + song_stopwatch: Stopwatch, + update_fft_counter: bool, + display_str_stopwatch: Stopwatch, display_str: String, } @@ -160,13 +166,14 @@ fn main() { // Insert systems .add_systems(Startup, startup) - .add_systems(Update, ui_example_system) .add_systems( Update, - (update_fft.run_if(bevy::time::common_conditions::on_timer( + (update_frame_counters.run_if(bevy::time::common_conditions::on_timer( Duration::from_secs_f64(1.0 / RENDERING_FPS as f64), )),), ) + .add_systems(Update, update_fft) + .add_systems(Update, ui_example_system) .add_systems(Update, get_keyboard_input) .add_systems(Update, update_view_settings); @@ -185,8 +192,10 @@ fn main() { despawn_handles: Vec::new(), fft_frame_counter: 0, total_frame_counter: 0, - stopwatch: Stopwatch::new(), + song_stopwatch: Stopwatch::start_new(), + display_str_stopwatch: Stopwatch::new(), display_str: String::new(), + update_fft_counter: false, }); app.run(); diff --git a/src/systems/egui.rs b/src/systems/egui.rs index cb07eb0..3331e7e 100644 --- a/src/systems/egui.rs +++ b/src/systems/egui.rs @@ -68,28 +68,28 @@ pub fn ui_example_system( if ui.button("Save").clicked() { write_fftargs_to_config(&args); app_state.display_str = format!("Saved to {:?}", config_path()); - app_state.stopwatch.start(); + app_state.display_str_stopwatch.start(); } if ui.button("Reset").clicked() { *args = parse_cli_args(); app_state.display_str = String::from("Reset to saved settings."); - app_state.stopwatch.start(); + app_state.display_str_stopwatch.start(); args.display_gui = true; } if ui.button("Reset to default").clicked() { *args = cli_args_to_fft_args(crate::args::CLIArgs::parse(), true); app_state.display_str = String::from("Reset to default settings."); - app_state.stopwatch.start(); + app_state.display_str_stopwatch.start(); args.display_gui = true; } }); - if app_state.stopwatch.is_running() { + if app_state.display_str_stopwatch.is_running() { ui.label(&app_state.display_str); } - if app_state.stopwatch.elapsed().as_secs() > 3 { - app_state.stopwatch.stop(); - app_state.stopwatch.reset(); + if app_state.display_str_stopwatch.elapsed().as_secs() > 3 { + app_state.display_str_stopwatch.stop(); + app_state.display_str_stopwatch.reset(); } }); } diff --git a/src/systems/mod.rs b/src/systems/mod.rs index 69fbc92..b5cff5f 100644 --- a/src/systems/mod.rs +++ b/src/systems/mod.rs @@ -3,3 +3,4 @@ pub mod egui; pub mod startup; pub mod update_fft; pub mod update_view_settings; +pub mod update_frame_counters; diff --git a/src/systems/update_fft.rs b/src/systems/update_fft.rs index 5e570f2..feb4b1a 100644 --- a/src/systems/update_fft.rs +++ b/src/systems/update_fft.rs @@ -29,22 +29,22 @@ pub fn update_fft( mut text_query: Query<&mut Text>, ) { let h = window.single_mut().height(); - let mut update_i = false; + app_state.update_fft_counter = false; let interval = RENDERING_FPS / args.fft_fps; // Get the current frame (either from fft or interpolation) - let clamped_frame_counter = app_state.fft_frame_counter.clamp(0, app_state.fft.len() - 1); + let clamped_frame_counter = app_state.fft_frame_counter.clamp(0, app_state.fft.len() - 2); let curr_fft = match app_state.total_frame_counter as u32 % interval { 0 => { if app_state.fft_frame_counter > app_state.fft.len() { std::process::exit(0); } - update_i = true; + app_state.update_fft_counter = true; app_state.fft[clamped_frame_counter].clone() } rem => time_interpolate( - &(app_state.fft[clamped_frame_counter - 1]), &(app_state.fft[clamped_frame_counter]), + &(app_state.fft[clamped_frame_counter + 1]), rem as f32 / interval as f32, ), }; @@ -83,12 +83,4 @@ pub fn update_fft( _ => {} } } - - // Moves real frame and interpolated frame counters - if !args.paused { - if update_i { - app_state.fft_frame_counter += 1; - } - app_state.total_frame_counter += 1; - } } diff --git a/src/systems/update_frame_counters.rs b/src/systems/update_frame_counters.rs new file mode 100644 index 0000000..b97f213 --- /dev/null +++ b/src/systems/update_frame_counters.rs @@ -0,0 +1,29 @@ +use crate::fft::time_interpolate; +use crate::*; +use bevy::render::mesh::VertexAttributeValues; +use bevy::sprite::Anchor; +use bevy::{ + app::AppExit, + prelude::*, + sprite::{MaterialMesh2dBundle, Mesh2dHandle}, +}; +use bevy_egui::egui::{Align2, Color32, Stroke}; +use bevy_egui::{egui, EguiContexts, EguiPlugin}; +use clap::{ArgAction, Parser}; +use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; +use rodio::{source::Source, Decoder, OutputStream}; +use std::ffi::OsString; +use std::fs::File; +use std::io::BufReader; +use std::path::PathBuf; +use std::time::Duration; + + +pub fn update_frame_counters( + mut app_state: ResMut, + mut args: ResMut, +) { + let elapsed_time = app_state.song_stopwatch.elapsed().as_secs_f64(); + app_state.fft_frame_counter = (elapsed_time / TIME_BETWEEN_FFT_FRAMES) as usize; + app_state.total_frame_counter = (elapsed_time / TIME_BETWEEN_FRAMES) as usize; +} From d353a401eb3a704fab34e4b63f8a4e6e4d991e4f Mon Sep 17 00:00:00 2001 From: gursi26 Date: Wed, 13 Mar 2024 23:13:25 -0400 Subject: [PATCH 2/5] quick fix --- src/main.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main.rs b/src/main.rs index 499de24..f7d75bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -166,12 +166,7 @@ fn main() { // Insert systems .add_systems(Startup, startup) - .add_systems( - Update, - (update_frame_counters.run_if(bevy::time::common_conditions::on_timer( - Duration::from_secs_f64(1.0 / RENDERING_FPS as f64), - )),), - ) + .add_systems(Update, update_frame_counters) .add_systems(Update, update_fft) .add_systems(Update, ui_example_system) .add_systems(Update, get_keyboard_input) From 53ee45e295d94f34c855ace1c867cd4cd155b280 Mon Sep 17 00:00:00 2001 From: gursi26 Date: Fri, 15 Mar 2024 01:28:35 -0400 Subject: [PATCH 3/5] fixed indexing bug in update_fft --- src/config.rs | 206 ++++++++++++++++++++++++++++++-------- src/main.rs | 13 +-- src/systems/update_fft.rs | 16 +-- 3 files changed, 179 insertions(+), 56 deletions(-) diff --git a/src/config.rs b/src/config.rs index daf4798..9b89b11 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,10 +1,14 @@ +use bevy::prelude::Color; use dirs::home_dir; -use std::{fs::remove_file, io::{stdout, Read, Write}, path::PathBuf}; use serde::{Deserialize, Serialize}; use serde_yaml::{self}; -use std::fs::{read_to_string, create_dir, create_dir_all, File, OpenOptions}; +use std::fs::{create_dir, create_dir_all, read_to_string, File, OpenOptions}; use std::io::BufWriter; -use bevy::prelude::Color; +use std::{ + fs::remove_file, + io::{stdout, Read, Write}, + path::PathBuf, +}; use crate::{CLIArgs, FFTArgs}; @@ -89,16 +93,37 @@ macro_rules! overwrite_non_default_args { pub fn convert_color_to_hex(c: &Color) -> String { let c_vec = c.rgb_to_vec3(); - let (r, g, b) = ((c_vec.x * 255.0) as u32, (c_vec.y * 255.0) as u32, (c_vec.z * 255.0) as u32); - format!("{}{}{}", format!("{:02X}", r), format!("{:02X}", g), format!("{:02X}", b)) + let (r, g, b) = ( + (c_vec.x * 255.0) as u32, + (c_vec.y * 255.0) as u32, + (c_vec.z * 255.0) as u32, + ); + format!( + "{}{}{}", + format!("{:02X}", r), + format!("{:02X}", g), + format!("{:02X}", b) + ) } pub fn write_fftargs_to_config(args: &FFTArgs) { let mut default_args = ConfigFFTArgs::default(); - overwrite_non_default_args!(&mut default_args.background_color, convert_color_to_hex(&args.background_color)); - overwrite_non_default_args!(&mut default_args.border_color, convert_color_to_hex(&args.border_color)); - overwrite_non_default_args!(&mut default_args.bar_color, convert_color_to_hex(&args.bar_color)); - overwrite_non_default_args!(&mut default_args.text_color, convert_color_to_hex(&args.text_color)); + overwrite_non_default_args!( + &mut default_args.background_color, + convert_color_to_hex(&args.background_color) + ); + overwrite_non_default_args!( + &mut default_args.border_color, + convert_color_to_hex(&args.border_color) + ); + overwrite_non_default_args!( + &mut default_args.bar_color, + convert_color_to_hex(&args.bar_color) + ); + overwrite_non_default_args!( + &mut default_args.text_color, + convert_color_to_hex(&args.text_color) + ); overwrite_non_default_args!(&mut default_args.border_size, args.border_size); overwrite_non_default_args!(&mut default_args.display_track_name, args.track_name); overwrite_non_default_args!(&mut default_args.font_size, args.font_size); @@ -120,11 +145,11 @@ pub fn write_fftargs_to_config(args: &FFTArgs) { .expect("Could not open file."); serde_yaml::to_writer(config_file, &default_args).unwrap(); - let mut cfg_yaml: Vec = read_to_string(&cfg_path) - .unwrap() // panic on possible file-reading errors - .lines() // split the string into an iterator of string slices - .map(String::from) // make each slice into a string - .collect(); // gather them together into a vector + let mut cfg_yaml: Vec = read_to_string(&cfg_path) + .unwrap() // panic on possible file-reading errors + .lines() // split the string into an iterator of string slices + .map(String::from) // make each slice into a string + .collect(); // gather them together into a vector cfg_yaml.retain(|x| x.contains(":")); @@ -132,7 +157,8 @@ pub fn write_fftargs_to_config(args: &FFTArgs) { let f = File::create(&cfg_path).expect("Unable to create file"); let mut f = BufWriter::new(f); - f.write_all(cfg_yaml.join("\n").as_bytes()).expect("Unable to write data"); + f.write_all(cfg_yaml.join("\n").as_bytes()) + .expect("Unable to write data"); } pub fn reset_config_file() { @@ -152,19 +178,67 @@ pub fn reset_config_file() { pub fn merge_config_with_cli_args(args: &mut CLIArgs, use_default: bool) { let default_user_config = ConfigFFTArgs::default(); if !config_exists() { - update_cli_arg!(&mut args.background_color, None::, default_user_config.background_color); - update_cli_arg!(&mut args.bar_color, None::, default_user_config.bar_color); - update_cli_arg!(&mut args.border_color, None::, default_user_config.border_color); - update_cli_arg!(&mut args.border_size, None::, default_user_config.border_size); - update_cli_arg!(&mut args.font_size, None::, default_user_config.font_size); - update_cli_arg!(&mut args.freq_resolution, None::, default_user_config.freq_resolution); - update_cli_arg!(&mut args.max_freq, None::, default_user_config.max_freq); - update_cli_arg!(&mut args.min_freq, None::, default_user_config.min_freq); - update_cli_arg!(&mut args.smoothness, None::, default_user_config.smoothness); - update_cli_arg!(&mut args.text_color, None::, default_user_config.text_color); + update_cli_arg!( + &mut args.background_color, + None::, + default_user_config.background_color + ); + update_cli_arg!( + &mut args.bar_color, + None::, + default_user_config.bar_color + ); + update_cli_arg!( + &mut args.border_color, + None::, + default_user_config.border_color + ); + update_cli_arg!( + &mut args.border_size, + None::, + default_user_config.border_size + ); + update_cli_arg!( + &mut args.font_size, + None::, + default_user_config.font_size + ); + update_cli_arg!( + &mut args.freq_resolution, + None::, + default_user_config.freq_resolution + ); + update_cli_arg!( + &mut args.max_freq, + None::, + default_user_config.max_freq + ); + update_cli_arg!( + &mut args.min_freq, + None::, + default_user_config.min_freq + ); + update_cli_arg!( + &mut args.smoothness, + None::, + default_user_config.smoothness + ); + update_cli_arg!( + &mut args.text_color, + None::, + default_user_config.text_color + ); update_cli_arg!(&mut args.volume, None::, default_user_config.volume); - update_cli_arg!(&mut args.window_width, None::, default_user_config.window_width); - update_cli_arg!(&mut args.window_height, None::, default_user_config.window_height); + update_cli_arg!( + &mut args.window_width, + None::, + default_user_config.window_width + ); + update_cli_arg!( + &mut args.window_height, + None::, + default_user_config.window_height + ); return; } @@ -182,17 +256,69 @@ pub fn merge_config_with_cli_args(args: &mut CLIArgs, use_default: bool) { args.display_gui = Some(x); } - update_cli_arg!(&mut args.background_color, user_config_yaml.background_color, default_user_config.background_color); - update_cli_arg!(&mut args.bar_color, user_config_yaml.bar_color, default_user_config.bar_color); - update_cli_arg!(&mut args.border_color, user_config_yaml.border_color, default_user_config.border_color); - update_cli_arg!(&mut args.border_size, user_config_yaml.border_size, default_user_config.border_size); - update_cli_arg!(&mut args.font_size, user_config_yaml.font_size, default_user_config.font_size); - update_cli_arg!(&mut args.freq_resolution, user_config_yaml.freq_resolution, default_user_config.freq_resolution); - update_cli_arg!(&mut args.max_freq, user_config_yaml.max_freq, default_user_config.max_freq); - update_cli_arg!(&mut args.min_freq, user_config_yaml.min_freq, default_user_config.min_freq); - update_cli_arg!(&mut args.smoothness, user_config_yaml.smoothness, default_user_config.smoothness); - update_cli_arg!(&mut args.text_color, user_config_yaml.text_color, default_user_config.text_color); - update_cli_arg!(&mut args.volume, user_config_yaml.volume, default_user_config.volume); - update_cli_arg!(&mut args.window_width, user_config_yaml.window_width, default_user_config.window_width); - update_cli_arg!(&mut args.window_height, user_config_yaml.window_height, default_user_config.window_height); + update_cli_arg!( + &mut args.background_color, + user_config_yaml.background_color, + default_user_config.background_color + ); + update_cli_arg!( + &mut args.bar_color, + user_config_yaml.bar_color, + default_user_config.bar_color + ); + update_cli_arg!( + &mut args.border_color, + user_config_yaml.border_color, + default_user_config.border_color + ); + update_cli_arg!( + &mut args.border_size, + user_config_yaml.border_size, + default_user_config.border_size + ); + update_cli_arg!( + &mut args.font_size, + user_config_yaml.font_size, + default_user_config.font_size + ); + update_cli_arg!( + &mut args.freq_resolution, + user_config_yaml.freq_resolution, + default_user_config.freq_resolution + ); + update_cli_arg!( + &mut args.max_freq, + user_config_yaml.max_freq, + default_user_config.max_freq + ); + update_cli_arg!( + &mut args.min_freq, + user_config_yaml.min_freq, + default_user_config.min_freq + ); + update_cli_arg!( + &mut args.smoothness, + user_config_yaml.smoothness, + default_user_config.smoothness + ); + update_cli_arg!( + &mut args.text_color, + user_config_yaml.text_color, + default_user_config.text_color + ); + update_cli_arg!( + &mut args.volume, + user_config_yaml.volume, + default_user_config.volume + ); + update_cli_arg!( + &mut args.window_width, + user_config_yaml.window_width, + default_user_config.window_width + ); + update_cli_arg!( + &mut args.window_height, + user_config_yaml.window_height, + default_user_config.window_height + ); } diff --git a/src/main.rs b/src/main.rs index f7d75bd..428de50 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,16 +1,16 @@ #![allow(unused)] mod args; +mod config; mod fft; mod systems; -mod config; use args::*; -use fft::*; use config::*; +use fft::*; use stopwatch::Stopwatch; -use systems::get_keyboard_input::*; use systems::egui::*; +use systems::get_keyboard_input::*; use systems::startup::*; use systems::update_fft::*; use systems::update_frame_counters; @@ -18,13 +18,13 @@ use systems::update_frame_counters::*; use systems::update_view_settings::*; use bevy::render::mesh::VertexAttributeValues; -use bevy_egui::egui::{Align2, Color32, Stroke}; use bevy::sprite::Anchor; use bevy::{ app::AppExit, prelude::*, sprite::{MaterialMesh2dBundle, Mesh2dHandle}, }; +use bevy_egui::egui::{Align2, Color32, Stroke}; use bevy_egui::{egui, EguiContexts, EguiPlugin}; use clap::{ArgAction, Parser}; use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; @@ -88,7 +88,6 @@ struct AppState { display_str: String, } - fn compute_and_preprocess_fft(fp: &PathBuf, args: &FFTArgs) -> Vec> { println!("Computing FFT..."); let mut fft = compute_fft( @@ -135,7 +134,6 @@ fn main() { // Initialize Bevy app let mut binding = App::new(); let app = binding - // Insert plugins .add_plugins( DefaultPlugins.set(WindowPlugin { @@ -159,11 +157,9 @@ fn main() { }), ) .add_plugins(EguiPlugin) - // Insert resources .insert_resource(ClearColor(args.background_color)) .insert_resource(args) - // Insert systems .add_systems(Startup, startup) .add_systems(Update, update_frame_counters) @@ -195,4 +191,3 @@ fn main() { app.run(); } - diff --git a/src/systems/update_fft.rs b/src/systems/update_fft.rs index feb4b1a..a24021b 100644 --- a/src/systems/update_fft.rs +++ b/src/systems/update_fft.rs @@ -33,18 +33,16 @@ pub fn update_fft( let interval = RENDERING_FPS / args.fft_fps; // Get the current frame (either from fft or interpolation) - let clamped_frame_counter = app_state.fft_frame_counter.clamp(0, app_state.fft.len() - 2); let curr_fft = match app_state.total_frame_counter as u32 % interval { 0 => { - if app_state.fft_frame_counter > app_state.fft.len() { + if app_state.fft_frame_counter >= app_state.fft.len() - 1 { std::process::exit(0); } - app_state.update_fft_counter = true; - app_state.fft[clamped_frame_counter].clone() + app_state.fft[app_state.fft_frame_counter].clone() } rem => time_interpolate( - &(app_state.fft[clamped_frame_counter]), - &(app_state.fft[clamped_frame_counter + 1]), + &(app_state.fft[app_state.fft_frame_counter]), + &(app_state.fft[app_state.fft_frame_counter + 1]), rem as f32 / interval as f32, ), }; @@ -70,7 +68,11 @@ pub fn update_fft( _ => {} } - let dims = meshes.get_mut(handle2).unwrap().attribute_mut(Mesh::ATTRIBUTE_POSITION).unwrap(); + let dims = meshes + .get_mut(handle2) + .unwrap() + .attribute_mut(Mesh::ATTRIBUTE_POSITION) + .unwrap(); let bar_value_2 = (new_value.clone() * (h / 2.0) as f32 - args.border_size as f32) .clamp(h * MIN_BAR_HEIGHT, h * MAX_BAR_HEIGHT); match dims { From d12ae9dbd6fbe9331991bcccf112ba3bb7323e6e Mon Sep 17 00:00:00 2001 From: gursi26 Date: Fri, 15 Mar 2024 01:31:12 -0400 Subject: [PATCH 4/5] moved stopwatch definition outside struct constructor --- src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 428de50..d88eaeb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -175,6 +175,7 @@ fn main() { let sink = rodio::Sink::try_new(&stream_handle).unwrap(); sink.set_volume(volume as f32 / 100.0); sink.append(source); + let song_stopwatch = Stopwatch::start_new(); app.insert_resource(AppState { sink, @@ -183,7 +184,7 @@ fn main() { despawn_handles: Vec::new(), fft_frame_counter: 0, total_frame_counter: 0, - song_stopwatch: Stopwatch::start_new(), + song_stopwatch, display_str_stopwatch: Stopwatch::new(), display_str: String::new(), update_fft_counter: false, From 3ef57a0f4d954152e8d62f0cb08aac77794f0d5e Mon Sep 17 00:00:00 2001 From: gursi26 Date: Fri, 15 Mar 2024 02:00:13 -0400 Subject: [PATCH 5/5] restructured AppState resource, replace fft_stopwatch with Instant --- Cargo.toml | 1 - src/args.rs | 10 ++++---- src/main.rs | 36 ++++++++++++++++++---------- src/systems/egui.rs | 25 ++++++++++--------- src/systems/get_keyboard_input.rs | 8 +++---- src/systems/startup.rs | 7 +++--- src/systems/update_fft.rs | 16 ++++++------- src/systems/update_frame_counters.rs | 12 ++++------ src/systems/update_view_settings.rs | 32 +++++++++++++++---------- 9 files changed, 78 insertions(+), 69 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 350a2e4..0e1f40e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,6 @@ serde = { version = "1.0.197", features = ["derive"] } dirs = "5.0.1" serde_yaml = "0.9.32" spectrum-analyzer = "1.5.0" -stopwatch = "0.0.7" [profile.release] strip = true diff --git a/src/args.rs b/src/args.rs index 0551089..3ae140f 100644 --- a/src/args.rs +++ b/src/args.rs @@ -1,7 +1,7 @@ -use std::path::Path; -use bevy::prelude::*; use crate::*; +use bevy::prelude::*; use clap::{ArgAction, Parser}; +use std::path::Path; #[derive(Debug, Parser)] #[clap(author, version, about)] @@ -101,8 +101,6 @@ pub fn cli_args_to_fft_args(mut cli_args: CLIArgs, use_default: bool) -> FFTArgs max_freq: cli_args.max_freq.unwrap(), display_gui: cli_args.display_gui.unwrap(), volume: cli_args.volume.unwrap(), - paused: false, - fft_fps: FFT_FPS } } @@ -110,14 +108,14 @@ pub fn parse_cli_args() -> FFTArgs { cli_args_to_fft_args(args::CLIArgs::parse(), false) } // Value constraints -pub fn bar_smoothness_constraint(v: u32) { +pub fn bar_smoothness_constraint(v: u32) { if v > 3 { println!("smoothness must be between 0 and 3 inclusive."); std::process::exit(1); } } -fn freq_resolution_constraint(v: u32) { +fn freq_resolution_constraint(v: u32) { if v < 10 || v > 300 { println!("freq-resolution must be between 10 and 300 inclusive."); std::process::exit(1); diff --git a/src/main.rs b/src/main.rs index d88eaeb..83dd702 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,6 @@ mod systems; use args::*; use config::*; use fft::*; -use stopwatch::Stopwatch; use systems::egui::*; use systems::get_keyboard_input::*; use systems::startup::*; @@ -34,6 +33,7 @@ use std::fs::File; use std::io::BufReader; use std::path::PathBuf; use std::time::Duration; +use std::time::Instant; // TODO: Add to other package managers // TODO: Remove fft_fps and other deprecated configs from readme @@ -70,29 +70,33 @@ struct FFTArgs { max_freq: f32, display_gui: bool, volume: u32, - paused: bool, - fft_fps: u32, } #[derive(Resource)] struct AppState { sink: rodio::Sink, + display_str: String, + display_start_time: f64, + paused: bool, + fft_fps: u32, + rendering_fps: u32, +} + +#[derive(Resource)] +struct FFTState { fft: Vec>, curr_bars: Vec<(Handle, Handle)>, despawn_handles: Vec, total_frame_counter: usize, fft_frame_counter: usize, - song_stopwatch: Stopwatch, - update_fft_counter: bool, - display_str_stopwatch: Stopwatch, - display_str: String, + fft_timer: Instant, } fn compute_and_preprocess_fft(fp: &PathBuf, args: &FFTArgs) -> Vec> { println!("Computing FFT..."); let mut fft = compute_fft( fp, - args.fft_fps, + FFT_FPS, args.freq_resolution, args.min_freq, args.max_freq, @@ -175,19 +179,25 @@ fn main() { let sink = rodio::Sink::try_new(&stream_handle).unwrap(); sink.set_volume(volume as f32 / 100.0); sink.append(source); - let song_stopwatch = Stopwatch::start_new(); + + // Start stopwatch that keeps fft in sync + let fft_timer = Instant::now(); app.insert_resource(AppState { sink, + display_str: String::new(), + display_start_time: 0.0, + paused: false, + fft_fps: FFT_FPS, + rendering_fps: RENDERING_FPS, + }) + .insert_resource(FFTState { fft: fft_vec, curr_bars: Vec::new(), despawn_handles: Vec::new(), fft_frame_counter: 0, total_frame_counter: 0, - song_stopwatch, - display_str_stopwatch: Stopwatch::new(), - display_str: String::new(), - update_fft_counter: false, + fft_timer, }); app.run(); diff --git a/src/systems/egui.rs b/src/systems/egui.rs index 3331e7e..30e474c 100644 --- a/src/systems/egui.rs +++ b/src/systems/egui.rs @@ -1,12 +1,15 @@ -use crate::{cli_args_to_fft_args, config_path, parse_cli_args, reset_config_file, write_fftargs_to_config, AppState, FFTArgs}; +use crate::{ + cli_args_to_fft_args, config_path, parse_cli_args, reset_config_file, write_fftargs_to_config, + AppState, FFTArgs, FFTState, +}; use bevy::render::mesh::VertexAttributeValues; -use bevy_egui::egui::{Align2, Color32, Stroke}; use bevy::sprite::Anchor; use bevy::{ app::AppExit, prelude::*, sprite::{MaterialMesh2dBundle, Mesh2dHandle}, }; +use bevy_egui::egui::{Align2, Color32, Stroke}; use bevy_egui::{egui, EguiContexts, EguiPlugin}; use clap::{ArgAction, Parser}; use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; @@ -17,10 +20,10 @@ use std::io::BufReader; use std::path::PathBuf; use std::time::Duration; - pub fn ui_example_system( mut contexts: EguiContexts, keyboard_input: Res>, + mut fft_state: ResMut, mut app_state: ResMut, mut args: ResMut, ) { @@ -68,28 +71,25 @@ pub fn ui_example_system( if ui.button("Save").clicked() { write_fftargs_to_config(&args); app_state.display_str = format!("Saved to {:?}", config_path()); - app_state.display_str_stopwatch.start(); + app_state.display_start_time = fft_state.fft_timer.elapsed().as_secs_f64(); } if ui.button("Reset").clicked() { *args = parse_cli_args(); app_state.display_str = String::from("Reset to saved settings."); - app_state.display_str_stopwatch.start(); + app_state.display_start_time = fft_state.fft_timer.elapsed().as_secs_f64(); args.display_gui = true; } if ui.button("Reset to default").clicked() { *args = cli_args_to_fft_args(crate::args::CLIArgs::parse(), true); app_state.display_str = String::from("Reset to default settings."); - app_state.display_str_stopwatch.start(); + app_state.display_start_time = fft_state.fft_timer.elapsed().as_secs_f64(); args.display_gui = true; } }); - if app_state.display_str_stopwatch.is_running() { - ui.label(&app_state.display_str); - } - if app_state.display_str_stopwatch.elapsed().as_secs() > 3 { - app_state.display_str_stopwatch.stop(); - app_state.display_str_stopwatch.reset(); + ui.label(&app_state.display_str); + if fft_state.fft_timer.elapsed().as_secs_f64() - app_state.display_start_time > 5.0 { + app_state.display_str = String::new(); } }); } @@ -117,4 +117,3 @@ fn color_picker_widget(ui: &mut egui::Ui, color: &mut Color) -> egui::Response { ); res } - diff --git a/src/systems/get_keyboard_input.rs b/src/systems/get_keyboard_input.rs index 02b49c4..0f8b28a 100644 --- a/src/systems/get_keyboard_input.rs +++ b/src/systems/get_keyboard_input.rs @@ -1,12 +1,12 @@ -use crate::{AppState, FFTArgs}; +use crate::{AppState, FFTArgs, FFTState}; use bevy::render::mesh::VertexAttributeValues; -use bevy_egui::egui::{Align2, Color32, Stroke}; use bevy::sprite::Anchor; use bevy::{ app::AppExit, prelude::*, sprite::{MaterialMesh2dBundle, Mesh2dHandle}, }; +use bevy_egui::egui::{Align2, Color32, Stroke}; use bevy_egui::{egui, EguiContexts, EguiPlugin}; use clap::{ArgAction, Parser}; use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; @@ -17,10 +17,10 @@ use std::io::BufReader; use std::path::PathBuf; use std::time::Duration; - pub fn get_keyboard_input( keyboard_input: Res>, mut exit: EventWriter, + mut fft_state: ResMut, mut app_state: ResMut, mut args: ResMut, ) { @@ -31,7 +31,7 @@ pub fn get_keyboard_input( args.display_gui = !args.display_gui; } if keyboard_input.just_pressed(KeyCode::Space) { - args.paused = !args.paused; + app_state.paused = !app_state.paused; if app_state.sink.is_paused() { app_state.sink.play(); } else { diff --git a/src/systems/startup.rs b/src/systems/startup.rs index 28d76db..9f8513f 100644 --- a/src/systems/startup.rs +++ b/src/systems/startup.rs @@ -1,12 +1,12 @@ -use crate::{AppState, FFTArgs}; +use crate::{FFTArgs, FFTState}; use bevy::render::mesh::VertexAttributeValues; -use bevy_egui::egui::{Align2, Color32, Stroke}; use bevy::sprite::Anchor; use bevy::{ app::AppExit, prelude::*, sprite::{MaterialMesh2dBundle, Mesh2dHandle}, }; +use bevy_egui::egui::{Align2, Color32, Stroke}; use bevy_egui::{egui, EguiContexts, EguiPlugin}; use clap::{ArgAction, Parser}; use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; @@ -17,7 +17,6 @@ use std::io::BufReader; use std::path::PathBuf; use std::time::Duration; - fn spawn_bars( num_bars: u32, w: f32, @@ -75,7 +74,7 @@ pub fn startup( mut window: Query<&mut Window>, mut meshes: ResMut>, mut materials: ResMut>, - mut fft_queue: ResMut, + mut fft_queue: ResMut, clear_color: Res, args: Res, ) { diff --git a/src/systems/update_fft.rs b/src/systems/update_fft.rs index a24021b..c433d29 100644 --- a/src/systems/update_fft.rs +++ b/src/systems/update_fft.rs @@ -23,32 +23,32 @@ pub fn update_fft( mut window: Query<&mut Window>, mut meshes: ResMut>, mut materials: ResMut>, + mut fft_state: ResMut, mut app_state: ResMut, mut args: ResMut, mut clear_color: ResMut, mut text_query: Query<&mut Text>, ) { let h = window.single_mut().height(); - app_state.update_fft_counter = false; - let interval = RENDERING_FPS / args.fft_fps; + let interval = app_state.rendering_fps / app_state.fft_fps; // Get the current frame (either from fft or interpolation) - let curr_fft = match app_state.total_frame_counter as u32 % interval { + let curr_fft = match fft_state.total_frame_counter as u32 % interval { 0 => { - if app_state.fft_frame_counter >= app_state.fft.len() - 1 { + if fft_state.fft_frame_counter >= fft_state.fft.len() - 1 { std::process::exit(0); } - app_state.fft[app_state.fft_frame_counter].clone() + fft_state.fft[fft_state.fft_frame_counter].clone() } rem => time_interpolate( - &(app_state.fft[app_state.fft_frame_counter]), - &(app_state.fft[app_state.fft_frame_counter + 1]), + &(fft_state.fft[fft_state.fft_frame_counter]), + &(fft_state.fft[fft_state.fft_frame_counter + 1]), rem as f32 / interval as f32, ), }; // Iterate through all currently displayed bars to change values - for (handle, new_value) in app_state.curr_bars.chunks(2).zip(curr_fft.iter()) { + for (handle, new_value) in fft_state.curr_bars.chunks(2).zip(curr_fft.iter()) { let (handle1, handle2) = (handle[0].0.clone_weak(), handle[1].0.clone_weak()); let dims = meshes diff --git a/src/systems/update_frame_counters.rs b/src/systems/update_frame_counters.rs index b97f213..8a7e20d 100644 --- a/src/systems/update_frame_counters.rs +++ b/src/systems/update_frame_counters.rs @@ -18,12 +18,8 @@ use std::io::BufReader; use std::path::PathBuf; use std::time::Duration; - -pub fn update_frame_counters( - mut app_state: ResMut, - mut args: ResMut, -) { - let elapsed_time = app_state.song_stopwatch.elapsed().as_secs_f64(); - app_state.fft_frame_counter = (elapsed_time / TIME_BETWEEN_FFT_FRAMES) as usize; - app_state.total_frame_counter = (elapsed_time / TIME_BETWEEN_FRAMES) as usize; +pub fn update_frame_counters(mut fft_state: ResMut, mut args: ResMut) { + let elapsed_time = fft_state.fft_timer.elapsed().as_secs_f64(); + fft_state.fft_frame_counter = (elapsed_time / TIME_BETWEEN_FFT_FRAMES) as usize; + fft_state.total_frame_counter = (elapsed_time / TIME_BETWEEN_FRAMES) as usize; } diff --git a/src/systems/update_view_settings.rs b/src/systems/update_view_settings.rs index fea78c5..55ee947 100644 --- a/src/systems/update_view_settings.rs +++ b/src/systems/update_view_settings.rs @@ -1,13 +1,13 @@ -use crate::*; use crate::fft::time_interpolate; +use crate::*; use bevy::render::mesh::VertexAttributeValues; -use bevy_egui::egui::{Align2, Color32, Stroke}; use bevy::sprite::{Anchor, Material2d}; use bevy::{ app::AppExit, prelude::*, sprite::{MaterialMesh2dBundle, Mesh2dHandle}, }; +use bevy_egui::egui::{Align2, Color32, Stroke}; use bevy_egui::{egui, EguiContexts, EguiPlugin}; use clap::{ArgAction, Parser}; use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; @@ -18,13 +18,12 @@ use std::io::BufReader; use std::path::PathBuf; use std::time::Duration; - -pub fn update_view_settings ( +pub fn update_view_settings( mut commands: Commands, mut window: Query<&mut Window>, mut meshes: ResMut>, mut materials: ResMut>, - mut app_state: ResMut, + mut app_state: ResMut, mut args: ResMut, mut clear_color: ResMut, mut text_query: Query<(&mut Transform, &mut Text)>, @@ -33,7 +32,7 @@ pub fn update_view_settings ( ) { let mut differencing_args = differencing_args_query.get_single_mut().unwrap(); - // Update bar sizes and positions on resize + // Update bar sizes and positions on resize let w = window.single_mut().width(); if differencing_args.window_width != w { let h = window.single_mut().height(); @@ -43,8 +42,10 @@ pub fn update_view_settings ( let bar_size = w / app_state.fft[0].len() as f32; for (i, b) in app_state.despawn_handles.chunks(2).enumerate() { - bar_query.get_mut(b[0]).unwrap().translation.x = bar_size * i as f32 + bar_size / 2.0 - w / 2.0; - bar_query.get_mut(b[1]).unwrap().translation.x = bar_size * i as f32 + bar_size / 2.0 - w / 2.0; + bar_query.get_mut(b[0]).unwrap().translation.x = + bar_size * i as f32 + bar_size / 2.0 - w / 2.0; + bar_query.get_mut(b[1]).unwrap().translation.x = + bar_size * i as f32 + bar_size / 2.0 - w / 2.0; } let outer_bar_size = bar_size / 2.0; @@ -90,7 +91,10 @@ pub fn update_view_settings ( } // Update text color + visibility + size - if differencing_args.text_color != args.text_color || differencing_args.track_name != args.track_name || differencing_args.font_size != args.font_size { + if differencing_args.text_color != args.text_color + || differencing_args.track_name != args.track_name + || differencing_args.font_size != args.font_size + { for mut text in &mut text_query { if args.track_name { text.1.sections[0].style.color = args.text_color; @@ -112,9 +116,12 @@ pub fn update_view_settings ( } // Update bar colors - if differencing_args.bar_color != args.bar_color || differencing_args.border_color != args.border_color { + if differencing_args.bar_color != args.bar_color + || differencing_args.border_color != args.border_color + { for handle in app_state.curr_bars.chunks(2) { - let (color_handle1, color_handle2) = (handle[0].1.clone_weak(), handle[1].1.clone_weak()); + let (color_handle1, color_handle2) = + (handle[0].1.clone_weak(), handle[1].1.clone_weak()); materials.get_mut(color_handle1).unwrap().color = args.border_color; materials.get_mut(color_handle2).unwrap().color = args.bar_color; } @@ -125,7 +132,8 @@ pub fn update_view_settings ( // Update border size if differencing_args.border_size != args.border_size { let w = window.single_mut().width(); - let bar_size = ((w as f32 / (app_state.curr_bars.len() / 2) as f32) - args.border_size as f32) / 2.0; + let bar_size = + ((w as f32 / (app_state.curr_bars.len() / 2) as f32) - args.border_size as f32) / 2.0; for handle in app_state.curr_bars.chunks(2) { let handle1 = handle[1].0.clone_weak();