use midir::MidiOutputConnection;
pub use crate::protocols::query::*;
use super::Button;
use crate::OutputDevice;
const MAX_RGB: u8 = 127;
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub struct PaletteColor {
pub(crate) id: u8,
}
impl PaletteColor {
pub fn is_valid(&self) -> bool {
self.id <= 127
}
pub fn new(id: u8) -> Self {
let self_ = Self { id };
assert!(self_.is_valid());
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 {
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());
self_
}
pub fn is_valid(&self) -> bool {
self.r <= MAX_RGB && self.g <= MAX_RGB && self.b <= MAX_RGB
}
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 <= MAX_RGB);
self.r = r
}
pub fn set_green(&mut self, g: u8) {
assert!(g <= MAX_RGB);
self.g = g
}
pub fn set_blue(&mut self, b: u8) {
assert!(b <= MAX_RGB);
self.b = b
}
}
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum ButtonStyle {
Palette {
color: PaletteColor,
},
Rgb {
color: RgbColor,
},
Flash {
color1: PaletteColor,
color2: PaletteColor,
},
Pulse {
color: PaletteColor,
},
}
impl ButtonStyle {
pub fn palette(color: PaletteColor) -> Self {
ButtonStyle::Palette { color }
}
pub fn flash(color: PaletteColor) -> Self {
Self::flash2(color, PaletteColor::BLACK)
}
pub fn flash2(color1: PaletteColor, color2: PaletteColor) -> Self {
ButtonStyle::Flash { color1, color2 }
}
pub fn pulse(color: PaletteColor) -> Self {
ButtonStyle::Pulse { color }
}
pub fn rgb(color: RgbColor) -> Self {
ButtonStyle::Rgb { color }
}
pub fn is_valid(&self) -> bool {
match self {
ButtonStyle::Palette { color } => color.is_valid(),
ButtonStyle::Rgb { color } => color.is_valid(),
ButtonStyle::Flash { color1, color2 } => color1.is_valid() && color2.is_valid(),
ButtonStyle::Pulse { color } => color.is_valid(),
}
}
}
impl From<PaletteColor> for ButtonStyle {
fn from(color: PaletteColor) -> Self {
ButtonStyle::Palette { color }
}
}
impl From<&PaletteColor> for ButtonStyle {
fn from(color: &PaletteColor) -> Self {
Self::from(*color)
}
}
impl From<RgbColor> for ButtonStyle {
fn from(color: RgbColor) -> Self {
ButtonStyle::Rgb { color }
}
}
impl From<&RgbColor> for ButtonStyle {
fn from(color: &RgbColor) -> Self {
Self::from(*color)
}
}
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 LIGHT_RED: PaletteColor = Self { id: 4 };
pub const RED: PaletteColor = Self { id: 5 };
pub const ORANGE: PaletteColor = Self { id: 9 };
pub const YELLOW: PaletteColor = Self { id: 13 };
pub const LIME_GREEN: PaletteColor = Self { id: 17 };
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 PURPLE: PaletteColor = Self { id: 49 };
pub const MAGENTA: PaletteColor = Self { id: 53 };
pub const PINK: PaletteColor = Self { id: 57 };
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,
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum SleepMode {
Sleep = 0,
Wake = 1,
}
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum Layout {
Live = 0, Programmer = 1,
}
impl From<u8> for Layout {
fn from(id: u8) -> Self {
match id {
0 => Self::Live,
1 => Self::Programmer,
_ => panic!("Unexpected layout id {}", id),
}
}
}
pub struct Output {
connection: MidiOutputConnection,
}
impl crate::OutputDevice for Output {
const MIDI_CONNECTION_NAME: &'static str = "Launchy Mini Mk3 output";
const MIDI_DEVICE_KEYWORD: &'static str = "Launchpad Mini MK3 LPMiniMK3 MI";
fn from_connection(connection: MidiOutputConnection) -> Result<Self, crate::MidiError> {
let mut self_ = Self { connection };
self_.change_layout(Layout::Programmer)?;
Ok(self_)
}
fn send(&mut self, bytes: &[u8]) -> Result<(), crate::MidiError> {
self.connection.send(bytes)?;
Ok(())
}
}
impl Output {
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,
};
self.send(&[type_byte, Self::encode_button(button), color.id])
}
pub fn set_buttons<I, T>(&mut self, buttons: I) -> Result<(), crate::MidiError>
where
I: IntoIterator<Item = T>,
T: std::borrow::Borrow<(Button, ButtonStyle)>,
{
let buttons = buttons.into_iter();
assert!(buttons.size_hint().0 <= 81);
let mut bytes = Vec::with_capacity(8 + 5 * buttons.size_hint().1.unwrap_or(40));
bytes.extend(&[240, 0, 32, 41, 2, 13, 3]);
for pair in buttons {
let (button, style) = pair.borrow();
assert!(style.is_valid());
match style {
ButtonStyle::Palette { color } => {
bytes.extend([0, Self::encode_button(*button), color.id()])
}
ButtonStyle::Rgb { color } => bytes.extend([
3,
Self::encode_button(*button),
color.red(),
color.green(),
color.blue(),
]),
ButtonStyle::Flash { color1, color2 } => {
bytes.extend([1, Self::encode_button(*button), color2.id(), color1.id()])
}
ButtonStyle::Pulse { color } => {
bytes.extend([2, Self::encode_button(*button), color.id()])
}
}
}
bytes.push(247);
self.send(&bytes)
}
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,
{
self.set_buttons(
buttons
.into_iter()
.map(|pair| *pair.borrow())
.map(|(button, color)| (button, color.into())),
)
}
pub fn light_columns(
&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(u8, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
self.set_buttons(
buttons
.into_iter()
.map(|pair| *pair.borrow())
.flat_map(|(col, color)| {
column_buttons(col).map(move |button| (button, color.into()))
}),
)
}
pub fn light_rows(
&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(u8, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
self.set_buttons(
buttons
.into_iter()
.map(|pair| *pair.borrow())
.flat_map(|(row, color)| {
row_buttons(row).map(move |button| (button, color.into()))
}),
)
}
pub fn light_all(&mut self, color: PaletteColor) -> Result<(), crate::MidiError> {
let mut buffer = vec![240, 0, 32, 41, 2, 13, 3];
for row in 1..10 {
for column in 1..10 {
buffer.push(0);
buffer.push(row * 10 + column);
buffer.push(color.id);
}
}
buffer.push(247);
self.send(&buffer)
}
pub fn send_clock_tick(&mut self) -> Result<(), crate::MidiError> {
self.send(&[248, 0, 0])
}
pub fn request_device_inquiry(&mut self, query: DeviceIdQuery) -> Result<(), crate::MidiError> {
request_device_inquiry(self, query)
}
pub fn request_sleep_mode(&mut self) -> Result<(), crate::MidiError> {
self.send(&[240, 0, 32, 41, 2, 13, 9, 247])
}
pub fn scroll_text(
&mut self,
text: &[u8],
color: PaletteColor,
speed: u8,
should_loop: bool,
) -> Result<(), crate::MidiError> {
assert!((0..128).contains(&speed));
let bytes = &[
&[
240,
0,
32,
41,
2,
13,
7,
should_loop as u8,
speed,
0,
color.id(),
],
text,
&[247],
]
.concat();
self.send(bytes)
}
pub fn stop_scroll(&mut self) -> Result<(), crate::MidiError> {
self.send(&[240, 0, 32, 41, 2, 13, 7 , 247])
}
pub fn send_sleep(&mut self, sleep_mode: SleepMode) -> Result<(), crate::MidiError> {
self.send(&[240, 0, 32, 41, 2, 13, 9, sleep_mode as u8, 247])
}
pub fn change_layout(&mut self, layout: Layout) -> Result<(), crate::MidiError> {
self.send(&[240, 0, 32, 41, 2, 13, 14, layout as u8, 247])
}
fn encode_button(button: Button) -> u8 {
match button {
Button::GridButton { x, y } => {
assert!(x <= 8);
assert!(y <= 7);
10 * (8 - y) + x + 1
}
Button::ControlButton { index } => {
assert!(index <= 15);
if index <= 7 {
index + 91
} else {
(8 - (index - 8)) * 10 + 9
}
}
}
}
pub fn sleep(&mut self) -> Result<(), crate::MidiError> {
self.send_sleep(SleepMode::Sleep)
}
pub fn wake(&mut self) -> Result<(), crate::MidiError> {
self.send_sleep(SleepMode::Wake)
}
pub fn light(&mut self, button: Button, color: PaletteColor) -> Result<(), crate::MidiError> {
self.set_button(button, color, LightMode::Plain)
}
pub fn flash(&mut self, button: Button, color: PaletteColor) -> Result<(), crate::MidiError> {
self.set_button(button, color, LightMode::Flash)
}
pub fn pulse(&mut self, button: Button, color: PaletteColor) -> Result<(), crate::MidiError> {
self.set_button(button, color, LightMode::Pulse)
}
pub fn light_column(
&mut self,
column: u8,
color: PaletteColor,
) -> Result<(), crate::MidiError> {
self.light_columns([(column, color)])
}
pub fn light_row(&mut self, row: u8, color: PaletteColor) -> Result<(), crate::MidiError> {
self.light_rows([(row, color)])
}
pub fn light_rgb(&mut self, button: Button, color: RgbColor) -> Result<(), crate::MidiError> {
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> {
self.set_buttons(
buttons
.into_iter()
.map(|pair| *pair.borrow())
.map(|(button, color)| (button, color.into())),
)
}
pub fn flash_multiple(
&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(Button, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
self.set_buttons(
buttons
.into_iter()
.map(|pair| *pair.borrow())
.map(|(button, color)| (button, ButtonStyle::flash(color))),
)
}
pub fn pulse_multiple(
&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(Button, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
self.set_buttons(
buttons
.into_iter()
.map(|pair| *pair.borrow())
.map(|(button, color)| (button, ButtonStyle::pulse(color))),
)
}
pub fn set_brightness(&mut self, brightness: u8) -> Result<(), crate::MidiError> {
assert!((0..128).contains(&brightness));
self.send(&[240, 0, 32, 41, 2, 13, 8, brightness, 247])
}
pub fn request_brightness(&mut self) -> Result<(), crate::MidiError> {
self.send(&[240, 0, 32, 41, 2, 13, 8, 247])
}
pub fn clear(&mut self) -> Result<(), crate::MidiError> {
self.light_all(PaletteColor::BLACK)
}
}
fn column_buttons(x: u8) -> impl Iterator<Item = Button> {
(0..8).map(move |y| Button::GridButton { x, y })
}
fn row_buttons(y: u8) -> impl Iterator<Item = Button> {
(0..8)
.map(move |x| Button::GridButton { x, y })
.chain([Button::ControlButton { index: 8 + y }])
}