diff --git a/Cargo.lock b/Cargo.lock index 9d11419..c164f7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -599,6 +599,7 @@ dependencies = [ "serde", "serde_json", "simple_logger", + "time", ] [[package]] @@ -1273,9 +1274,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.34" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8248b6521bb14bc45b4067159b9b6ad792e2d6d754d6c41fb50e29fefe38749" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", @@ -1296,9 +1297,9 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba3a3ef41e6672a2f0f001392bb5dcd3ff0a9992d618ca761a11c3121547774" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ "num-conv", "time-core", diff --git a/Cargo.toml b/Cargo.toml index 2f9a508..bae43bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,12 +2,12 @@ name = "macroix" version = "0.1.0" edition = "2021" +rust-version = "1.80" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] confy = "0.5.1" -# egui-macroquad = { version = "0.15.0", default_features = false } kira = "0.8.5" log = "0.4.21" macroquad = { version = "0.4.4", default_features = false } @@ -15,6 +15,8 @@ midir = "0.9.1" serde = { version = "1.0.193", features = ["derive"] } serde_json = "1.0.108" simple_logger = "4.3.3" +# fix build error +time = "0.3.36" # From README: # > Adding the following snippet to your Cargo.toml ensures that all dependencies compile in release even in debug mode. diff --git a/TODO.md b/TODO.md index 4b3abec..08d68ea 100644 --- a/TODO.md +++ b/TODO.md @@ -6,10 +6,6 @@ - tweak strictness .. just a lil more generous on timing? ... Dial it it! Could add sliders for (1) num correct (2) Bpm step (3) Correctness sensitivity -## asap - -(_what's must-have to make it useful to me? what's very important to make it engaging to me?_) - **Golden mode so I can jam with it** - [ ] make gold reachable @@ -18,23 +14,14 @@ **Look good for energizing dev** -- [ ] Make UI look nice-ish - - [ ] do a pass in Figma - - [ ] better font - - [ ] funklet inspired colors and look - - [ ] show/hide noisy stuff - ## soon - [ ] quick start + gets you into flow - idea: saves whatever loop, BPM you were doing last time -- recovers on next start -- capture progress over time (graph it, etc) + - capture progress over time (graph it, etc) - [..] add better debugging for midi signals, so I can filter to important ones (e.g. can ignore polyphonic aftertouch 167 on changing HH pedal in terms of hitting notes on the beat) - - can translate to names from here https://midi.org/expanded-midi-1-0-messages-list, then log better - proximate reason.. to figure out problem with closed HH not triggering - -- commit UI prototypes (tauri, iced) to Github (optionally, migrate into macroix repo if reasonable to centralize) - (bug) explore triggering - [ ] double triggering of some TD17 notes (e.g. 2x hihat hits or 2x open hihat hits, esp on hard hits?) - [ ] non triggering (hit too soft? event getting dropped?) @@ -60,6 +47,12 @@ - [x] e.g. hacky is a button to reset -> press "r" - another idea is "fade out" by age (e.g. just keep last K loops, or actually fade over time until gone by Kth loop) - UX v2: Design + - commit UI prototypes (tauri, iced) to Github (optionally, migrate into macroix repo if reasonable to centralize) + - [ ] Make UI look nice-ish + - [ ] do a pass in Figma + - [ ] better font + - [ ] funklet inspired colors and look + - [ ] show/hide noisy stuff - prototype in Figma: core interactions, colors, layout, etc - might we show music as sheet music notation (e.g. https://github.com/jaredforth/lilypond-rs or various others) - Explore similar existing offerings @@ -91,7 +84,7 @@ - [ ] fix BPM - assuming each beat in grid is a 16th note, it should be BPM \* 2 (so 120 = 60) - I think ideally the data model for user_hits and desired_hits aligns nicely, i.e. 1.0 is beat 1, 2.0 is beat 2. So e.g. 16th notes are 0.25 in length - - [ ] Refactor message passing .. should be typed (see `main.rs` in `rx.try_recv`) + - [x] Refactor message passing .. should be typed (see `main.rs` in `rx.try_recv`) - [ ] unit tests - consider + document which pieces can be unit tested (and iterated on more effectively than manual testing) - ex. write unit tests re: the accuracy summary metric @@ -126,8 +119,6 @@ - [x] support >1 midi value per voice - [ ] allow easy rebinding within the app - [ ] save calibrated offset (latency) config per connected midi device / system (TD17 = -0.01) .. i have multiple for testing -- Feature: Metronome - - [ ] toggle metronome on/off - Feature: Volume control - [ ] global - [ ] per voice (inl metronome) @@ -218,6 +209,8 @@ ## done +- Feature: Metronome + - [x] toggle metronome on/off - [x] log levels that allow easy filtering - [x] (bug) GOLDEN MODE logic is broken. Denominator seems to be correct user_hits instead of desired notes - [x] refactor so i don't need explicit branches for each of 4 instruments everywhere.. diff --git a/src/audio.rs b/src/audio.rs index b71602d..20c0646 100644 --- a/src/audio.rs +++ b/src/audio.rs @@ -44,6 +44,7 @@ pub struct Audio { clock: ClockHandle, last_scheduled_tick: f64, bpm: f64, + metronome_enabled: bool, pub user_hits: Vec, calibration_input: VecDeque, @@ -75,6 +76,7 @@ impl Audio { clock, last_scheduled_tick: -1., bpm: DEFAULT_BPM, + metronome_enabled: false, user_hits: vec![], calibration_input: VecDeque::new(), @@ -142,8 +144,21 @@ impl Audio { .await?; } - // if metronome on, schedule it - // (&voices.metronome, "res/sounds/click.wav"), + if self.is_metronome_enabled() { + // TODO: play a different sound at start of each measure + // clicks on quarter notes + let metronome_notes = vec![0., 2., 4., 6., 8., 10., 12., 14.]; + let sound_path = "res/sounds/click.wav"; + schedule_audio( + &metronome_notes, + sound_path, + &mut self.manager, + &self.clock, + self.last_scheduled_tick, + tick_to_schedule, + ) + .await?; + } self.last_scheduled_tick = tick_to_schedule; @@ -185,6 +200,14 @@ impl Audio { } } + pub fn toggle_metronome(self: &mut Self) { + self.metronome_enabled = !self.metronome_enabled; + } + + pub fn is_metronome_enabled(self: &Self) -> bool { + self.metronome_enabled + } + /// saves a user's hits, so they can be displayed and checked for accuracy pub fn track_user_hit(self: &mut Self, instrument: Instrument, processing_delay_s: f64) { // convert processing delay to ticks, based on BPM diff --git a/src/ui.rs b/src/ui.rs index eb36a3f..8166a61 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -80,6 +80,8 @@ impl UI { draw_gold_mode(gold_mode); + draw_metronome(audio); + if flags.ui_debug_mode { draw_debug_grid(); } @@ -405,6 +407,21 @@ fn draw_loop_choices<'a, 'b: 'a>( }); } +fn draw_metronome<'a>(audio: &'a mut Audio) { + widgets::Window::new(hash!(), UI_TOP_LEFT + vec2(0., 200.), vec2(320., 200.)) + .label("Metronome") + .titlebar(true) + .ui(&mut *root_ui(), |ui| { + if ui.button( + None, + format!("Metronome {:?}", audio.is_metronome_enabled()), + ) { + log::info!("toggle metronome"); + audio.toggle_metronome(); + } + }); +} + fn draw_gold_mode(gold_mode: &GoldMode) { draw_text( format!(