From 8a462a899dd4c725b4a11775a5f5db91980fe808 Mon Sep 17 00:00:00 2001 From: Dukantic Date: Mon, 7 Jul 2025 21:27:12 +0200 Subject: [PATCH] move all music data to struct, save this data now --- src/main.rs | 167 +++++++++++++------------------------------- src/music.rs | 107 ++++++++++++++++++++++++++++ src/polygon_draw.rs | 7 ++ src/utils.rs | 68 +++++++++++++++++- 4 files changed, 230 insertions(+), 119 deletions(-) create mode 100644 src/music.rs diff --git a/src/main.rs b/src/main.rs index d91cc36..e0933cd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,9 @@ mod polygon_draw; use polygon_draw::{Polygon, PolygonFrame}; +mod music; +use music::Music; + mod utils; use utils::string_to_color; @@ -48,23 +51,16 @@ enum Message { FileNameChanged(String), } -#[derive(Serialize, Deserialize)] struct MyApp { - poly_frame: PolygonFrame, - #[serde(skip, default = "dummy_instant")] + music: Music, time_last_frame: Instant, - nb_sec_for_rev: f32, - #[serde(skip, default = "dummy_audio_manager")] - audio_manager: AudioManager, - #[serde(skip, default = "dummy_sound")] - default_sound: StaticSoundData, - file_name: String, show_save_panel: bool, paused: bool, - #[serde(skip, default = "load_path_sounds")] + audio_manager: AudioManager, + default_sound: StaticSoundData, all_sounds: Vec, - #[serde(skip, default = "load_path_saves")] all_saves: Vec, + current_delta: f32, } impl MyApp { @@ -74,22 +70,15 @@ impl MyApp { let sound_data = StaticSoundData::from_file("assets/tick.ogg").expect("Fail to load audio"); ( Self { - nb_sec_for_rev: 4.0, time_last_frame: Instant::now(), audio_manager: manager, default_sound: sound_data.clone(), - file_name: "polymusic.json".to_string(), show_save_panel: true, paused: false, all_sounds: load_path_sounds(), all_saves: load_path_saves(), - poly_frame: PolygonFrame { - teta: 0.0, - polygons: vec![ - //Polygon::n_gon(0.0, 12), - //Polygon::triangle(0.0), - ], - }, + music: Music::default(), + current_delta: 0.0, }, Task::none(), ) @@ -99,133 +88,73 @@ impl MyApp { Message::WindowEvent(window::Event::Resized(size)) => { println!("Resize detected: {}x{}", size.width, size.height); } - Message::ButtonPressedIncrement => self.nb_sec_for_rev += 0.5, + Message::ButtonPressedIncrement => self.music.nb_sec_for_rev += 0.5, Message::ButtonPressedDecrement => { - if self.nb_sec_for_rev > 0.5 { - self.nb_sec_for_rev -= 0.5; + if self.music.nb_sec_for_rev > 0.5 { + self.music.nb_sec_for_rev -= 0.5; } } Message::AddPolygon(s) => { - let mut poly: Polygon; - if s.starts_with("Ngon") { - if let Ok(sides) = s.trim_start_matches("Ngon").parse::() { - poly = Polygon::n_gon(0.0, sides, self.default_sound.clone()); - poly.name = format!("Ngon_{sides}"); - } else { - return; - } - } else { - match s.as_str() { - "Segment" => { - poly = Polygon::segment(0.0, self.default_sound.clone()); - poly.name = "Segment".to_string(); - } - "Triangle" => { - poly = Polygon::triangle(0.0, self.default_sound.clone()); - poly.name = "Triangle".to_string(); - } - "Square" => { - poly = Polygon::square(0.0, self.default_sound.clone()); - poly.name = "Square".to_string(); - } - "Nr6In30" => { - poly = Polygon::nr_6_in_30(0.0, self.default_sound.clone()); - poly.name = "Nr6In30".to_string(); - } - "Nr7In30" => { - poly = Polygon::nr_7_in_30(0.0, self.default_sound.clone()); - poly.name = "Nr7In30".to_string(); - } - "Nr8In30" => { - poly = Polygon::nr_8_in_30(0.0, self.default_sound.clone()); - poly.name = "Nr8In30".to_string(); - } - "Nr9In30" => { - poly = Polygon::nr_9_in_30(0.0, self.default_sound.clone()); - poly.name = "Nr9In30".to_string(); - } - "Nr8In42" => { - poly = Polygon::nr_8_in_42(0.0, self.default_sound.clone()); - poly.name = "Nr8In42".to_string(); - } - "Nr9In42" => { - poly = Polygon::nr_9_in_42(0.0, self.default_sound.clone()); - poly.name = "Nr9In42".to_string(); - } - "Nr10aIn42" => { - poly = Polygon::nr_10a_in_42(0.0, self.default_sound.clone()); - poly.name = "Nr10aIn42".to_string(); - } - "Nr10bIn42" => { - poly = Polygon::nr_10b_in_42(0.0, self.default_sound.clone()); - poly.name = "Nr10bIn42".to_string(); - } - _ => poly = Polygon::n_gon(0.0, 0, self.default_sound.clone()), - } - } - self.poly_frame.polygons.push(poly); + self.music.add_polygon(self.current_delta, s); } Message::Tick => { if !self.paused { let time_btw = Instant::now().duration_since(self.time_last_frame); - let teta_temp = self.poly_frame.teta; - self.poly_frame.teta += 2.0 - * PI - * (1.0 / self.nb_sec_for_rev) - * (time_btw.as_millis() as f32 / 1_000.0); - self.poly_frame.teta %= 2.0 * PI; - let sound_to_play = self - .poly_frame - .all_sound_to_play_btw(teta_temp, self.poly_frame.teta); - for sound in sound_to_play { - self.audio_manager - .play(sound.clone()) - .expect("Error to play sound"); - } + self.current_delta += time_btw.as_millis() as f32 / 1000.0; + self.music + .apply_tick(self.current_delta, time_btw, &mut self.audio_manager); self.time_last_frame = Instant::now(); } } Message::Remove(i) => { - self.poly_frame.polygons.remove(i - 1); + self.music.remove_polygon(self.current_delta, i - 1); } Message::ChangeTeta(i, teta) => { - self.poly_frame.polygons[i].global_teta = teta; + self.music.set_teta(self.current_delta, i, teta); } Message::ChangeColor(i, s) => { - let c = string_to_color(&s); - self.poly_frame.polygons[i].color = c; - self.poly_frame.polygons[i].color_name = s; + self.music.set_color(self.current_delta, i, s); } Message::ChangeSound(i, s) => { - self.poly_frame.polygons[i].sound = - StaticSoundData::from_file(format!("./assets/{s}")) - .expect("Fail to load audio"); - self.poly_frame.polygons[i].sound_name = s; + let sound = StaticSoundData::from_file(format!("./assets/{s}")) + .expect("Fail to load audio"); + self.music.set_sound(self.current_delta, i, sound, s); } Message::Save => { - let json = serde_json::to_string_pretty(&self).unwrap(); - fs::write(format!("./saves/{0}", &self.file_name), json).unwrap(); + let json = serde_json::to_string_pretty(&self.music).unwrap(); + fs::write(format!("./saves/{0}", &self.music.file_name), json).unwrap(); } Message::Load => { - let json = fs::read_to_string(format!("./saves/{0}", &self.file_name)).unwrap(); - let decoded: MyApp = serde_json::from_str(&json).unwrap(); - *self = decoded; - self.poly_frame.update(); + let json = fs::read_to_string(format!("./saves/{0}", &self.music.file_name)); + match json { + Ok(j) => { + let decoded: Music = serde_json::from_str(&j).unwrap(); + self.music = decoded; + self.music.update_frame(self.current_delta); + } + Err(e) => { + eprintln!("Error, no saves with this name to load, {e} "); + } + } } Message::ToggleSavePanel => self.show_save_panel = !self.show_save_panel, - Message::FileNameChanged(s) => self.file_name = s, + Message::FileNameChanged(s) => self.music.file_name = s, _ => {} } } fn view(&self) -> iced::Element { - let txt_nb_rev = format!("Number of second for revolution : {}", self.nb_sec_for_rev); + let txt_nb_rev = format!( + "Number of second for revolution : {}", + self.music.nb_sec_for_rev + ); let mut i = 0; let entries = self.all_sounds.clone(); //Create all polygon options let polygon_rows: Vec> = self - .poly_frame + .music + .current_frame(self.current_delta) .polygons .iter() .map(|polygon| { @@ -284,15 +213,17 @@ impl MyApp { if self.show_save_panel { save_panel.push( - TextInput::new("Name File", &self.file_name) + TextInput::new("Name File", &self.music.file_name) .on_input(|new_value| Message::FileNameChanged(new_value)) .into(), ); save_panel.push(button("Save").on_press(Message::Save).into()); save_panel.push( - pick_list(self.all_saves.clone(), Some(&self.file_name), move |s| { - Message::FileNameChanged(s) - }) + pick_list( + self.all_saves.clone(), + Some(&self.music.file_name), + move |s| Message::FileNameChanged(s), + ) .into(), ); @@ -303,7 +234,7 @@ impl MyApp { row(save_panel).spacing(20), row![ container( - canvas(&self.poly_frame) + canvas(self.music.current_frame(self.current_delta)) .height(Length::FillPortion(1)) .width(Length::FillPortion(1)) ), diff --git a/src/music.rs b/src/music.rs new file mode 100644 index 0000000..c9e470e --- /dev/null +++ b/src/music.rs @@ -0,0 +1,107 @@ +use crate::utils::string_to_polygon; +use crate::{polygon_draw::*, utils::string_to_color}; +use serde::{Deserialize, Serialize}; + +use kira::{ + AudioManager, AudioManagerSettings, DefaultBackend, sound::static_sound::StaticSoundData, +}; + +use std::f32::consts::PI; +use std::time::Duration; +use std::time::Instant; + +#[derive(Serialize, Deserialize)] +pub struct Music { + pub poly_frame: Vec<(f32, PolygonFrame)>, + pub nb_sec_for_rev: f32, + pub file_name: String, +} + +impl Music { + fn find_poly_frame(&mut self, delta: f32) -> &mut PolygonFrame { + if let Some(i) = self + .poly_frame + .windows(2) + .position(|w| w[0].0 < delta && delta < w[1].0) + { + &mut self.poly_frame[i].1 + } else { + &mut self.poly_frame[0].1 + } + } + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~PUBLIC~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + pub fn current_frame(&self, delta: f32) -> &PolygonFrame { + if let Some(i) = self + .poly_frame + .windows(2) + .position(|w| w[0].0 < delta && delta < w[1].0) + { + &self.poly_frame[i].1 + } else { + &self.poly_frame[0].1 + } + } + + pub fn default() -> Music { + Music { + poly_frame: vec![(0.0, PolygonFrame::default())], + nb_sec_for_rev: 1.0, + file_name: "Polymusic.json".to_string(), + } + } + + pub fn update_frame(&mut self, delta: f32) { + self.find_poly_frame(delta).update(); + } + + pub fn apply_tick(&mut self, delta: f32, time_btw: Duration, audio_manager: &mut AudioManager) { + let nb_sec_for_rev = self.nb_sec_for_rev; + let current_frame = self.find_poly_frame(delta); + let teta_temp = current_frame.teta; + current_frame.teta += + 2.0 * PI * (1.0 / nb_sec_for_rev) * (time_btw.as_millis() as f32 / 1_000.0); + current_frame.teta %= 2.0 * PI; + let sound_to_play = current_frame.all_sound_to_play_btw(teta_temp, current_frame.teta); + for sound in sound_to_play { + audio_manager + .play(sound.clone()) + .expect("Error to play sound"); + } + } + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~SET~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + pub fn set_sound( + &mut self, + delta: f32, + index: usize, + sound: StaticSoundData, + sound_name: String, + ) { + let current_frame = self.find_poly_frame(delta); + current_frame.polygons[index].sound = sound; + current_frame.polygons[index].sound_name = sound_name; + } + + pub fn set_teta(&mut self, delta: f32, index: usize, teta: f32) { + self.find_poly_frame(delta).polygons[index].global_teta = teta; + } + + pub fn set_color(&mut self, delta: f32, index: usize, color_name: String) { + let current_frame = self.find_poly_frame(delta); + current_frame.polygons[index].color = string_to_color(&color_name); + current_frame.polygons[index].color_name = color_name; + } + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ADD/REMOVE~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + pub fn add_polygon(&mut self, delta: f32, polygon_name: String) { + let current_frame = self.find_poly_frame(delta); + let poly = string_to_polygon(polygon_name); + current_frame.polygons.push(poly); + } + + pub fn remove_polygon(&mut self, delta: f32, i: usize) { + self.find_poly_frame(delta).polygons.remove(i); + } +} diff --git a/src/polygon_draw.rs b/src/polygon_draw.rs index e7dc43c..83cd3c0 100644 --- a/src/polygon_draw.rs +++ b/src/polygon_draw.rs @@ -42,6 +42,13 @@ impl PolygonFrame { poly.update(); } } + + pub fn default() -> PolygonFrame { + PolygonFrame { + teta: 0.0, + polygons: vec![], + } + } } impl canvas::Program for PolygonFrame { diff --git a/src/utils.rs b/src/utils.rs index 3d2ffa9..75c2160 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,5 +1,8 @@ +use crate::Polygon; use iced::Color; - +use kira::{ + AudioManager, AudioManagerSettings, DefaultBackend, sound::static_sound::StaticSoundData, +}; pub fn string_to_color>(s: S) -> Color { match s.as_ref() { "Green" => Color::from_rgb(0.0, 1.0, 0.0), @@ -10,3 +13,66 @@ pub fn string_to_color>(s: S) -> Color { _ => Color::BLACK, } } +pub fn string_to_polygon>(str: S) -> Polygon { + let s = str.as_ref(); + let mut poly: Polygon; + if s.starts_with("Ngon") { + if let Ok(sides) = s.trim_start_matches("Ngon").parse::() { + return Polygon::n_gon(0.0, sides, dummy_sound()); + } else { + return Polygon::n_gon(0.0, 0, dummy_sound()); + } + } else { + match s { + "Segment" => { + poly = Polygon::segment(0.0, dummy_sound()); + poly.name = "Segment".to_string(); + } + "Triangle" => { + poly = Polygon::triangle(0.0, dummy_sound()); + poly.name = "Triangle".to_string(); + } + "Square" => { + poly = Polygon::square(0.0, dummy_sound()); + poly.name = "Square".to_string(); + } + "Nr6In30" => { + poly = Polygon::nr_6_in_30(0.0, dummy_sound()); + poly.name = "Nr6In30".to_string(); + } + "Nr7In30" => { + poly = Polygon::nr_7_in_30(0.0, dummy_sound()); + poly.name = "Nr7In30".to_string(); + } + "Nr8In30" => { + poly = Polygon::nr_8_in_30(0.0, dummy_sound()); + poly.name = "Nr8In30".to_string(); + } + "Nr9In30" => { + poly = Polygon::nr_9_in_30(0.0, dummy_sound()); + poly.name = "Nr9In30".to_string(); + } + "Nr8In42" => { + poly = Polygon::nr_8_in_42(0.0, dummy_sound()); + poly.name = "Nr8In42".to_string(); + } + "Nr9In42" => { + poly = Polygon::nr_9_in_42(0.0, dummy_sound()); + poly.name = "Nr9In42".to_string(); + } + "Nr10aIn42" => { + poly = Polygon::nr_10a_in_42(0.0, dummy_sound()); + poly.name = "Nr10aIn42".to_string(); + } + "Nr10bIn42" => { + poly = Polygon::nr_10b_in_42(0.0, dummy_sound()); + poly.name = "Nr10bIn42".to_string(); + } + _ => poly = Polygon::n_gon(0.0, 0, dummy_sound()), + } + poly + } +} +fn dummy_sound() -> StaticSoundData { + StaticSoundData::from_file("assets/tick.ogg").expect("Fail to load audio") +}