From de6dd59e484ce086832c8414ad79e911cae92708 Mon Sep 17 00:00:00 2001
From: Nicole Tietz-Sokolskaya <me@ntietz.com>
Date: Sun, 8 Dec 2024 22:07:14 -0500
Subject: [PATCH] start some ui, refactor midi library a bit

---
 Cargo.toml      |  2 ++
 src/bin/main.rs |  6 +++++-
 src/lib.rs      |  1 +
 src/midi.rs     |  1 +
 src/parser.rs   | 15 +++------------
 src/ui.rs       | 41 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 53 insertions(+), 13 deletions(-)
 create mode 100644 src/ui.rs

diff --git a/Cargo.toml b/Cargo.toml
index d06cbea..3d493c0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -5,6 +5,8 @@ edition = "2021"
 
 [dependencies]
 anyhow = "1.0.86"
+eframe = "0.29.1"
+egui = "0.29.1"
 enigo = { version = "0.2.1", features = ["serde"] }
 hex = "0.4.3"
 midir = "0.10.0"
diff --git a/src/bin/main.rs b/src/bin/main.rs
index 20b9933..78d6c3e 100644
--- a/src/bin/main.rs
+++ b/src/bin/main.rs
@@ -13,6 +13,8 @@ fn main() {
     //enigo.text("echo \"hello world\"").unwrap();
     //enigo.key(Key::Return, Direction::Press).unwrap();
 
+    midi_keys::ui::run();
+
     match run() {
         Ok(_) => {}
         Err(err) => {
@@ -28,7 +30,7 @@ fn run() -> Result<()> {
         Ok(m) => m,
         Err(e) => {
             println!("error: {}", e);
-            return replay_file("assets/windsynth.log");
+            return Ok(());
         }
     };
     let port_name = midi_in.port_name(&midi_device)?;
@@ -49,6 +51,8 @@ fn run() -> Result<()> {
     let mut buf = String::new();
     stdin().read_line(&mut buf)?;
 
+    // TODO
+    let _ = replay_file("assets/windsynth.log");
     Ok(())
 }
 
diff --git a/src/lib.rs b/src/lib.rs
index 535cd1e..7920f79 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,4 @@
 pub mod log;
 pub mod midi;
 pub mod parser;
+pub mod ui;
diff --git a/src/midi.rs b/src/midi.rs
index fe4d0b9..46ffd53 100644
--- a/src/midi.rs
+++ b/src/midi.rs
@@ -26,6 +26,7 @@ pub enum VoiceCategory {
     ProgramChange { value: u8 },
     ChannelPressure { pressure: u8 },
     PitchWheel { value: u16 },
+    Unknown,
 }
 
 #[derive(PartialEq, Eq, Debug, Clone)]
diff --git a/src/parser.rs b/src/parser.rs
index 43b7c20..0b7cadf 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -42,10 +42,7 @@ fn parse_system_common(status_byte: u8, bytes: &[u8]) -> IResult<&[u8], SystemCo
             song_number,
         }),
         0xf6 => Ok((bytes, SystemCommon::TuneRequest)),
-        _ => Err(nom::Err::Error(nom::error::Error {
-            input: bytes,
-            code: nom::error::ErrorKind::Fail,
-        })),
+        _ => Ok((bytes, SystemCommon::Unknown)),
     }
 }
 
@@ -66,7 +63,6 @@ pub fn parse_voice_message(status_byte: u8, remainder: &[u8]) -> IResult<&[u8],
     let category_nibble = 0xf0 & status_byte;
     let channel = 0x0f & status_byte;
 
-    println!("category_nibble = {:#x}", category_nibble);
     let (remainder, category) = match category_nibble {
         0x80 => parse_voice_note(remainder, true)?,
         0x90 => parse_voice_note(remainder, false)?,
@@ -82,12 +78,7 @@ pub fn parse_voice_message(status_byte: u8, remainder: &[u8]) -> IResult<&[u8],
             pressure,
         })?,
         0xe0 => parse_pitch_wheel(remainder)?,
-        _ => {
-            return Err(nom::Err::Error(nom::error::Error {
-                input: remainder,
-                code: nom::error::ErrorKind::Fail,
-            }))
-        }
+        _ => (remainder, VoiceCategory::Unknown),
     };
 
     Ok((remainder, VoiceMessage::new(category, channel)))
@@ -110,7 +101,7 @@ pub fn parse_pitch_wheel(bytes: &[u8]) -> IResult<&[u8], VoiceCategory> {
 
 pub fn parse_system_exclusive(bytes: &[u8]) -> IResult<&[u8], SystemCommon> {
     let (remainder, data) = take_till(is_status_byte)(bytes)?;
-    let (remainder, _) = opt(tag([0xf7]))(remainder)?;
+    let (remainder, _) = tag([0xf7])(remainder)?;
 
     let data: Vec<u8> = data.into();
 
diff --git a/src/ui.rs b/src/ui.rs
new file mode 100644
index 0000000..b97d7a0
--- /dev/null
+++ b/src/ui.rs
@@ -0,0 +1,41 @@
+use std::time::Instant;
+
+/// Launches the UI and runs it until it's done executing.
+pub fn run() {
+    let native_options = eframe::NativeOptions::default();
+    // TODO: don't ignore result
+    let _ = eframe::run_native(
+        "Midi Keys",
+        native_options,
+        Box::new(|cc| Ok(Box::new(MidiKeysApp::new(cc)))),
+    );
+}
+
+struct MidiKeysApp {
+    previous_frame_time: Instant,
+}
+
+impl MidiKeysApp {
+    fn new(_cc: &eframe::CreationContext<'_>) -> Self {
+        // this is where to hook in for customizing eguji, like fonts and visuals.
+
+        let previous_frame_time = Instant::now();
+        MidiKeysApp {
+            previous_frame_time,
+        }
+    }
+}
+
+impl eframe::App for MidiKeysApp {
+    fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
+        let duration = self.previous_frame_time.elapsed().as_secs_f32();
+        self.previous_frame_time = Instant::now();
+
+        egui::CentralPanel::default().show(ctx, |ui| {
+            ui.heading("Hello world");
+
+            let framerate = format!("{:>8.2}", 1. / duration);
+            ui.label(framerate);
+        });
+    }
+}