use crate::launchkey::bitmap::LaunchkeyBitmap;
use crate::launchkey::commands::LaunchkeyCommand;
use crate::launchkey::constants::{LaunchKeySku, DISABLE_DAW_MODE, ENABLE_DAW_MODE};
use crate::launchkey::modes::encoder_mode::EncoderMode;
use crate::launchkey::modes::fader_mode::FaderMode;
use crate::launchkey::modes::pad_mode::PadMode;
use crate::launchkey::surface::display::{Arrangement, GlobalDisplayTarget};
use midir::{MidiOutput, MidiOutputPort};
use std::any::TypeId;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;
use log::{debug, error, info, log};
use crate::launchkey::error::{LaunchkeyError, LaunchkeyInitError, MidiSendError};
use crate::midi::to_hex::ToHexString;
pub trait LaunchKeyState {}
pub struct DAWMode;
impl LaunchKeyState for DAWMode {}
pub struct StandaloneMode;
impl LaunchKeyState for StandaloneMode {}
pub struct LaunchkeyManager<S: LaunchKeyState + 'static> {
conn_out: Rc<RefCell<midir::MidiOutputConnection>>,
sku: LaunchKeySku,
state: PhantomData<S>,
}
impl<S: LaunchKeyState> LaunchkeyManager<S> {
fn _send_command(&mut self, command: LaunchkeyCommand) -> Result<(), MidiSendError> {
match command {
LaunchkeyCommand::SetPadMode(ref pad_mode) => {
match pad_mode {
PadMode::Drum => self._send_command(LaunchkeyCommand::SetDrumDAWMode(false)),
PadMode::DrumDAW => self._send_command(LaunchkeyCommand::SetDrumDAWMode(true)),
_ => Ok(()),
}?;
self._send_bytes(&command.as_bytes(&self.sku))
}
command => self._send_bytes(&command.as_bytes(&self.sku)),
}
}
fn _send_bytes(&mut self, bytes: &[u8]) -> Result<(), MidiSendError> {
debug!("Sending MIDI message: {}", &bytes.to_hex_string());
self.conn_out.borrow_mut().send(bytes)?;
Ok(())
}
}
impl LaunchkeyManager<StandaloneMode> {
pub fn new(
midi_out: MidiOutput,
port: &MidiOutputPort,
sku: LaunchKeySku,
) -> Result<Self, LaunchkeyInitError> {
let conn_out = midi_out
.connect(port, "launchkey-manager")
.map_err(LaunchkeyInitError::MidiConnectError)?;
Ok(Self {
conn_out: Rc::new(RefCell::new(conn_out)),
sku,
state: PhantomData,
})
}
pub fn default() -> Result<Self, LaunchkeyInitError> {
let midi_out = MidiOutput::new("Custom DAW")?;
let ports = midi_out.ports();
if ports.is_empty() {
return Err(LaunchkeyInitError::DeviceNotFound("No MIDI output ports found".to_string()));
}
info!("Looking for 'MIDIOUT2' among available MIDI ports...");
let out_port = ports
.iter()
.find(|port| {
midi_out
.port_name(port)
.unwrap_or_default()
.contains("MIDIOUT2")
})
.ok_or_else(|| LaunchkeyInitError::DeviceNotFound("Could not find MIDIOUT2 port".to_string()))?;
Self::new(midi_out, out_port, LaunchKeySku::Mini)
}
pub fn set_screen_text_global(
&mut self,
target: GlobalDisplayTarget,
arrangement: Arrangement,
) -> Result<(), MidiSendError> {
self._send_command(LaunchkeyCommand::SetScreenTextGlobal {
target,
arrangement,
})
}
pub fn send_screen_bitmap(
&mut self,
target: GlobalDisplayTarget,
bitmap: LaunchkeyBitmap,
) -> Result<(), MidiSendError> {
self._send_command(LaunchkeyCommand::SendScreenBitmap {
target,
bitmap: Box::new(bitmap),
})
}
pub fn into_daw_mode(mut self) -> Result<LaunchkeyManager<DAWMode>, LaunchkeyError> {
self._send_bytes(&ENABLE_DAW_MODE)?;
info!("Transitioned to DAW mode");
Ok(LaunchkeyManager {
conn_out: self.conn_out.clone(),
sku: self.sku.clone(),
state: PhantomData,
})
}
}
impl LaunchkeyManager<DAWMode> {
pub fn into_standalone_mode(
mut self,
) -> Result<LaunchkeyManager<StandaloneMode>, LaunchkeyError> {
self._send_bytes(&DISABLE_DAW_MODE)?;
info!("Transitioned to Standalone mode");
Ok(LaunchkeyManager {
conn_out: self.conn_out.clone(),
sku: self.sku.clone(),
state: PhantomData,
})
}
pub fn setup_default_element_modes(&mut self) -> Result<(), LaunchkeyError> {
self.send_command(LaunchkeyCommand::SetPadMode(PadMode::DAW))?;
self.send_command(LaunchkeyCommand::SetEncoderMode(EncoderMode::Plugin))?;
self.send_command(LaunchkeyCommand::SetFaderMode(FaderMode::Volume))?;
Ok(())
}
pub fn send_command(&mut self, command: LaunchkeyCommand) -> Result<(), MidiSendError> {
debug!("Dispatching command: {:?}", command);
self._send_command(command)
}
pub fn send_commands(&mut self, commands: &[LaunchkeyCommand]) -> Result<(), MidiSendError> {
debug!("Sending {} MIDI commands to Launchkey", commands.len());
for command in commands {
self._send_command((*command).clone())?;
}
Ok(()) }
}
impl<S: LaunchKeyState + 'static> Drop for LaunchkeyManager<S> {
fn drop(&mut self) {
if TypeId::of::<S>() == TypeId::of::<DAWMode>() {
if let Err(err) = self._send_bytes(&DISABLE_DAW_MODE) {
error!("Failed to disable DAW mode during cleanup: {}", err);
}
}
}
}