EIS-BLE-S3/cue/src/ble.rs

94 lines
2.8 KiB
Rust

use midir::{MidiInput, MidiOutput, MidiInputConnection, MidiOutputConnection};
use std::sync::mpsc as std_mpsc;
use tokio::sync::mpsc;
use crate::protocol::{self, EisMessage};
const DEVICE_NAME: &str = "EIS4";
#[derive(Debug, Clone)]
pub enum BleEvent {
Status(String),
Data(EisMessage),
}
pub async fn connect_and_run(
tx: mpsc::UnboundedSender<BleEvent>,
mut cmd_rx: mpsc::UnboundedReceiver<Vec<u8>>,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let _ = tx.send(BleEvent::Status("Looking for MIDI device...".into()));
let (midi_in, in_port, midi_out, out_port) = loop {
if let Some(found) = find_midi_ports() {
break found;
}
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
};
let _ = tx.send(BleEvent::Status("Connecting MIDI...".into()));
let (sysex_tx, sysex_rx) = std_mpsc::channel::<Vec<u8>>();
let _in_conn: MidiInputConnection<()> = midi_in.connect(
&in_port, "cue-in",
move |_ts, data, _| {
if let Some(sysex) = extract_sysex(data) {
let _ = sysex_tx.send(sysex);
}
},
(),
).map_err(|e| format!("MIDI input connect: {e}"))?;
let mut out_conn: MidiOutputConnection = midi_out.connect(
&out_port, "cue-out",
).map_err(|e| format!("MIDI output connect: {e}"))?;
let _ = tx.send(BleEvent::Status("Connected".into()));
loop {
while let Ok(sysex) = sysex_rx.try_recv() {
if let Some(msg) = protocol::parse_sysex(&sysex) {
let _ = tx.send(BleEvent::Data(msg));
}
}
loop {
match cmd_rx.try_recv() {
Ok(pkt) => {
if let Err(e) = out_conn.send(&pkt) {
eprintln!("MIDI send error: {e}");
}
}
Err(mpsc::error::TryRecvError::Disconnected) => return Ok(()),
Err(mpsc::error::TryRecvError::Empty) => break,
}
}
tokio::time::sleep(std::time::Duration::from_millis(5)).await;
}
}
fn find_midi_ports() -> Option<(
MidiInput, midir::MidiInputPort,
MidiOutput, midir::MidiOutputPort,
)> {
let midi_in = MidiInput::new("cue-in").ok()?;
let midi_out = MidiOutput::new("cue-out").ok()?;
let in_port = midi_in.ports().into_iter().find(|p| {
midi_in.port_name(p).map_or(false, |n| n.contains(DEVICE_NAME))
})?;
let out_port = midi_out.ports().into_iter().find(|p| {
midi_out.port_name(p).map_or(false, |n| n.contains(DEVICE_NAME))
})?;
Some((midi_in, in_port, midi_out, out_port))
}
fn extract_sysex(data: &[u8]) -> Option<Vec<u8>> {
if data.first() != Some(&0xF0) { return None; }
let end = data.iter().position(|&b| b == 0xF7)?;
Some(data[1..end].to_vec())
}