pause and play, first step for the time line

This commit is contained in:
2025-07-08 19:05:35 +02:00
parent e76720c0d7
commit 47c41be544
5 changed files with 116 additions and 32 deletions

1
Cargo.lock generated
View File

@@ -2730,6 +2730,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"iced", "iced",
"kira", "kira",
"regex",
"serde", "serde",
"serde_json", "serde_json",
"tokio", "tokio",

View File

@@ -9,3 +9,4 @@ kira = "0.10.6"
tokio = {version = "1.45.1", features = ["time"]} tokio = {version = "1.45.1", features = ["time"]}
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.140" serde_json = "1.0.140"
regex = "1.11.1"

View File

@@ -1,10 +1,12 @@
mod polygon_draw; mod polygon_draw;
use iced::widget::text_input::Style;
use polygon_draw::Polygon; use polygon_draw::Polygon;
mod music; mod music;
use music::Music; use music::Music;
mod utils; mod utils;
use utils::str_to_sec;
use std::fs; use std::fs;
@@ -17,6 +19,7 @@ use iced::{
}, },
}; };
use regex::Regex;
use std::f32::consts::PI; use std::f32::consts::PI;
use std::time::Instant; use std::time::Instant;
@@ -24,6 +27,7 @@ use kira::{
AudioManager, AudioManagerSettings, DefaultBackend, sound::static_sound::StaticSoundData, AudioManager, AudioManagerSettings, DefaultBackend, sound::static_sound::StaticSoundData,
}; };
use crate::utils::delta_to_string;
fn main() -> iced::Result { fn main() -> iced::Result {
iced::application("My App", MyApp::update, MyApp::view) iced::application("My App", MyApp::update, MyApp::view)
.theme(|_| Theme::Dark) .theme(|_| Theme::Dark)
@@ -46,6 +50,10 @@ enum Message {
Save, Save,
Load, Load,
FileNameChanged(String), FileNameChanged(String),
TogglePaused,
SetMusicLength,
LengthChange(String),
ChangeDelta(f32),
} }
struct MyApp { struct MyApp {
@@ -57,6 +65,7 @@ struct MyApp {
all_sounds: Vec<String>, all_sounds: Vec<String>,
all_saves: Vec<String>, all_saves: Vec<String>,
current_delta: f32, current_delta: f32,
str_music_length: String,
} }
impl MyApp { impl MyApp {
@@ -67,12 +76,13 @@ impl MyApp {
Self { Self {
time_last_frame: Instant::now(), time_last_frame: Instant::now(),
audio_manager: manager, audio_manager: manager,
show_save_panel: true, show_save_panel: false,
paused: false, paused: true,
all_sounds: load_path_sounds(), all_sounds: load_path_sounds(),
all_saves: load_path_saves(), all_saves: load_path_saves(),
music: Music::default(), music: Music::default(),
current_delta: 0.0, current_delta: 0.0,
str_music_length: "01:00:00".to_string(),
}, },
Task::none(), Task::none(),
) )
@@ -133,6 +143,22 @@ impl MyApp {
} }
Message::ToggleSavePanel => self.show_save_panel = !self.show_save_panel, Message::ToggleSavePanel => self.show_save_panel = !self.show_save_panel,
Message::FileNameChanged(s) => self.music.file_name = s, Message::FileNameChanged(s) => self.music.file_name = s,
Message::LengthChange(s) => self.str_music_length = s,
Message::TogglePaused => {
self.paused = !self.paused;
if !self.paused {
self.time_last_frame = Instant::now();
}
}
Message::SetMusicLength => {
if self.is_length_valid() {
self.music.length = str_to_sec(&self.str_music_length)
}
}
Message::ChangeDelta(f) => {
self.current_delta = f;
self.music.fix_teta(self.current_delta);
}
_ => {} _ => {}
} }
} }
@@ -175,7 +201,7 @@ impl MyApp {
}) })
.step(PI / 84f32), // 84 | 4 for do PI / 4 .step(PI / 84f32), // 84 | 4 for do PI / 4
] ]
.spacing(20) .spacing(10)
.into() .into()
}) })
.collect(); .collect();
@@ -198,7 +224,7 @@ impl MyApp {
.chain(ngon_options) .chain(ngon_options)
.collect(); .collect();
let polygon_column = scrollable(Column::with_children(polygon_rows)); let polygon_column = scrollable(Column::with_children(polygon_rows).spacing(20));
let mut save_panel: Vec<Element<Message>> = vec![ let mut save_panel: Vec<Element<Message>> = vec![
button("Toggle Save Panel") button("Toggle Save Panel")
.on_press(Message::ToggleSavePanel) .on_press(Message::ToggleSavePanel)
@@ -242,7 +268,7 @@ impl MyApp {
pick_list(all_options, Some("Choose polygon".to_string()), |s| { pick_list(all_options, Some("Choose polygon".to_string()), |s| {
Message::AddPolygon(s) Message::AddPolygon(s)
}), }),
polygon_column.spacing(10), polygon_column,
] ]
.spacing(10) .spacing(10)
.height(Length::FillPortion(1)) .height(Length::FillPortion(1))
@@ -250,17 +276,42 @@ impl MyApp {
] ]
.height(Length::FillPortion(2)) .height(Length::FillPortion(2))
.spacing(20), .spacing(20),
row![text("futur time line")] column![
.height(Length::FillPortion(1)) text("TimeLine"),
.width(Length::FillPortion(1)), row![
button("Toggle Play").on_press(Message::TogglePaused),
text(format!(
"{}/{}",
delta_to_string(self.current_delta),
delta_to_string(self.music.length)
))
],
row![
text("Music Length"),
TextInput::new("MM:SS:CS", &self.str_music_length)
.on_input(|new_value| Message::LengthChange(new_value)),
button("Valid").on_press(Message::SetMusicLength),
],
slider(0.0..=self.music.length, self.current_delta, move |f| {
Message::ChangeDelta(f)
})
.step(&self.music.length / 10_000.),
]
.height(Length::FillPortion(1))
.width(Length::FillPortion(1)),
] ]
.spacing(25) .spacing(25)
.padding(25) .padding(25)
.into() .into()
} }
fn is_length_valid(&self) -> bool {
let re = Regex::new(r"^\d{2}:\d{2}:\d{2}$").unwrap();
re.is_match(self.str_music_length.as_str())
}
fn subscription(&self) -> iced::Subscription<Message> { fn subscription(&self) -> iced::Subscription<Message> {
iced::Subscription::batch([ iced::Subscription::batch([
window::events().map(|(_id, event)| Message::WindowEvent(event)), //window::events().map(|(_id, event)| Message::WindowEvent(event)),
iced::time::every(std::time::Duration::from_millis(16)).map(|_| Message::Tick), iced::time::every(std::time::Duration::from_millis(16)).map(|_| Message::Tick),
]) ])
} }

View File

@@ -12,6 +12,8 @@ pub struct Music {
pub poly_frame: Vec<(f32, PolygonFrame)>, pub poly_frame: Vec<(f32, PolygonFrame)>,
pub nb_sec_for_rev: f32, pub nb_sec_for_rev: f32,
pub file_name: String, pub file_name: String,
pub length: f32,
teta: f32,
} }
impl Music { impl Music {
@@ -23,7 +25,7 @@ impl Music {
{ {
&mut self.poly_frame[i].1 &mut self.poly_frame[i].1
} else { } else {
&mut self.poly_frame[0].1 &mut self.poly_frame.last_mut().unwrap().1
} }
} }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~PUBLIC~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~PUBLIC~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -35,15 +37,20 @@ impl Music {
{ {
&self.poly_frame[i].1 &self.poly_frame[i].1
} else { } else {
&self.poly_frame[0].1 &self.poly_frame.last().unwrap().1
} }
} }
pub fn default() -> Music { pub fn default() -> Music {
Music { Music {
poly_frame: vec![(0.0, PolygonFrame::default())], poly_frame: vec![
(0.0, PolygonFrame::default()),
(10.0, PolygonFrame::default()),
],
nb_sec_for_rev: 1.0, nb_sec_for_rev: 1.0,
file_name: "Polymusic.json".to_string(), file_name: "Polymusic.json".to_string(),
length: 60.0,
teta: 0.,
} }
} }
@@ -51,13 +58,20 @@ impl Music {
self.find_poly_frame(delta).update(); self.find_poly_frame(delta).update();
} }
pub fn fix_teta(&mut self, delta: f32) {
let new_teta = delta % self.nb_sec_for_rev * 2.0 * PI / self.nb_sec_for_rev;
self.teta = new_teta;
}
pub fn apply_tick(&mut self, delta: f32, time_btw: Duration, audio_manager: &mut AudioManager) { 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 teta_temp = self.teta;
self.teta +=
2.0 * PI * (1.0 / self.nb_sec_for_rev) * (time_btw.as_millis() as f32 / 1_000.0);
self.teta %= 2.0 * PI;
let currrent_teta = self.teta;
let current_frame = self.find_poly_frame(delta); let current_frame = self.find_poly_frame(delta);
let teta_temp = current_frame.teta;
current_frame.teta += current_frame.teta = currrent_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); let sound_to_play = current_frame.all_sound_to_play_btw(teta_temp, current_frame.teta);
for sound in sound_to_play { for sound in sound_to_play {
audio_manager audio_manager

View File

@@ -1,5 +1,5 @@
use crate::Polygon; use crate::Polygon;
use iced::Color; use iced::{Color, widget::canvas::path::lyon_path::geom::euclid::num::Floor};
use kira::sound::static_sound::StaticSoundData; use kira::sound::static_sound::StaticSoundData;
pub fn string_to_color<S: AsRef<str>>(s: S) -> Color { pub fn string_to_color<S: AsRef<str>>(s: S) -> Color {
match s.as_ref() { match s.as_ref() {
@@ -11,65 +11,82 @@ pub fn string_to_color<S: AsRef<str>>(s: S) -> Color {
_ => Color::BLACK, _ => Color::BLACK,
} }
} }
pub fn delta_to_string(delta: f32) -> String {
let time = [
((delta / 60.0) as u32),
((delta % 60.0) as u32),
((delta * 100.0) % 100.0) as u32,
];
let mut time_str = [String::new(), String::new(), String::new()];
for i in 0..3 {
if time[i] < 10 {
time_str[i] = format!("0{}", time[i]);
} else {
time_str[i] = time[i].to_string();
}
}
format!("{}:{}:{}", time_str[0], time_str[1], time_str[2])
}
pub fn str_to_sec(str: &str) -> f32 {
let parts: Vec<&str> = str.split(':').collect();
let min: f32 = parts[0].parse().expect("str of music length not valid");
let sec: f32 = parts[1].parse().expect("str of music length not valid");
let cs: f32 = parts[2].parse().expect("str of music length not valid");
min * 60.0 + sec + (cs / 100.0)
}
pub fn string_to_polygon<S: AsRef<str>>(str: S) -> Polygon { pub fn string_to_polygon<S: AsRef<str>>(str: S) -> Polygon {
let s = str.as_ref(); let s = str.as_ref();
let mut poly: Polygon; let mut poly: Polygon;
if s.starts_with("Ngon") { if s.starts_with("Ngon") {
if let Ok(sides) = s.trim_start_matches("Ngon").parse::<u8>() { if let Ok(sides) = s.trim_start_matches("Ngon").parse::<u8>() {
return Polygon::n_gon(0.0, sides, dummy_sound()); poly = Polygon::n_gon(0.0, sides, dummy_sound());
} else { } else {
return Polygon::n_gon(0.0, 0, dummy_sound()); poly = Polygon::n_gon(0.0, 0, dummy_sound());
} }
} else { } else {
match s { match s {
"Segment" => { "Segment" => {
poly = Polygon::segment(0.0, dummy_sound()); poly = Polygon::segment(0.0, dummy_sound());
poly.name = "Segment".to_string();
} }
"Triangle" => { "Triangle" => {
poly = Polygon::triangle(0.0, dummy_sound()); poly = Polygon::triangle(0.0, dummy_sound());
poly.name = "Triangle".to_string();
} }
"Square" => { "Square" => {
poly = Polygon::square(0.0, dummy_sound()); poly = Polygon::square(0.0, dummy_sound());
poly.name = "Square".to_string();
} }
"Nr6In30" => { "Nr6In30" => {
poly = Polygon::nr_6_in_30(0.0, dummy_sound()); poly = Polygon::nr_6_in_30(0.0, dummy_sound());
poly.name = "Nr6In30".to_string();
} }
"Nr7In30" => { "Nr7In30" => {
poly = Polygon::nr_7_in_30(0.0, dummy_sound()); poly = Polygon::nr_7_in_30(0.0, dummy_sound());
poly.name = "Nr7In30".to_string();
} }
"Nr8In30" => { "Nr8In30" => {
poly = Polygon::nr_8_in_30(0.0, dummy_sound()); poly = Polygon::nr_8_in_30(0.0, dummy_sound());
poly.name = "Nr8In30".to_string();
} }
"Nr9In30" => { "Nr9In30" => {
poly = Polygon::nr_9_in_30(0.0, dummy_sound()); poly = Polygon::nr_9_in_30(0.0, dummy_sound());
poly.name = "Nr9In30".to_string();
} }
"Nr8In42" => { "Nr8In42" => {
poly = Polygon::nr_8_in_42(0.0, dummy_sound()); poly = Polygon::nr_8_in_42(0.0, dummy_sound());
poly.name = "Nr8In42".to_string();
} }
"Nr9In42" => { "Nr9In42" => {
poly = Polygon::nr_9_in_42(0.0, dummy_sound()); poly = Polygon::nr_9_in_42(0.0, dummy_sound());
poly.name = "Nr9In42".to_string();
} }
"Nr10aIn42" => { "Nr10aIn42" => {
poly = Polygon::nr_10a_in_42(0.0, dummy_sound()); poly = Polygon::nr_10a_in_42(0.0, dummy_sound());
poly.name = "Nr10aIn42".to_string();
} }
"Nr10bIn42" => { "Nr10bIn42" => {
poly = Polygon::nr_10b_in_42(0.0, dummy_sound()); 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 = Polygon::n_gon(0.0, 0, dummy_sound()),
} }
poly
} }
poly.name = s.to_string();
poly
} }
fn dummy_sound() -> StaticSoundData { fn dummy_sound() -> StaticSoundData {
StaticSoundData::from_file("assets/tick.ogg").expect("Fail to load audio") StaticSoundData::from_file("assets/tick.ogg").expect("Fail to load audio")