pause and play, first step for the time line
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -2730,6 +2730,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"iced",
|
"iced",
|
||||||
"kira",
|
"kira",
|
||||||
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
69
src/main.rs
69
src/main.rs
@@ -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),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|||||||
30
src/music.rs
30
src/music.rs
@@ -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
|
||||||
|
|||||||
47
src/utils.rs
47
src/utils.rs
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user