Skip to content

Commit 6e4e908

Browse files
authored
Merge pull request #29 from ProjectLighthouseCAU/midi-events
Add support for MIDI events
2 parents 6c46c19 + c7fe8f1 commit 6e4e908

File tree

6 files changed

+91
-1
lines changed

6 files changed

+91
-1
lines changed

lighthouse-client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ tracing-subscriber = { version = "0.3", features = ["env-filter", "std"] }
3333
tokio = { version = "1.21", features = ["rt", "rt-multi-thread", "macros", "time"] }
3434
clap = { version = "4.5", features = ["derive", "env"] }
3535
dotenvy = "0.15"
36+
midi-msg = "0.8.0"
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use clap::Parser;
2+
use futures::StreamExt;
3+
use lighthouse_client::{protocol::Authentication, Lighthouse, Result, TokioWebSocket, LIGHTHOUSE_URL};
4+
use lighthouse_protocol::InputEvent;
5+
use midi_msg::MidiMsg;
6+
use tracing::{info, warn};
7+
8+
async fn run(lh: Lighthouse<TokioWebSocket>) -> Result<()> {
9+
info!("Connected to the Lighthouse server");
10+
11+
// Stream input events
12+
let mut stream = lh.stream_input().await?;
13+
while let Some(msg) = stream.next().await {
14+
let event = msg?.payload;
15+
if let InputEvent::Midi(midi) = event {
16+
match MidiMsg::from_midi(&midi.data) {
17+
Ok((msg, _)) => info!("Got MIDI message: {:?}", msg),
18+
Err(e) => warn!("Could not parse MIDI message: {:?}", e),
19+
};
20+
}
21+
}
22+
23+
Ok(())
24+
}
25+
26+
#[derive(Parser)]
27+
struct Args {
28+
/// The username.
29+
#[arg(short, long, env = "LIGHTHOUSE_USER")]
30+
username: String,
31+
/// The API token.
32+
#[arg(short, long, env = "LIGHTHOUSE_TOKEN")]
33+
token: String,
34+
/// The server URL.
35+
#[arg(long, env = "LIGHTHOUSE_URL", default_value = LIGHTHOUSE_URL)]
36+
url: String,
37+
}
38+
39+
#[tokio::main(flavor = "current_thread")]
40+
async fn main() -> Result<()> {
41+
tracing_subscriber::fmt().init();
42+
_ = dotenvy::dotenv();
43+
44+
let args = Args::parse();
45+
let auth = Authentication::new(&args.username, &args.token);
46+
let lh = Lighthouse::connect_with_tokio_to(&args.url, auth).await?;
47+
48+
run(lh).await
49+
}

lighthouse-protocol/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ license.workspace = true
1313
rand = "0.8"
1414
rmpv = { version = "1.0.1", features = ["with-serde"] }
1515
serde = { version = "1.0", features = ["derive"] }
16+
serde_bytes = "0.11.17"
1617
serde_with = "3.4"
1718

1819
[dev-dependencies]

lighthouse-protocol/src/input/input_event.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
22

33
use crate::Direction;
44

5-
use super::{EventSource, GamepadEvent, KeyEvent, MouseEvent, UnknownEvent};
5+
use super::{EventSource, GamepadEvent, KeyEvent, MidiEvent, MouseEvent, UnknownEvent};
66

77
/// A user input event, as generated by the new frontend (LUNA).
88
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
@@ -11,6 +11,7 @@ pub enum InputEvent {
1111
Key(KeyEvent),
1212
Mouse(MouseEvent),
1313
Gamepad(GamepadEvent),
14+
Midi(MidiEvent),
1415
#[serde(untagged)]
1516
Unknown(UnknownEvent),
1617
}
@@ -22,6 +23,7 @@ impl InputEvent {
2223
InputEvent::Key(KeyEvent { source, .. }) => source,
2324
InputEvent::Mouse(MouseEvent { source, .. }) => source,
2425
InputEvent::Gamepad(GamepadEvent { source, .. }) => source,
26+
InputEvent::Midi(MidiEvent { source, .. }) => source,
2527
InputEvent::Unknown(UnknownEvent { source, .. }) => source,
2628
}
2729
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
use serde::{Deserialize, Serialize};
2+
3+
use super::EventSource;
4+
5+
/// A MIDI message event.
6+
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
7+
pub struct MidiEvent {
8+
/// The client identifier. Also unique per MIDI input device.
9+
pub source: EventSource,
10+
/// The binary MIDI message.
11+
///
12+
/// The first byte is a status byte (first/most significant bit = 1), the
13+
/// remaining bytes are data bytes (first/most significant bit = 0).
14+
///
15+
/// To give a simple example, pressing C5 on a MIDI keyboard would generate the
16+
/// following message:
17+
///
18+
/// ```plaintext
19+
/// [0x90, 0x48, 0x64]
20+
/// Ch.1 Note 72 Velocity 100
21+
/// NoteOn i.e. C5
22+
/// ```
23+
///
24+
/// The note values can be looked up online:
25+
///
26+
/// - https://www.phys.unsw.edu.au/jw/notes.html
27+
///
28+
/// Same goes for a full description of the packet structure:
29+
///
30+
/// - https://www.w3.org/TR/webmidi/#terminology
31+
/// - http://www.opensound.com/pguide/midi/midi5.html
32+
/// - https://www.songstuff.com/recording/article/midi-message-format/
33+
#[serde(with = "serde_bytes")]
34+
pub data: Vec<u8>,
35+
}

lighthouse-protocol/src/input/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ mod input_event;
88
mod key_event;
99
mod key_modifiers;
1010
mod legacy_input_event;
11+
mod midi_event;
1112
mod mouse_button;
1213
mod mouse_event;
1314
mod unknown_event;
@@ -22,6 +23,7 @@ pub use input_event::*;
2223
pub use key_event::*;
2324
pub use key_modifiers::*;
2425
pub use legacy_input_event::*;
26+
pub use midi_event::*;
2527
pub use mouse_button::*;
2628
pub use mouse_event::*;
2729
pub use unknown_event::*;

0 commit comments

Comments
 (0)