use midir::{MidiOutput, MidiOutputConnection, MidiInput, MidiInputConnection, MidiInputPort};
fn guess_port<T: midir::MidiIO>(midi_io: &T, keyword: &str) -> Option<T::Port> {
for port in midi_io.ports() {
let name = match midi_io.port_name(&port) {
Ok(name) => name,
Err(_) => continue,
};
if name.contains(keyword) {
return Some(port);
}
}
return None;
}
pub trait OutputDevice where Self: Sized {
const MIDI_CONNECTION_NAME: &'static str;
const MIDI_DEVICE_KEYWORD: &'static str;
fn from_connection(connection: MidiOutputConnection) -> Result<Self, crate::MidiError>;
fn send(&mut self, bytes: &[u8]) -> Result<(), crate::MidiError>;
fn guess() -> Result<Self, crate::MidiError> {
let midi_output = MidiOutput::new(crate::APPLICATION_NAME)?;
let port = guess_port(&midi_output, Self::MIDI_DEVICE_KEYWORD)
.ok_or(crate::MidiError::NoPortFound { keyword: Self::MIDI_DEVICE_KEYWORD })?;
let connection = midi_output.connect(&port, Self::MIDI_CONNECTION_NAME)?;
return Self::from_connection(connection);
}
}
pub struct InputDeviceHandler {
_connection: MidiInputConnection<()>
}
pub struct InputDeviceHandlerPolling<Message> {
_connection: MidiInputConnection<()>,
receiver: std::sync::mpsc::Receiver<Message>,
}
impl<Message> crate::MsgPollingWrapper for InputDeviceHandlerPolling<Message> {
type Message = Message;
fn receiver(&self) -> &std::sync::mpsc::Receiver<Self::Message> { &self.receiver }
}
pub trait InputDevice {
const MIDI_CONNECTION_NAME: &'static str;
const MIDI_DEVICE_KEYWORD: &'static str;
type Message;
fn decode_message(timestamp: u64, data: &[u8]) -> Self::Message;
#[must_use = "If not saved, the connection will be immediately dropped"]
fn from_port<F>(midi_input: MidiInput, port: &MidiInputPort, mut user_callback: F)
-> Result<InputDeviceHandler, crate::MidiError>
where F: FnMut(Self::Message) + Send + 'static {
let midir_callback = move |timestamp: u64, data: &[u8], _: &mut _| {
let msg = Self::decode_message(timestamp, data);
(user_callback)(msg);
};
let connection = midi_input.connect(port, Self::MIDI_CONNECTION_NAME, midir_callback, ())?;
return Ok(InputDeviceHandler { _connection: connection });
}
#[must_use = "If not saved, the connection will be immediately dropped"]
fn from_port_polling(midi_input: MidiInput, port: &MidiInputPort)
-> Result<InputDeviceHandlerPolling<Self::Message>, crate::MidiError>
where Self::Message: Send + 'static {
let (sender, receiver) = std::sync::mpsc::channel();
let midir_callback = move |timestamp: u64, data: &[u8], _: &mut _| {
let msg = Self::decode_message(timestamp, data);
sender.send(msg).expect("Message receiver has hung up (this shouldn't happen)");
};
let connection = midi_input.connect(port, Self::MIDI_CONNECTION_NAME, midir_callback, ())?;
return Ok(InputDeviceHandlerPolling { _connection: connection, receiver });
}
#[must_use = "If not saved, the connection will be immediately dropped"]
fn guess<F>(user_callback: F) -> Result<InputDeviceHandler, crate::MidiError>
where F: FnMut(Self::Message) + Send + 'static {
let midi_input = MidiInput::new(crate::APPLICATION_NAME)?;
let port = guess_port(&midi_input, Self::MIDI_DEVICE_KEYWORD)
.ok_or(crate::MidiError::NoPortFound { keyword: Self::MIDI_DEVICE_KEYWORD })?;
return Self::from_port(midi_input, &port, user_callback);
}
#[must_use = "If not saved, the connection will be immediately dropped"]
fn guess_polling() -> Result<InputDeviceHandlerPolling<Self::Message>, crate::MidiError>
where Self::Message: Send + 'static {
let midi_input = MidiInput::new(crate::APPLICATION_NAME)?;
let port = guess_port(&midi_input, Self::MIDI_DEVICE_KEYWORD)
.ok_or(crate::MidiError::NoPortFound { keyword: Self::MIDI_DEVICE_KEYWORD })?;
return Self::from_port_polling(midi_input, &port);
}
}
pub struct IterFor<'a, M> {
receiver: &'a std::sync::mpsc::Receiver<M>,
deadline: std::time::Instant,
}
impl<M> Iterator for IterFor<'_, M> {
type Item = M;
fn next(&mut self) -> Option<Self::Item> {
let now = std::time::Instant::now();
if now >= self.deadline { return None }
self.receiver.recv_timeout(self.deadline - std::time::Instant::now()).ok()
}
}
pub trait MsgPollingWrapper {
type Message;
fn receiver(&self) -> &std::sync::mpsc::Receiver<Self::Message>;
fn recv(&self) -> Self::Message {
return self.receiver().recv()
.expect("Message sender has hung up - please report a bug");
}
fn try_recv(&self) -> Option<Self::Message> {
use std::sync::mpsc::TryRecvError;
match self.receiver().try_recv() {
Ok(msg) => return Some(msg),
Err(TryRecvError::Empty) => return None,
Err(TryRecvError::Disconnected) => panic!("Message sender has hung up - please report a bug"),
}
}
fn recv_timeout(&self, timeout: std::time::Duration) -> Option<Self::Message> {
use std::sync::mpsc::RecvTimeoutError;
match self.receiver().recv_timeout(timeout) {
Ok(msg) => return Some(msg),
Err(RecvTimeoutError::Timeout) => return None,
Err(RecvTimeoutError::Disconnected) => panic!("Message sender has hung up - please report a bug"),
}
}
fn iter(&self) -> std::sync::mpsc::Iter<Self::Message> {
return self.receiver().iter();
}
fn iter_pending(&self) -> std::sync::mpsc::TryIter<Self::Message> {
return self.receiver().try_iter();
}
fn iter_for(&self, duration: std::time::Duration) -> IterFor<Self::Message> {
IterFor {
receiver: self.receiver(),
deadline: std::time::Instant::now() + duration,
}
}
fn iter_for_millis(&self, millis: u64) -> IterFor<Self::Message> {
self.iter_for(std::time::Duration::from_millis(millis))
}
fn drain(&self) -> usize {
return self.iter_pending().count();
}
}