Last active
June 12, 2020 14:16
-
-
Save FlorentinDUBOIS/2fd3dd570f2dab1f38597dd3839203c5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::convert::{From, TryFrom}; | |
use std::error::Error; | |
use std::fmt; | |
use std::fmt::{Display, Formatter}; | |
use std::sync::{Arc, Mutex}; | |
use std::sync::mpsc::{channel, Receiver}; | |
use std::io; | |
use midir::{MidiInput, MidiInputConnection, MidiOutput, MidiOutputConnection}; | |
#[derive(Clone, Debug, Hash, PartialEq, Eq)] | |
pub enum Kind { | |
NoteOff = 0x80, | |
NoteOn = 0x90, | |
ControllerChange = 0xb0, | |
} | |
impl TryFrom<u8> for Kind { | |
type Error = Box<dyn Error + Sync + Send>; | |
fn try_from(code: u8) -> Result<Self, Self::Error> { | |
match code { | |
0x80 => Ok(Kind::NoteOff), | |
0x90 => Ok(Kind::NoteOn), | |
0xb0 => Ok(Kind::ControllerChange), | |
_ => Err(Box::new(io::Error::new(io::ErrorKind::InvalidInput, format!( | |
"kind should be one of {}, {} or {}, got code {}", | |
0x80, 0x90, 0xb0, code | |
))))?, | |
} | |
} | |
} | |
impl From<Kind> for u8 { | |
fn from(kind: Kind) -> Self { | |
match kind { | |
Kind::NoteOff => 0x80, | |
Kind::NoteOn => 0x90, | |
Kind::ControllerChange => 0xb0, | |
} | |
} | |
} | |
#[derive(Clone, Debug, Hash, PartialEq, Eq)] | |
pub enum KeyEvent { | |
KeyPress = 0x0, | |
KeyDown = 0x7f, | |
} | |
impl TryFrom<u8> for KeyEvent { | |
type Error = Box<dyn Error + Sync + Send>; | |
fn try_from(code: u8) -> Result<Self, Self::Error> { | |
match code { | |
0x0 => Ok(KeyEvent::KeyPress), | |
0x7f => Ok(KeyEvent::KeyDown), | |
_ => Err(Box::new(io::Error::new(io::ErrorKind::InvalidInput,format!( | |
"key event should be one of {} or {}, got code {}", | |
0x0, 0x7f, code | |
))))?, | |
} | |
} | |
} | |
impl From<KeyEvent> for u8 { | |
fn from(event: KeyEvent) -> Self { | |
match event { | |
KeyEvent::KeyPress => 0x0, | |
KeyEvent::KeyDown => 0x7f, | |
} | |
} | |
} | |
#[derive(Clone, Debug, Hash, PartialEq, Eq)] | |
pub enum ControllerKey { | |
CursorUp = 0x68, | |
CursorDown = 0x69, | |
CursorLeft = 0x6a, | |
CursorRight = 0x6b, | |
Session = 0x6c, | |
User1 = 0x6d, | |
User2 = 0x6e, | |
Mixer = 0x6f, | |
} | |
impl TryFrom<u8> for ControllerKey { | |
type Error = Box<dyn Error + Sync + Send>; | |
fn try_from(code: u8) -> Result<Self, Self::Error> { | |
match code { | |
0x68 => Ok(ControllerKey::CursorUp), | |
0x69 => Ok(ControllerKey::CursorDown), | |
0x6a => Ok(ControllerKey::CursorLeft), | |
0x6b => Ok(ControllerKey::CursorRight), | |
0x6c => Ok(ControllerKey::Session), | |
0x6d => Ok(ControllerKey::User1), | |
0x6e => Ok(ControllerKey::User2), | |
0x6f => Ok(ControllerKey::Mixer), | |
_ => Err(Box::new(io::Error::new(io::ErrorKind::InvalidInput,format!( | |
"controller key should be one of {}, {}, {}, {}, {}, {}, {} or {}, got code {}", | |
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, code | |
))))?, | |
} | |
} | |
} | |
impl From<ControllerKey> for u8 { | |
fn from(key: ControllerKey) -> Self { | |
match key { | |
ControllerKey::CursorUp => 0x68, | |
ControllerKey::CursorDown => 0x69, | |
ControllerKey::CursorLeft => 0x6a, | |
ControllerKey::CursorRight => 0x6b, | |
ControllerKey::Session => 0x6c, | |
ControllerKey::User1 => 0x6d, | |
ControllerKey::User2 => 0x6e, | |
ControllerKey::Mixer => 0x6f, | |
} | |
} | |
} | |
#[derive(Clone, Debug, Hash, PartialEq, Eq)] | |
pub enum Key { | |
Volume = 0x8, | |
Pan = 0x18, | |
SendA = 0x28, | |
SendB = 0x38, | |
Stop = 0x48, | |
Mute = 0x58, | |
Solo = 0x68, | |
RecordArm = 0x78, | |
} | |
impl TryFrom<u8> for Key { | |
type Error = Box<dyn Error + Sync + Send>; | |
fn try_from(code: u8) -> Result<Self, Self::Error> { | |
match code { | |
0x8 => Ok(Key::Volume), | |
0x18 => Ok(Key::Pan), | |
0x28 => Ok(Key::SendA), | |
0x38 => Ok(Key::SendB), | |
0x48 => Ok(Key::Stop), | |
0x58 => Ok(Key::Mute), | |
0x68 => Ok(Key::Solo), | |
0x78 => Ok(Key::RecordArm), | |
_ => Err(Box::new(io::Error::new(io::ErrorKind::InvalidInput,format!( | |
"controller key should be one of {}, {}, {}, {}, {}, {}, {} or {}, got code {}", | |
0x8, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, code | |
))))?, | |
} | |
} | |
} | |
impl From<Key> for u8 { | |
fn from(key: Key) -> Self { | |
match key { | |
Key::Volume => 0x8, | |
Key::Pan => 0x18, | |
Key::SendA => 0x28, | |
Key::SendB => 0x38, | |
Key::Stop => 0x48, | |
Key::Mute => 0x58, | |
Key::Solo => 0x68, | |
Key::RecordArm => 0x78, | |
} | |
} | |
} | |
#[derive(Clone, Debug, Hash, PartialEq, Eq)] | |
pub enum Color { | |
Off = 0x0c, | |
RedLow = 0x0d, | |
RedFull = 0x0f, | |
AmberLow = 0x1d, | |
AmberFull = 0x3f, | |
YellowFull = 0x3e, | |
GreenLow = 0x1c, | |
GreenFull = 0x3c, | |
} | |
impl TryFrom<u8> for Color { | |
type Error = Box<dyn Error + Sync + Send>; | |
fn try_from(code: u8) -> Result<Self, Self::Error> { | |
match code { | |
0x0c => Ok(Color::Off), | |
0x0d => Ok(Color::RedLow), | |
0x0f => Ok(Color::RedFull), | |
0x1d => Ok(Color::AmberLow), | |
0x3f => Ok(Color::AmberFull), | |
0x3e => Ok(Color::YellowFull), | |
0x1c => Ok(Color::GreenLow), | |
0x3c => Ok(Color::GreenFull), | |
_ => Err(Box::new(io::Error::new(io::ErrorKind::InvalidInput,format!( | |
"controller key should be one of {}, {}, {}, {}, {}, {}, {} or {}, got code {}", | |
0x0c, 0x0d, 0x0f, 0x1d, 0x3f, 0x3e, 0x1c, 0x3c, code | |
))))?, | |
} | |
} | |
} | |
impl From<Color> for u8 { | |
fn from(color: Color) -> Self { | |
match color { | |
Color::Off => 0x0c, | |
Color::RedLow => 0x0d, | |
Color::RedFull => 0x0f, | |
Color::AmberLow => 0x1d, | |
Color::AmberFull => 0x3f, | |
Color::YellowFull => 0x3e, | |
Color::GreenLow => 0x1c, | |
Color::GreenFull => 0x3c, | |
} | |
} | |
} | |
pub trait Launchpad { | |
type Error; | |
fn reset(&mut self) -> Result<(), Self::Error>; | |
fn controller_change(&mut self, key: ControllerKey, data: u8) -> Result<(), Self::Error>; | |
fn note_on(&mut self, key: u8, data: u8) -> Result<(), Self::Error>; | |
fn note_off(&mut self, key: u8, data: u8) -> Result<(), Self::Error>; | |
fn send(&mut self, kind: u8, key: u8, data: u8) -> Result<(), Self::Error>; | |
fn compute_key(row: u8, column: u8) -> u8; | |
} | |
pub struct Pad { | |
/// Keep the structure even if we do not read it. | |
/// We receive input event from the given function | |
#[allow(dead_code)] | |
input_conn: MidiInputConnection<()>, | |
output_conn: MidiOutputConnection, | |
} | |
impl Launchpad for Pad { | |
type Error = Box<dyn Error + Sync + Send>; | |
fn reset(&mut self) -> Result<(), Self::Error> { | |
Ok(self.send(Kind::ControllerChange.into(), 0x0, 0x0)?) | |
} | |
fn controller_change(&mut self, key: ControllerKey, data: u8) -> Result<(), Self::Error> { | |
Ok(self.send(Kind::ControllerChange.into(), key.into(), data)?) | |
} | |
fn note_on(&mut self, key: u8, data: u8) -> Result<(), Self::Error> { | |
Ok(self.send(Kind::NoteOn.into(), key, data)?) | |
} | |
fn note_off(&mut self, key: u8, data: u8) -> Result<(), Self::Error> { | |
Ok(self.send(Kind::NoteOff.into(), key, data)?) | |
} | |
fn send(&mut self, kind: u8, key: u8, data: u8) -> Result<(), Self::Error> { | |
Ok(self.output_conn.send(&[kind, key, data])?) | |
} | |
fn compute_key(row: u8, column: u8) -> u8 { | |
return 0x10 * row + column; | |
} | |
} | |
impl Pad { | |
pub fn connect(name: &str) -> Result<(Self, Receiver<Vec<u8>>), Box<dyn Error + Sync + Send>> { | |
let (tx, rx) = channel(); | |
let tx = Arc::new(Mutex::new(tx)); | |
let input_device = MidiInput::new(name) | |
.map_err(|err| format!("could not instantiate the midi input, {}", err))?; | |
let output_device = MidiOutput::new(name) | |
.map_err(|err| format!("could not instantiate the midi output, {}", err))?; | |
let input_port = match input_device.port_count() { | |
0 => Err(format!("could not find a port for the midi input device '{}'", name))?, | |
_ => input_device.ports()[0].to_owned(), | |
}; | |
let output_port = match output_device.port_count() { | |
0 => Err(format!("could not find a port for the midi output device '{}'", name))?, | |
_ => output_device.ports()[0].to_owned(), | |
}; | |
let output_conn = output_device.connect(&output_port, name) | |
.map_err(|err| format!("could not connect to the midi output device '{}', {}", name, err))?; | |
let input_conn = input_device.connect(&input_port, name, move |_, signal, _| { | |
let tx = tx.lock().expect("could not lock the signal handler to speak to the midi controller"); | |
tx.send(signal.to_owned()).expect("could not send a signal to the midi controller"); | |
}, ()) | |
.map_err(|err| format!("could not connect to the midi input device '{}'", err))?; | |
Ok(( | |
Self { | |
input_conn, | |
output_conn, | |
}, | |
rx, | |
)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment