use midir::MidiOutputConnection;
pub use crate::protocols::query::*;
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 {
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 <= 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,
}
#[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);
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,
})?;
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)?;
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);
self.output.send(&bytes)
}
pub fn set_fader(&mut self, index: u8, value: u8) -> Result<(), crate::MidiError> {
assert!(index <= 7);
assert!(value <= 127);
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)?;
Ok(self_)
}
fn send(&mut self, bytes: &[u8]) -> Result<(), crate::MidiError> {
self.connection.send(bytes)?;
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 })])?;
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,
};
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,
};
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);
self.send(&bytes)
}
pub fn light_columns(
&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(u8, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
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> {
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> {
self.send(&[240, 0, 32, 41, 2, 24, 14, color.id, 247])
}
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_version_inquiry(&mut self) -> Result<(), crate::MidiError> {
request_version_inquiry(self)
}
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();
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> {
FaderMode::new(self, fader_type)
}
pub fn enter_bootloader(&mut self) -> Result<(), crate::MidiError> {
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,
};
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);
self.send(&bytes)
}
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 <= 7);
index + 104
}
}
}
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, LightMode::Plain)
}
pub fn flash_multiple(
&mut self,
buttons: impl IntoIterator<Item = impl std::borrow::Borrow<(Button, PaletteColor)>>,
) -> Result<(), crate::MidiError> {
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> {
self.set_buttons(buttons, LightMode::Pulse)
}
pub fn clear(&mut self) -> Result<(), crate::MidiError> {
self.light_all(PaletteColor::BLACK)
}
}