diff --git a/Cargo.lock b/Cargo.lock index 0aaccab..2080d83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1757,6 +1757,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + [[package]] name = "jni" version = "0.21.1" @@ -2724,6 +2730,8 @@ version = "0.1.0" dependencies = [ "iced", "kira", + "serde", + "serde_json", "tokio", ] @@ -3030,6 +3038,12 @@ dependencies = [ "unicode-script", ] +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + [[package]] name = "same-file" version = "1.0.6" @@ -3096,6 +3110,18 @@ dependencies = [ "syn 2.0.101", ] +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.20" diff --git a/Cargo.toml b/Cargo.toml index 1755eec..e8b6a4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,3 +7,5 @@ edition = "2024" iced = { version = "0.13.1", features = ["canvas", "tokio"] } kira = "0.10.6" tokio = {version = "1.45.1", features = ["time"]} +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0.140" diff --git a/saves/polymusic.json b/saves/polymusic.json new file mode 100644 index 0000000..afcdc79 --- /dev/null +++ b/saves/polymusic.json @@ -0,0 +1,33 @@ +{ + "poly_frame": { + "teta": 1.3752483, + "polygons": [ + { + "global_teta": 0.0, + "points_teta": [ + 0.0, + 3.1415927 + ], + "sound_name": "A_LA.ogg", + "name": "Segment", + "color_name": "Pink" + }, + { + "global_teta": 0.0, + "points_teta": [ + 1.0471976, + 1.2566371, + 2.5132742, + 3.7699113, + 5.0265484, + 5.2359877 + ], + "sound_name": "D_RE.ogg", + "name": "Nr6In30", + "color_name": "Black" + } + ] + }, + "nb_sec_for_rev": 8.0, + "file_name": "polymusic.json" +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 402079f..6092359 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,9 +5,10 @@ use std::fs; use iced::{ Color, Element, Task, Theme, time::{self, Duration}, - widget::{Column, button, canvas, column, pick_list, row, slider, text}, + widget::{Column, button, canvas, column, container, pick_list, row, slider, text}, }; use polygon_draw::{Polygon, PolygonFrame}; +use serde::{Deserialize, Serialize}; use std::f32::consts::PI; use std::time::Instant; @@ -33,14 +34,23 @@ enum Message { Remove(usize), ChangeColor(usize, String), ChangeSound(usize, String), + ToggleSavePanel, + Save, + Load, } +#[derive(Serialize, Deserialize)] struct MyApp { poly_frame: PolygonFrame, + #[serde(skip, default = "dummy_instant")] 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, } impl MyApp { @@ -54,6 +64,8 @@ impl MyApp { time_last_frame: Instant::now(), audio_manager: manager, default_sound: sound_data.clone(), + file_name: "polymusic.json".to_string(), + show_save_panel: true, poly_frame: PolygonFrame { teta: 0.0, polygons: vec![ @@ -175,6 +187,17 @@ impl MyApp { .expect("Fail to load audio"); self.poly_frame.polygons[i].sound_name = s; } + Message::Save => { + let json = serde_json::to_string_pretty(&self).unwrap(); + fs::write(format!("./saves/{0}", &self.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(); + } + Message::ToggleSavePanel => self.show_save_panel = !self.show_save_panel, } } @@ -238,10 +261,21 @@ impl MyApp { .collect(); let polygon_column = Column::with_children(polygon_rows); + let mut save_panel: Vec> = vec![ + button("Toggle Save Panel") + .on_press(Message::ToggleSavePanel) + .into(), + ]; + + if self.show_save_panel { + save_panel.push(button("Save").on_press(Message::Save).into()); + save_panel.push(button("Load").on_press(Message::Load).into()); + } column![ text("Polymusic").size(32.0), + row(save_panel).spacing(20), row![ - canvas(&self.poly_frame).height(500).width(500), + container(canvas(&self.poly_frame).height(500).width(500)), column![ text(txt_nb_rev), row![ @@ -255,10 +289,24 @@ impl MyApp { polygon_column, ], ] + .spacing(20), ] + .spacing(25) + .padding(25) .into() } fn subscription(&self) -> iced::Subscription { time::every(Duration::from_millis(16)).map(|_| Message::Tick) } } + +fn dummy_sound() -> StaticSoundData { + StaticSoundData::from_file("assets/tick.ogg").expect("Fail to load audio") +} +fn dummy_instant() -> Instant { + Instant::now() +} +fn dummy_audio_manager() -> AudioManager { + AudioManager::::new(AudioManagerSettings::default()) + .expect("Error to load AudioManager") +} diff --git a/src/polygon_draw.rs b/src/polygon_draw.rs index bd9f5ad..5b74ed5 100644 --- a/src/polygon_draw.rs +++ b/src/polygon_draw.rs @@ -7,6 +7,7 @@ use iced::widget::canvas::Stroke; use iced::widget::canvas::Style; use iced::{Color, Rectangle, Renderer, Theme}; use kira::sound::static_sound::StaticSoundData; +use serde::{Deserialize, Serialize}; pub trait RotationExt { fn rotate(&mut self, teta: f32) -> Self; @@ -20,6 +21,7 @@ impl RotationExt for Vector { ) } } +#[derive(Serialize, Deserialize)] pub struct PolygonFrame { pub teta: f32, pub polygons: Vec, @@ -33,6 +35,11 @@ impl PolygonFrame { } all_sound } + pub fn update(&mut self) { + for poly in &mut self.polygons { + poly.update(); + } + } } impl canvas::Program for PolygonFrame { @@ -102,18 +109,33 @@ impl canvas::Program for PolygonFrame { } } -#[derive(Debug)] +#[derive(Debug, Serialize, Deserialize)] pub struct Polygon { pub global_teta: f32, pub points_teta: Vec, + #[serde(skip, default = "dummy_sound")] pub sound: StaticSoundData, pub sound_name: String, pub name: String, + #[serde(skip)] pub color: Color, pub color_name: String, } #[warn(dead_code)] impl Polygon { + pub fn update(&mut self) { + let path = format!("./assets/{0}", &self.sound_name); + eprintln!("path:{path}"); + self.sound = StaticSoundData::from_file(&path).expect("fail to load the sound"); + self.color = match self.color_name.as_str() { + "Green" => Color::from_rgb(0.0, 1.0, 0.0), + "Blue" => Color::from_rgb(0.0, 0.0, 1.0), + "Cyan" => Color::from_rgb(0.0, 1.0, 1.0), + "Yellow" => Color::from_rgb(1.0, 1.0, 0.0), + "Pink" => Color::from_rgb(1.0, 0.0, 1.0), + _ => Color::BLACK, + }; + } pub fn sound_to_play_btw(&self, before: f32, after: f32) -> Vec<&StaticSoundData> { let mut sound_to_play: Vec<&StaticSoundData> = vec![]; if after < before { @@ -140,7 +162,7 @@ impl Polygon { global_teta: teta, points_teta: v, sound: sound, - sound_name: "./assets/tick.ogg".to_string(), + sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), name: "".to_string(), color: Color::BLACK, @@ -164,7 +186,7 @@ impl Polygon { name: "".to_string(), color: Color::BLACK, global_teta: teta, - sound_name: "./assets/tick.ogg".to_string(), + sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), points_teta: vec![ 2.0 * 5.0 * PI / 30.0, @@ -181,7 +203,7 @@ impl Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, - sound_name: "./assets/tick.ogg".to_string(), + sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ @@ -200,7 +222,7 @@ impl Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, - sound_name: "./assets/tick.ogg".to_string(), + sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ @@ -220,7 +242,7 @@ impl Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, - sound_name: "./assets/tick.ogg".to_string(), + sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ @@ -241,7 +263,7 @@ impl Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, - sound_name: "./assets/tick.ogg".to_string(), + sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ @@ -261,7 +283,7 @@ impl Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, - sound_name: "./assets/tick.ogg".to_string(), + sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ @@ -282,7 +304,7 @@ impl Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, - sound_name: "./assets/tick.ogg".to_string(), + sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ @@ -304,7 +326,7 @@ impl Polygon { sound: sound, name: "".to_string(), color: Color::BLACK, - sound_name: "./assets/tick.ogg".to_string(), + sound_name: "tick.ogg".to_string(), color_name: "Black".to_string(), global_teta: teta, points_teta: vec![ @@ -322,3 +344,6 @@ impl Polygon { } } } +fn dummy_sound() -> StaticSoundData { + StaticSoundData::from_file("assets/tick.ogg").expect("Fail to load audio") +}