From d701e2c624b8ebe14f34a54a8acb2cebc91f4c09 Mon Sep 17 00:00:00 2001 From: MBMS <31241793+MyBlackMIDIScore@users.noreply.github.com> Date: Wed, 25 Sep 2024 00:32:14 +0300 Subject: [PATCH] Add 2 second start delay (#72) --- Cargo.lock | 41 +++++++++++++++++++++++++ Cargo.toml | 1 + src/gui/window.rs | 6 ++-- src/gui/window/scene/cake_system/mod.rs | 6 ++-- src/gui/window/stats.rs | 27 +++++++++------- src/gui/window/top_panel.rs | 9 +++--- src/midi/audio/live.rs | 10 +++--- src/midi/audio/ram.rs | 16 ++++++---- src/midi/cake/blocks.rs | 4 +-- src/midi/cake/mod.rs | 6 ++-- src/midi/live/mod.rs | 2 +- src/midi/live/parse.rs | 4 +-- src/midi/live/view.rs | 4 +-- src/midi/mod.rs | 1 + src/midi/ram/mod.rs | 2 +- src/midi/shared/timer.rs | 16 +++++++--- 16 files changed, 107 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b1021f7..204aff2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -950,6 +950,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + [[package]] name = "derivative" version = "2.2.0" @@ -2061,6 +2070,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-derive" version = "0.4.2" @@ -2468,6 +2483,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -3374,6 +3395,25 @@ dependencies = [ "weezl", ] +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + [[package]] name = "tiny-skia" version = "0.8.4" @@ -3783,6 +3823,7 @@ dependencies = [ "rustc-hash", "serde", "serde_derive", + "time", "toml 0.8.12", "vulkano", "vulkano-shaders", diff --git a/Cargo.toml b/Cargo.toml index 1b5af1e..580b9c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,6 +37,7 @@ num_enum = "0.7.0" rfd = { version = "0.12.0", default-features = false, features = [ 'xdg-portal', ] } +time = "0.3.36" [profile.dev] opt-level = 2 diff --git a/src/gui/window.rs b/src/gui/window.rs index fa6f0f9..7f8bceb 100644 --- a/src/gui/window.rs +++ b/src/gui/window.rs @@ -11,8 +11,8 @@ mod xsynth_settings; use std::{ path::PathBuf, sync::{Arc, RwLock}, - time::Duration, }; +use time::Duration; use egui::{style::Margin, Frame, Visuals}; @@ -130,7 +130,7 @@ impl GuiWasabiWindow { .show_separator_line(false) .show(&ctx, |ui| { if let Some(midi_file) = self.midi_file.as_mut() { - let one_sec = Duration::from_secs(1); + let one_sec = Duration::seconds(1); let time = midi_file.timer().get_time(); ui.input(|events| { @@ -144,7 +144,7 @@ impl GuiWasabiWindow { egui::Key::ArrowLeft => { if midi_file.allows_seeking_backward() { midi_file.timer_mut().seek(if time <= one_sec { - Duration::from_secs(0) + Duration::seconds(0) } else { time - one_sec }) diff --git a/src/gui/window/scene/cake_system/mod.rs b/src/gui/window/scene/cake_system/mod.rs index b49b735..d6779ab 100644 --- a/src/gui/window/scene/cake_system/mod.rs +++ b/src/gui/window/scene/cake_system/mod.rs @@ -235,7 +235,7 @@ impl CakeRenderer { } } - let midi_time = midi_file.current_time().as_secs_f64(); + let midi_time = midi_file.current_time().as_seconds_f64(); let screen_start = (midi_time * midi_file.ticks_per_second() as f64) as i32; let screen_end = ((midi_time + view_range) * midi_file.ticks_per_second() as f64) as i32; @@ -372,8 +372,8 @@ impl CakeRenderer { .key_blocks() .iter() .map(|block| { - let passed = block.get_notes_passed_at(screen_end as u32) - - block.get_notes_passed_at(screen_start as u32); + let passed = + block.get_notes_passed_at(screen_end) - block.get_notes_passed_at(screen_start); if block.get_note_at(screen_start as u32).is_some() { passed as u64 + 1 diff --git a/src/gui/window/stats.rs b/src/gui/window/stats.rs index e953951..75c9ba9 100644 --- a/src/gui/window/stats.rs +++ b/src/gui/window/stats.rs @@ -50,9 +50,9 @@ pub fn draw_stats(win: &mut GuiWasabiWindow, ctx: &Context, pos: Pos2, mut stats .fixed_pos(pos) .fixed_size(egui::Vec2::new(200.0, 128.0)) .show(ctx, |ui| { - let mut time_millis: u64 = 0; - let mut time_sec: u64 = 0; - let mut time_min: u64 = 0; + let mut time_millis: i64 = 0; + let mut time_sec: i64 = 0; + let mut time_min: i64 = 0; let mut length_millis: u64 = 0; let mut length_sec: u64 = 0; let mut length_min: u64 = 0; @@ -61,7 +61,7 @@ pub fn draw_stats(win: &mut GuiWasabiWindow, ctx: &Context, pos: Pos2, mut stats if let Some(midi_file) = win.midi_file.as_mut() { stats.time_total = midi_file.midi_length().unwrap_or(0.0); - let time = midi_file.timer().get_time().as_secs_f64(); + let time = midi_file.timer().get_time().as_seconds_f64(); length_millis = (stats.time_total * 10.0) as u64 % 10; length_sec = stats.time_total as u64 % 60; @@ -73,9 +73,9 @@ pub fn draw_stats(win: &mut GuiWasabiWindow, ctx: &Context, pos: Pos2, mut stats stats.time_passed = time; } - time_millis = (stats.time_passed * 10.0) as u64 % 10; - time_sec = stats.time_passed as u64 % 60; - time_min = stats.time_passed as u64 / 60; + time_millis = (stats.time_passed * 10.0) as i64 % 10; + time_sec = stats.time_passed as i64 % 60; + time_min = stats.time_passed as i64 / 60; note_stats = midi_file.stats(); } @@ -84,10 +84,15 @@ pub fn draw_stats(win: &mut GuiWasabiWindow, ctx: &Context, pos: Pos2, mut stats ui.monospace("Time:"); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { ui.monospace(format!( - "{:0width$}:{:0width$}.{} / {:0width$}:{:0width$}.{}", - time_min, - time_sec, - time_millis, + "{}{:0width$}:{:0width$}.{} / {:0width$}:{:0width$}.{}", + if time_sec + time_millis < 0 { + '-' + } else { + '\0' + }, + time_min.abs(), + time_sec.abs(), + time_millis.abs(), length_min, length_sec, length_millis, diff --git a/src/gui/window/top_panel.rs b/src/gui/window/top_panel.rs index bfae1e7..2a42a51 100644 --- a/src/gui/window/top_panel.rs +++ b/src/gui/window/top_panel.rs @@ -1,6 +1,6 @@ use egui::{Context, Frame}; -use std::time::Duration; +use time::Duration; use crate::{ gui::window::GuiWasabiWindow, midi::MIDIFileBase, settings::WasabiSettings, state::WasabiState, @@ -71,14 +71,15 @@ pub fn draw_panel( || ui.add(egui::Slider::new(&mut 0.0, 0.0..=1.0).show_value(false)); if let Some(midi_file) = win.midi_file.as_mut() { if let Some(length) = midi_file.midi_length() { - let mut time = midi_file.timer().get_time().as_secs_f64(); + let mut time = midi_file.timer().get_time().as_seconds_f64(); let time_prev = time; - ui.add(egui::Slider::new(&mut time, 0.0..=length).show_value(false)); + let start_delay = crate::midi::START_DELAY.as_seconds_f64(); + ui.add(egui::Slider::new(&mut time, -start_delay..=length).show_value(false)); if (time_prev != time) && (midi_file.allows_seeking_backward() || time_prev < time) { - midi_file.timer_mut().seek(Duration::from_secs_f64(time)); + midi_file.timer_mut().seek(Duration::seconds_f64(time)); } } else { empty_slider(); diff --git a/src/midi/audio/live.rs b/src/midi/audio/live.rs index 348d922..7812d84 100644 --- a/src/midi/audio/live.rs +++ b/src/midi/audio/live.rs @@ -1,8 +1,8 @@ use std::{ sync::{Arc, RwLock}, thread::{self, JoinHandle}, - time::Duration, }; +use time::Duration; use crossbeam_channel::Receiver; @@ -57,7 +57,7 @@ impl LiveAudioPlayer { match self.timer.wait_until_unpause() { UnpauseWaitResult::Unpaused => push_cc(&event), UnpauseWaitResult::UnpausedAndSeeked(time) => { - if time.as_secs_f64() - event.time > max_fall_time { + if time.as_seconds_f64() - event.time > max_fall_time { seek_catching_up = true; } continue; @@ -67,7 +67,7 @@ impl LiveAudioPlayer { } if seek_catching_up { - let time = self.timer.get_time().as_secs_f64(); + let time = self.timer.get_time().as_seconds_f64(); if time - event.time > max_fall_time { push_cc(&event); continue; @@ -76,7 +76,7 @@ impl LiveAudioPlayer { } } - let time = Duration::from_secs_f64(event.time); + let time = Duration::seconds_f64(event.time); match self.timer.wait_until(time) { WaitResult::Ok => {} WaitResult::Paused => { @@ -84,7 +84,7 @@ impl LiveAudioPlayer { } WaitResult::Seeked(time) => { reset(); - if time.as_secs_f64() - event.time > max_fall_time { + if time.as_seconds_f64() - event.time > max_fall_time { seek_catching_up = true; } continue; diff --git a/src/midi/audio/ram.rs b/src/midi/audio/ram.rs index a6d47a6..1228eb9 100644 --- a/src/midi/audio/ram.rs +++ b/src/midi/audio/ram.rs @@ -1,8 +1,8 @@ use std::{ sync::{Arc, RwLock}, thread::{self, JoinHandle}, - time::Duration, }; +use time::Duration; use crate::{ audio_playback::SimpleTemporaryPlayer, @@ -45,11 +45,11 @@ impl InRamAudioPlayer { reset(); match self.timer.wait_until_unpause() { UnpauseWaitResult::Unpaused => { - self.seek_to_time(self.timer.get_time().as_secs_f64()); + self.seek_to_time(self.timer.get_time().as_seconds_f64()); continue; } UnpauseWaitResult::UnpausedAndSeeked(time) => { - self.seek_to_time(time.as_secs_f64()); + self.seek_to_time(time.as_seconds_f64()); continue; } UnpauseWaitResult::Killed => break, @@ -59,7 +59,7 @@ impl InRamAudioPlayer { if self.index >= self.events.len() { match self.timer.wait_until_seeked() { SeekWaitResult::UnpausedAndSeeked(time) => { - self.seek_to_time(time.as_secs_f64()); + self.seek_to_time(time.as_seconds_f64()); continue; } SeekWaitResult::Killed => break, @@ -68,7 +68,7 @@ impl InRamAudioPlayer { let event = &self.events[self.index]; - let time = Duration::from_secs_f64(event.time); + let time = Duration::seconds_f64(event.time); match self.timer.wait_until(time) { WaitResult::Ok => {} WaitResult::Paused => { @@ -76,7 +76,7 @@ impl InRamAudioPlayer { } WaitResult::Seeked(time) => { reset(); - self.seek_to_time(time.as_secs_f64()); + self.seek_to_time(time.as_seconds_f64()); continue; } WaitResult::Killed => { @@ -93,6 +93,10 @@ impl InRamAudioPlayer { } fn find_time_index(&self, time: f64) -> usize { + if time < 0.0 { + return 0; + } + let events = &self.events; // Binary search to find the right time segment diff --git a/src/midi/cake/blocks.rs b/src/midi/cake/blocks.rs index f159188..275820c 100644 --- a/src/midi/cake/blocks.rs +++ b/src/midi/cake/blocks.rs @@ -45,14 +45,14 @@ impl CakeBlock { }) } } - pub fn get_notes_passed_at(&self, time: u32) -> u32 { + pub fn get_notes_passed_at(&self, time: i32) -> u32 { let mut last_notes_passed; let mut next_index = self.tree[0].length_marker_len(); loop { let node = self.tree[next_index]; - let offset = if time < node.leaf_cutoff() as u32 { + let offset = if time < node.leaf_cutoff() { node.leaf_left() } else { node.leaf_right() diff --git a/src/midi/cake/mod.rs b/src/midi/cake/mod.rs index 0eee50d..0effc44 100644 --- a/src/midi/cake/mod.rs +++ b/src/midi/cake/mod.rs @@ -1,8 +1,8 @@ use std::{ sync::{Arc, RwLock}, thread, - time::Duration, }; +use time::Duration; use midi_toolkit::{ events::{Event, MIDIEventEnum}, @@ -225,8 +225,8 @@ impl MIDIFileBase for CakeMIDIFile { } fn stats(&self) -> MIDIFileStats { - let time = self.timer.get_time().as_secs_f64(); - let time_int = (time * self.ticks_per_second as f64) as u32; + let time = self.timer.get_time().as_seconds_f64(); + let time_int = (time * self.ticks_per_second as f64) as i32; let passed_notes = self .key_blocks() diff --git a/src/midi/live/mod.rs b/src/midi/live/mod.rs index 7d4e4e8..8c46ce8 100644 --- a/src/midi/live/mod.rs +++ b/src/midi/live/mod.rs @@ -114,7 +114,7 @@ impl MIDIFile for LiveLoadMIDIFile { type ColumnsViews<'a> = LiveCurrentNoteViews<'a> where Self: 'a; fn get_current_column_views(&mut self, range: f64) -> Self::ColumnsViews<'_> { - let time = self.timer.get_time().as_secs_f64(); + let time = self.timer.get_time().as_seconds_f64(); let new_range = MIDIViewRange::new(time, time + range); self.view_data.shift_view_range(new_range); diff --git a/src/midi/live/parse.rs b/src/midi/live/parse.rs index c1b3142..f378413 100644 --- a/src/midi/live/parse.rs +++ b/src/midi/live/parse.rs @@ -1,8 +1,8 @@ use std::{ sync::{atomic::Ordering, Arc, RwLock}, thread::{self, JoinHandle}, - time::Duration, }; +use time::Duration; use atomic_float::AtomicF64; use crossbeam_channel::Receiver; @@ -79,7 +79,7 @@ impl LiveMidiParser { } let playback_time = (time - 10.0).max(0.0); // 10 seconds offset - let waited = parser_timer.wait_until(Duration::from_secs_f64(playback_time)); + let waited = parser_timer.wait_until(Duration::seconds_f64(playback_time)); if let WaitResult::Killed = waited { break; } diff --git a/src/midi/live/view.rs b/src/midi/live/view.rs index a28b259..f720a9c 100644 --- a/src/midi/live/view.rs +++ b/src/midi/live/view.rs @@ -32,8 +32,8 @@ impl LiveNoteViewData { parser, columns, view_range: MIDIViewRange { - start: 0.0, - end: 0.0, + start: f64::NEG_INFINITY, + end: f64::NEG_INFINITY, }, default_track_colors: if random_colors { MIDIColor::new_random_vec_for_tracks(track_count) diff --git a/src/midi/mod.rs b/src/midi/mod.rs index 1d72ffd..ebe98d6 100644 --- a/src/midi/mod.rs +++ b/src/midi/mod.rs @@ -17,6 +17,7 @@ use rand::Rng; pub use cake::{blocks::CakeBlock, intvec4::IntVector4, CakeMIDIFile, CakeSignature}; pub use live::LiveLoadMIDIFile; pub use ram::InRamMIDIFile; +pub use shared::timer::START_DELAY; use self::shared::timer::TimeKeeper; diff --git a/src/midi/ram/mod.rs b/src/midi/ram/mod.rs index 37fe508..1801712 100644 --- a/src/midi/ram/mod.rs +++ b/src/midi/ram/mod.rs @@ -57,7 +57,7 @@ impl MIDIFile for InRamMIDIFile { type ColumnsViews<'a> = InRamCurrentNoteViews<'a> where Self: 'a; fn get_current_column_views(&mut self, range: f64) -> Self::ColumnsViews<'_> { - let time = self.timer.get_time().as_secs_f64(); + let time = self.timer.get_time().as_seconds_f64(); let new_range = MIDIViewRange::new(time, time + range); self.view_data.shift_view_range(new_range); diff --git a/src/midi/shared/timer.rs b/src/midi/shared/timer.rs index 5e27ade..1cbd63a 100644 --- a/src/midi/shared/timer.rs +++ b/src/midi/shared/timer.rs @@ -1,6 +1,9 @@ #![allow(dead_code)] -use std::time::{Duration, Instant}; +use std::time::Instant; +use time::Duration; + +pub const START_DELAY: Duration = Duration::seconds(2); struct NotifySignal { new_state: TimerState, @@ -24,8 +27,8 @@ impl TimerState { TimerState::Running { continue_time, time_offset, - } => continue_time.elapsed() + *time_offset, - TimerState::Paused { time_offset } => *time_offset, + } => continue_time.elapsed() + *time_offset - START_DELAY, + TimerState::Paused { time_offset } => *time_offset - START_DELAY, } } @@ -44,7 +47,7 @@ impl TimeKeeper { pub fn new() -> Self { Self { current_state: TimerState::Paused { - time_offset: Duration::new(0, 0), + time_offset: START_DELAY, }, listeners: Vec::new(), } @@ -120,6 +123,7 @@ impl TimeKeeper { } pub fn seek(&mut self, time: Duration) { + let time = time + START_DELAY; if self.current_state.is_paused() { self.current_state = TimerState::Paused { time_offset: time }; } else { @@ -170,7 +174,9 @@ impl TimeListener { } // TODO: Maybe find a more reliable way to wait while still reading? - let result = self.reciever.recv_timeout(time - curr_time); + let result = self + .reciever + .recv_timeout((time - curr_time).unsigned_abs()); match result { Ok(signal) => {