use midir::MidiOutputConnection;
use super::Button;
use crate::OutputDevice;
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub struct PaletteColor {
pub(crate) id: u8,
}
impl PaletteColor {
pub fn is_valid(&self) -> bool {
return self.id <= 127;
}
pub fn new(id: u8) -> Self {
let self_ = Self { id };
assert!(self_.is_valid());
return self_;
}
pub fn id(&self) -> u8 { self.id }
pub fn set_id(&mut self, id: u8) { self.id = id }
}
impl From<u8> for PaletteColor {
fn from(id: u8) -> Self {
return Self::new(id);
}
}
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub struct RgbColor {
r: u8,
g: u8,
b: u8,
}
impl RgbColor {
pub fn new(r: u8, g: u8, b: u8) -> Self {
let self_ = Self { r, g, b };
assert!(self_.is_valid());
return self_;
}
pub fn is_valid(&self) -> bool {
return self.r <= 63 && self.g <= 63 && self.b <= 63;
}
pub fn red(&self) -> u8 { self.r }
pub fn green(&self) -> u8 { self.g }
pub fn blue(&self) -> u8 { self.b }
pub fn set_red(&mut self, r: u8) { assert!(r <= 63); self.r = r }
pub fn set_green(&mut self, g: u8) { assert!(g <= 63); self.g = g }
pub fn set_blue(&mut self, b: u8) { assert!(b <= 63); self.b = b }
}
impl PaletteColor {
pub const BLACK: PaletteColor = Self { id: 0 };
pub const DARK_GRAY: PaletteColor = Self { id: 1 };
pub const LIGHT_GRAY: PaletteColor = Self { id: 2 };
pub const WHITE: PaletteColor = Self { id: 3 };
pub const RED: PaletteColor = Self { id: 5 };
pub const YELLOW: PaletteColor = Self { id: 13 };
pub const GREEN: PaletteColor = Self { id: 21 };
pub const SLIGHTLY_LIGHT_GREEN: PaletteColor = Self { id: 29 };
pub const LIGHT_BLUE: PaletteColor = Self { id: 37 };
pub const BLUE: PaletteColor = Self { id: 45 };
pub const MAGENTA: PaletteColor = Self { id: 53 };
pub const BROWN: PaletteColor = Self { id: 61 };
pub const CYAN: PaletteColor = Self { id: 90 };
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum LightMode {
Plain,
Flash,
Pulse,
}
pub enum DeviceIdQuery {
Specific(u8),
Any,
}
#[derive(Copy, Clone, Hash, Eq, PartialEq)]
pub enum FaderType {
Volume,
Pan,
}
pub struct Fader {
index: u8,
color: PaletteColor,
initial_value: u8,
}
impl Fader {
pub fn new(index: u8, color: PaletteColor, initial_value: u8) -> Self {
assert!(initial_value <= 127);
assert!(index <= 7);
return Self { index, color, initial_value };
}
pub fn index(&self) -> u8 { self.index }
pub fn color(&self) -> PaletteColor { self.color }
pub fn initial_value(&self) -> u8 { self.initial_value }
}
#[allow(dead_code)] enum Layout {
Session,
User1, User2,
Reserved, Volume,
Pan,
}
pub struct FaderMode {
output: Output,
fader_type: FaderType,
}
impl FaderMode {
fn new(mut output: Output, fader_type: FaderType) -> Result<Self, crate::MidiError> {
output.change_layout(match fader_type {
FaderType::Volume => Layout::Volume,
FaderType::Pan => Layout::Pan,
})?;
return Ok(Self { output, fader_type });
}
#[must_use="You must use the returned object, or the MIDI connection will be dropped"]
pub fn exit(mut self) -> Result<Output, crate::MidiError> {
self.output.change_layout(Layout::Session)?;
return Ok(self.output);
}
pub fn designate_faders(&mut self, faders: &[Fader]) -> Result<(), crate::MidiError> {
assert!(faders.len() <= 8);
let fader_type = match self.fader_type {
FaderType::Volume => 0,
FaderType::Pan => 1,
};
let mut bytes = Vec::with_capacity(8 + 4 * faders.len());
bytes.extend(&[240, 0, 32, 41, 2, 24, 43]);
for fader in faders {
bytes.extend(&[fader.index, fader_type, fader.color.id(), fader.initial_value]);
}
bytes.push(247);
return self.output.send(&bytes);
}
pub fn set_fader(&mut self, index: u8, value: u8) -> Result<(), crate::MidiError> {
assert!(index <= 7);
assert!(value <= 127);
return self.output.send(&[176, 21 + index, value]);
}
}
pub struct Output {
connection: MidiOutputConnection,
}
impl crate::OutputDevice for Output {
const MIDI_CONNECTION_NAME: &'static str = "Launchy Mk2 output";
const MIDI_DEVICE_KEYWORD: &'static str = "Launchpad MK2";
fn from_connection(connection: MidiOutputConnection) -> Result<Self, crate::MidiError> {
let mut self_ = Self { connection };
self_.change_layout(Layout::Session)?;
return Ok(self_);
}
fn send(&mut self, bytes: &[u8]) -> Result<(), crate::MidiError> {
self.connection.send(bytes)?;
return Ok(());
}
}
impl Output {
pub fn test_api(&mut self) -> Result<(), crate::MidiError> {
self.light_all(PaletteColor::DARK_GRAY)?;
std::thread::sleep(std::time::Duration::from_millis(250));
self.light_all(PaletteColor::BLACK)?;
self.light(Button::ControlButton { index: 0 }, PaletteColor { id: 5 })?;
self.light_rgb(Button::ControlButton { index: 1 }, RgbColor { r: 63, g: 0, b: 63 })?;
self.light(Button::GridButton { x: 0, y: 0 }, PaletteColor { id: 5 })?;
self.light_rgb(Button::GridButton { x: 1, y: 0 }, RgbColor { r: 63, g: 0, b: 63 })?;
self.light_multiple(&[
(Button::GridButton { x: 0, y: 1 }, PaletteColor { id: 18 }),
(Button::GridButton { x: 0, y: 2 }, PaletteColor { id: 18 }),
])?;
self.light_multiple_rgb(&[
(Button::GridButton { x: 0, y: 3 }, RgbColor { r: 63, g: 63, b: 63 }),
(Button::GridButton { x: 0, y: 4 }, RgbColor { r: 63, g: 40, b: 63 }),
])?;
self.flash(Button::GridButton { x: 1, y: 1 }, PaletteColor { id: 5 })?;
self.pulse(Button::GridButton { x: 1, y: 2 }, PaletteColor { id: 9 })?;
self.flash_multiple(&[
(Button::GridButton { x: 2, y: 1 }, PaletteColor { id: 5 }),
(Button::GridButton { x: 2, y: 2 }, PaletteColor { id: 9 }),
])?;
self.pulse_multiple(&[
(Button::GridButton { x: 3, y: 1 }, PaletteColor { id: 5 }),
(Button::GridButton { x: 3, y: 2 }, PaletteColor { id: 9 }),
])?;
self.flash(Button::ControlButton { index: 2 }, PaletteColor { id: 5 })?;
self.pulse(Button::ControlButton { index: 3 }, PaletteColor { id: 9 })?;
self.flash_multiple(&[
(Button::ControlButton { index: 4 }, PaletteColor { id: 5 }),
(Button::ControlButton { index: 5 }, PaletteColor { id: 9 }),
])?;
self.pulse_multiple(&[
(Button::ControlButton { index: 6 }, PaletteColor { id: 5 }),
(Button::ControlButton { index: 7 }, PaletteColor { id: 9 }),
])?;
self.light_rows(&[
(7, PaletteColor { id: 16 }),
(8, PaletteColor { id: 18 }),
])?;
return Ok(());
}
pub fn set_button(&mut self, button: Button, color: PaletteColor, light_mode: LightMode)
-> Result<(), crate::MidiError> {
assert!(color.id <= 127);
let type_byte = match button {
Button::GridButton { .. } => 0x90,
Button::ControlButton { .. } => 0xB0,
} + match light_mode {
LightMode::Plain => 0,
LightMode::Flash => 1,
LightMode::Pulse => 2,
};
return self.send(&[type_byte, Self::encode_button(button), color.id]);
}
pub fn set_buttons(&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(Button, PaletteColor)>>,
light_mode: LightMode
) -> Result<(), crate::MidiError> {
let msg_type_byte = match light_mode {
LightMode::Plain => 10,
LightMode::Flash => 35,
LightMode::Pulse => 40,
};
let add_null_byte = match light_mode {
LightMode::Plain => false,
LightMode::Flash | LightMode::Pulse => true,
};
return self.send_multiple(msg_type_byte, add_null_byte, 80, buttons.into_iter()
.map(|pair| {
let &(button, color) = pair.borrow();
(Self::encode_button(button), color)
}));
}
pub fn light_multiple_rgb<I, T>(&mut self,
buttons: I,
) -> Result<(), crate::MidiError>
where I: IntoIterator<Item = T>,
T: std::borrow::Borrow<(Button, RgbColor)>,
I::IntoIter: ExactSizeIterator {
let buttons = buttons.into_iter();
assert!(buttons.size_hint().0 <= 80);
let mut bytes = Vec::with_capacity(8 + 12 * buttons.len());
bytes.extend(&[240, 0, 32, 41, 2, 24, 11]);
for pair in buttons {
let &(button, color) = pair.borrow();
assert!(color.is_valid());
bytes.extend(&[Self::encode_button(button), color.r, color.g, color.b]);
}
bytes.push(247);
return self.send(&bytes);
}
pub fn light_columns(&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(u8, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
return self.send_multiple(12, false, 9, buttons);
}
pub fn light_rows(&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(u8, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
return self.send_multiple(13, false, 9, buttons.into_iter()
.map(|pair| {
let &(row, color) = pair.borrow();
(8 - row, color)
}));
}
pub fn light_all(&mut self, color: PaletteColor) -> Result<(), crate::MidiError> {
return self.send(&[240, 0, 32, 41, 2, 24, 14, color.id, 247]);
}
pub fn send_clock_tick(&mut self) -> Result<(), crate::MidiError> {
return self.send(&[248, 0, 0]);
}
pub fn request_device_inquiry(&mut self, query: DeviceIdQuery) -> Result<(), crate::MidiError> {
const QUERY_DEVICE_ID_FOR_ANY: u8 = 127;
let query_device_id = match query {
DeviceIdQuery::Specific(device_id) => {
assert_ne!(device_id, QUERY_DEVICE_ID_FOR_ANY);
device_id
},
DeviceIdQuery::Any => QUERY_DEVICE_ID_FOR_ANY,
};
return self.send(&[240, 126, query_device_id, 6, 1, 247]);
}
pub fn request_version_inquiry(&mut self) -> Result<(), crate::MidiError> {
return self.send(&[240, 0, 32, 41, 0, 112, 247]);
}
pub fn scroll_text(&mut self, text: &[u8], color: PaletteColor, should_loop: bool)
-> Result<(), crate::MidiError> {
let bytes = &[
&[240, 0, 32, 41, 2, 24, 20, color.id(), should_loop as u8],
text,
&[247],
].concat();
return self.send(bytes);
}
#[must_use="If you don't use the returned object, the MIDI connection will be dropped immediately"]
pub fn enter_fader_mode(self, fader_type: FaderType) -> Result<FaderMode, crate::MidiError> {
return FaderMode::new(self, fader_type);
}
pub fn enter_bootloader(&mut self) -> Result<(), crate::MidiError> {
return self.send(&[240, 0, 32, 41, 0, 113, 0, 105, 247]);
}
fn change_layout(&mut self, layout: Layout) -> Result<(), crate::MidiError> {
let layout = match layout {
Layout::Session => 0,
Layout::User1 => 1,
Layout::User2 => 2,
Layout::Reserved => 3,
Layout::Volume => 4,
Layout::Pan => 5,
};
return self.send(&[240, 0, 32, 41, 2, 24, 34, layout, 247]);
}
fn send_multiple(&mut self,
msg_type_byte: u8,
insert_null_bytes: bool,
max_packets: usize,
pair_iterator: impl IntoIterator<Item = impl std::borrow::Borrow<(u8, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
let pair_iterator = pair_iterator.into_iter();
let capacity = 8 + 12 * (pair_iterator.size_hint().0 + insert_null_bytes as usize);
let mut bytes = Vec::with_capacity(capacity);
bytes.extend(&[240, 0, 32, 41, 2, 24, msg_type_byte]);
for (i, pair) in pair_iterator.enumerate() {
if i >= max_packets {
panic!("Only {} or less elements are supported per message!", max_packets);
}
let &(button_specifier, color) = pair.borrow();
if insert_null_bytes { bytes.push(0) }
bytes.extend(&[button_specifier, color.id]);
}
bytes.push(247);
return self.send(&bytes);
}
fn encode_button(button: Button) -> u8 {
match button {
Button::GridButton { x, y } => {
assert!(x <= 8);
assert!(y <= 7);
return 10 * (8 - y) + x + 1;
},
Button::ControlButton { index } => {
assert!(index <= 7);
return index + 104;
}
}
}
pub fn light(&mut self, button: Button, color: PaletteColor) -> Result<(), crate::MidiError> {
return self.set_button(button, color, LightMode::Plain);
}
pub fn flash(&mut self, button: Button, color: PaletteColor) -> Result<(), crate::MidiError> {
return self.set_button(button, color, LightMode::Flash);
}
pub fn pulse(&mut self, button: Button, color: PaletteColor) -> Result<(), crate::MidiError> {
return self.set_button(button, color, LightMode::Pulse);
}
pub fn light_column(&mut self, column: u8, color: PaletteColor)
-> Result<(), crate::MidiError> {
return self.light_columns(&[(column, color)]);
}
pub fn light_row(&mut self, row: u8, color: PaletteColor)
-> Result<(), crate::MidiError> {
return self.light_rows(&[(row, color)]);
}
pub fn light_rgb(&mut self, button: Button, color: RgbColor) -> Result<(), crate::MidiError> {
return self.light_multiple_rgb(&[(button, color)]);
}
pub fn light_multiple(&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(Button, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
return self.set_buttons(buttons, LightMode::Plain);
}
pub fn flash_multiple(&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(Button, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
return self.set_buttons(buttons, LightMode::Flash);
}
pub fn pulse_multiple(&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(Button, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
return self.set_buttons(buttons, LightMode::Pulse);
}
pub fn clear(&mut self) -> Result<(), crate::MidiError> {
return self.light_all(PaletteColor::BLACK);
}
}