From 49b7993634ffe30fbd62dd8c40799ca3e4a7b962 Mon Sep 17 00:00:00 2001 From: fwcd Date: Sun, 23 Mar 2025 02:02:13 +0100 Subject: [PATCH 1/7] Add MIDIEvent --- lighthouse-protocol/src/input/midi_event.rs | 30 +++++++++++++++++++++ lighthouse-protocol/src/input/mod.rs | 2 ++ 2 files changed, 32 insertions(+) create mode 100644 lighthouse-protocol/src/input/midi_event.rs diff --git a/lighthouse-protocol/src/input/midi_event.rs b/lighthouse-protocol/src/input/midi_event.rs new file mode 100644 index 0000000..c49002e --- /dev/null +++ b/lighthouse-protocol/src/input/midi_event.rs @@ -0,0 +1,30 @@ +use serde::{Deserialize, Serialize}; + +/// A MIDI message event. +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] +pub struct MIDIEvent { + /// The binary MIDI message. + /// + /// The first byte is a status byte (first/most significant bit = 1), the + /// remaining bytes are data bytes (first/most significant bit = 0). + /// + /// To give a simple example, pressing C5 on a MIDI keyboard would generate the + /// following message: + /// + /// ```plaintext + /// [0x90, 0x48, 0x64] + /// Ch.1 Note 72 Velocity 100 + /// NoteOn i.e. C5 + /// ``` + /// + /// The note values can be looked up online: + /// + /// - https://www.phys.unsw.edu.au/jw/notes.html + /// + /// Same goes for a full description of the packet structure: + /// + /// - https://www.w3.org/TR/webmidi/#terminology + /// - http://www.opensound.com/pguide/midi/midi5.html + /// - https://www.songstuff.com/recording/article/midi-message-format/ + data: Vec, +} diff --git a/lighthouse-protocol/src/input/mod.rs b/lighthouse-protocol/src/input/mod.rs index dd05c64..be60063 100644 --- a/lighthouse-protocol/src/input/mod.rs +++ b/lighthouse-protocol/src/input/mod.rs @@ -8,6 +8,7 @@ mod input_event; mod key_event; mod key_modifiers; mod legacy_input_event; +mod midi_event; mod mouse_button; mod mouse_event; mod unknown_event; @@ -22,6 +23,7 @@ pub use input_event::*; pub use key_event::*; pub use key_modifiers::*; pub use legacy_input_event::*; +pub use midi_event::*; pub use mouse_button::*; pub use mouse_event::*; pub use unknown_event::*; From a830ce66b5398e8154b4cb18a20c8989645a4111 Mon Sep 17 00:00:00 2001 From: fwcd Date: Sun, 23 Mar 2025 02:03:07 +0100 Subject: [PATCH 2/7] Add MIDIEvent.source --- lighthouse-protocol/src/input/input_event.rs | 4 +++- lighthouse-protocol/src/input/midi_event.rs | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lighthouse-protocol/src/input/input_event.rs b/lighthouse-protocol/src/input/input_event.rs index a78fd5f..dec19d1 100644 --- a/lighthouse-protocol/src/input/input_event.rs +++ b/lighthouse-protocol/src/input/input_event.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::Direction; -use super::{EventSource, GamepadEvent, KeyEvent, MouseEvent, UnknownEvent}; +use super::{EventSource, GamepadEvent, KeyEvent, MIDIEvent, MouseEvent, UnknownEvent}; /// A user input event, as generated by the new frontend (LUNA). #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] @@ -11,6 +11,7 @@ pub enum InputEvent { Key(KeyEvent), Mouse(MouseEvent), Gamepad(GamepadEvent), + MIDI(MIDIEvent), #[serde(untagged)] Unknown(UnknownEvent), } @@ -22,6 +23,7 @@ impl InputEvent { InputEvent::Key(KeyEvent { source, .. }) => source, InputEvent::Mouse(MouseEvent { source, .. }) => source, InputEvent::Gamepad(GamepadEvent { source, .. }) => source, + InputEvent::MIDI(MIDIEvent { source, .. }) => source, InputEvent::Unknown(UnknownEvent { source, .. }) => source, } } diff --git a/lighthouse-protocol/src/input/midi_event.rs b/lighthouse-protocol/src/input/midi_event.rs index c49002e..6c5f574 100644 --- a/lighthouse-protocol/src/input/midi_event.rs +++ b/lighthouse-protocol/src/input/midi_event.rs @@ -1,8 +1,12 @@ use serde::{Deserialize, Serialize}; +use super::EventSource; + /// A MIDI message event. #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] pub struct MIDIEvent { + /// The client identifier. Also unique per MIDI input device. + pub source: EventSource, /// The binary MIDI message. /// /// The first byte is a status byte (first/most significant bit = 1), the From 82936e489b6eccdaa806dcadedde257567bc1164 Mon Sep 17 00:00:00 2001 From: fwcd Date: Sun, 23 Mar 2025 02:05:29 +0100 Subject: [PATCH 3/7] Make MIDIEvent.data public --- lighthouse-protocol/src/input/midi_event.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lighthouse-protocol/src/input/midi_event.rs b/lighthouse-protocol/src/input/midi_event.rs index 6c5f574..2d093ee 100644 --- a/lighthouse-protocol/src/input/midi_event.rs +++ b/lighthouse-protocol/src/input/midi_event.rs @@ -30,5 +30,5 @@ pub struct MIDIEvent { /// - https://www.w3.org/TR/webmidi/#terminology /// - http://www.opensound.com/pguide/midi/midi5.html /// - https://www.songstuff.com/recording/article/midi-message-format/ - data: Vec, + pub data: Vec, } From f5b5fea9db9ebf66ea161ca35b9645eea4694695 Mon Sep 17 00:00:00 2001 From: fwcd Date: Sun, 23 Mar 2025 02:08:41 +0100 Subject: [PATCH 4/7] Add example for MIDI events --- lighthouse-client/examples/midi_events.rs | 45 +++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 lighthouse-client/examples/midi_events.rs diff --git a/lighthouse-client/examples/midi_events.rs b/lighthouse-client/examples/midi_events.rs new file mode 100644 index 0000000..268f0a1 --- /dev/null +++ b/lighthouse-client/examples/midi_events.rs @@ -0,0 +1,45 @@ +use clap::Parser; +use futures::StreamExt; +use lighthouse_client::{protocol::Authentication, Lighthouse, Result, TokioWebSocket, LIGHTHOUSE_URL}; +use lighthouse_protocol::InputEvent; +use tracing::info; + +async fn run(lh: Lighthouse) -> Result<()> { + info!("Connected to the Lighthouse server"); + + // Stream input events + let mut stream = lh.stream_input().await?; + while let Some(msg) = stream.next().await { + let event = msg?.payload; + if let InputEvent::MIDI(midi) = event { + info!("Got MIDI event: {:?}", midi); + } + } + + Ok(()) +} + +#[derive(Parser)] +struct Args { + /// The username. + #[arg(short, long, env = "LIGHTHOUSE_USER")] + username: String, + /// The API token. + #[arg(short, long, env = "LIGHTHOUSE_TOKEN")] + token: String, + /// The server URL. + #[arg(long, env = "LIGHTHOUSE_URL", default_value = LIGHTHOUSE_URL)] + url: String, +} + +#[tokio::main(flavor = "current_thread")] +async fn main() -> Result<()> { + tracing_subscriber::fmt().init(); + _ = dotenvy::dotenv(); + + let args = Args::parse(); + let auth = Authentication::new(&args.username, &args.token); + let lh = Lighthouse::connect_with_tokio_to(&args.url, auth).await?; + + run(lh).await +} From f6e82d93c9b3480c1943d1c84d3af13dee68a78e Mon Sep 17 00:00:00 2001 From: fwcd Date: Sun, 23 Mar 2025 02:37:59 +0100 Subject: [PATCH 5/7] Decode MIDI messages in example --- lighthouse-client/Cargo.toml | 1 + lighthouse-client/examples/midi_events.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lighthouse-client/Cargo.toml b/lighthouse-client/Cargo.toml index 1afac61..5c95e41 100644 --- a/lighthouse-client/Cargo.toml +++ b/lighthouse-client/Cargo.toml @@ -33,3 +33,4 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "std"] } tokio = { version = "1.21", features = ["rt", "rt-multi-thread", "macros", "time"] } clap = { version = "4.5", features = ["derive", "env"] } dotenvy = "0.15" +midi-msg = "0.8.0" diff --git a/lighthouse-client/examples/midi_events.rs b/lighthouse-client/examples/midi_events.rs index 268f0a1..497507a 100644 --- a/lighthouse-client/examples/midi_events.rs +++ b/lighthouse-client/examples/midi_events.rs @@ -2,7 +2,8 @@ use clap::Parser; use futures::StreamExt; use lighthouse_client::{protocol::Authentication, Lighthouse, Result, TokioWebSocket, LIGHTHOUSE_URL}; use lighthouse_protocol::InputEvent; -use tracing::info; +use midi_msg::MidiMsg; +use tracing::{info, warn}; async fn run(lh: Lighthouse) -> Result<()> { info!("Connected to the Lighthouse server"); @@ -12,7 +13,10 @@ async fn run(lh: Lighthouse) -> Result<()> { while let Some(msg) = stream.next().await { let event = msg?.payload; if let InputEvent::MIDI(midi) = event { - info!("Got MIDI event: {:?}", midi); + match MidiMsg::from_midi(&midi.data) { + Ok((msg, _)) => info!("Got MIDI message: {:?}", msg), + Err(e) => warn!("Could not parse MIDI message: {:?}", e), + }; } } From b6ac1cf7352d796529467f8f05b6a033f694e8c9 Mon Sep 17 00:00:00 2001 From: fwcd Date: Sun, 23 Mar 2025 02:39:53 +0100 Subject: [PATCH 6/7] Use Rust convention for capitalizing MIDI --- lighthouse-client/examples/midi_events.rs | 2 +- lighthouse-protocol/src/input/input_event.rs | 6 +++--- lighthouse-protocol/src/input/midi_event.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lighthouse-client/examples/midi_events.rs b/lighthouse-client/examples/midi_events.rs index 497507a..07e29d8 100644 --- a/lighthouse-client/examples/midi_events.rs +++ b/lighthouse-client/examples/midi_events.rs @@ -12,7 +12,7 @@ async fn run(lh: Lighthouse) -> Result<()> { let mut stream = lh.stream_input().await?; while let Some(msg) = stream.next().await { let event = msg?.payload; - if let InputEvent::MIDI(midi) = event { + if let InputEvent::Midi(midi) = event { match MidiMsg::from_midi(&midi.data) { Ok((msg, _)) => info!("Got MIDI message: {:?}", msg), Err(e) => warn!("Could not parse MIDI message: {:?}", e), diff --git a/lighthouse-protocol/src/input/input_event.rs b/lighthouse-protocol/src/input/input_event.rs index dec19d1..a871f3b 100644 --- a/lighthouse-protocol/src/input/input_event.rs +++ b/lighthouse-protocol/src/input/input_event.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::Direction; -use super::{EventSource, GamepadEvent, KeyEvent, MIDIEvent, MouseEvent, UnknownEvent}; +use super::{EventSource, GamepadEvent, KeyEvent, MidiEvent, MouseEvent, UnknownEvent}; /// A user input event, as generated by the new frontend (LUNA). #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] @@ -11,7 +11,7 @@ pub enum InputEvent { Key(KeyEvent), Mouse(MouseEvent), Gamepad(GamepadEvent), - MIDI(MIDIEvent), + Midi(MidiEvent), #[serde(untagged)] Unknown(UnknownEvent), } @@ -23,7 +23,7 @@ impl InputEvent { InputEvent::Key(KeyEvent { source, .. }) => source, InputEvent::Mouse(MouseEvent { source, .. }) => source, InputEvent::Gamepad(GamepadEvent { source, .. }) => source, - InputEvent::MIDI(MIDIEvent { source, .. }) => source, + InputEvent::Midi(MidiEvent { source, .. }) => source, InputEvent::Unknown(UnknownEvent { source, .. }) => source, } } diff --git a/lighthouse-protocol/src/input/midi_event.rs b/lighthouse-protocol/src/input/midi_event.rs index 2d093ee..f03479f 100644 --- a/lighthouse-protocol/src/input/midi_event.rs +++ b/lighthouse-protocol/src/input/midi_event.rs @@ -4,7 +4,7 @@ use super::EventSource; /// A MIDI message event. #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] -pub struct MIDIEvent { +pub struct MidiEvent { /// The client identifier. Also unique per MIDI input device. pub source: EventSource, /// The binary MIDI message. From c7fe8f177383c57fb36d67d35554b553c6741c67 Mon Sep 17 00:00:00 2001 From: fwcd Date: Sun, 23 Mar 2025 02:42:14 +0100 Subject: [PATCH 7/7] Use serde_bytes to deserialize MIDI data For some reason this doesn't seem to work without --- lighthouse-protocol/Cargo.toml | 1 + lighthouse-protocol/src/input/midi_event.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lighthouse-protocol/Cargo.toml b/lighthouse-protocol/Cargo.toml index cb2fe5e..17a5875 100644 --- a/lighthouse-protocol/Cargo.toml +++ b/lighthouse-protocol/Cargo.toml @@ -13,6 +13,7 @@ license.workspace = true rand = "0.8" rmpv = { version = "1.0.1", features = ["with-serde"] } serde = { version = "1.0", features = ["derive"] } +serde_bytes = "0.11.17" serde_with = "3.4" [dev-dependencies] diff --git a/lighthouse-protocol/src/input/midi_event.rs b/lighthouse-protocol/src/input/midi_event.rs index f03479f..5197152 100644 --- a/lighthouse-protocol/src/input/midi_event.rs +++ b/lighthouse-protocol/src/input/midi_event.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; use super::EventSource; /// A MIDI message event. -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] +#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] pub struct MidiEvent { /// The client identifier. Also unique per MIDI input device. pub source: EventSource, @@ -30,5 +30,6 @@ pub struct MidiEvent { /// - https://www.w3.org/TR/webmidi/#terminology /// - http://www.opensound.com/pguide/midi/midi5.html /// - https://www.songstuff.com/recording/article/midi-message-format/ + #[serde(with = "serde_bytes")] pub data: Vec, }