Skip to content

Instantly share code, notes, and snippets.

@FlorentinDUBOIS
Last active June 12, 2020 14:16
Show Gist options
  • Save FlorentinDUBOIS/2fd3dd570f2dab1f38597dd3839203c5 to your computer and use it in GitHub Desktop.
Save FlorentinDUBOIS/2fd3dd570f2dab1f38597dd3839203c5 to your computer and use it in GitHub Desktop.
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