launchkey_sdk/launchkey/
manager.rs1use crate::launchkey::bitmap::LaunchkeyBitmap;
2use crate::launchkey::commands::LaunchkeyCommand;
3use crate::launchkey::constants::{LaunchKeySku, DISABLE_DAW_MODE, ENABLE_DAW_MODE};
4use crate::launchkey::modes::encoder_mode::EncoderMode;
5use crate::launchkey::modes::fader_mode::FaderMode;
6use crate::launchkey::modes::pad_mode::PadMode;
7use crate::launchkey::surface::display::{Arrangement, GlobalDisplayTarget};
8use midir::{MidiOutput, MidiOutputPort};
9use std::any::TypeId;
10use std::cell::RefCell;
11use std::marker::PhantomData;
12use std::rc::Rc;
13use log::{debug, error, info, log};
14use crate::launchkey::error::{LaunchkeyError, LaunchkeyInitError, MidiSendError};
15use crate::midi::to_hex::ToHexString;
16
17pub trait LaunchKeyState {}
19
20pub struct DAWMode;
22impl LaunchKeyState for DAWMode {}
23
24pub struct StandaloneMode;
26impl LaunchKeyState for StandaloneMode {}
27
28pub struct LaunchkeyManager<S: LaunchKeyState + 'static> {
31 conn_out: Rc<RefCell<midir::MidiOutputConnection>>,
32 sku: LaunchKeySku,
33 state: PhantomData<S>,
34}
35
36impl<S: LaunchKeyState> LaunchkeyManager<S> {
37 fn _send_command(&mut self, command: LaunchkeyCommand) -> Result<(), MidiSendError> {
39 match command {
40 LaunchkeyCommand::SetPadMode(ref pad_mode) => {
41 match pad_mode {
42 PadMode::Drum => self._send_command(LaunchkeyCommand::SetDrumDAWMode(false)),
43 PadMode::DrumDAW => self._send_command(LaunchkeyCommand::SetDrumDAWMode(true)),
44 _ => Ok(()),
45 }?;
46 self._send_bytes(&command.as_bytes(&self.sku))
47 }
48 command => self._send_bytes(&command.as_bytes(&self.sku)),
49 }
50 }
51
52 fn _send_bytes(&mut self, bytes: &[u8]) -> Result<(), MidiSendError> {
54 debug!("Sending MIDI message: {}", &bytes.to_hex_string());
55 self.conn_out.borrow_mut().send(bytes)?;
56 Ok(())
57 }
58}
59
60impl LaunchkeyManager<StandaloneMode> {
61 pub fn new(
63 midi_out: MidiOutput,
64 port: &MidiOutputPort,
65 sku: LaunchKeySku,
66 ) -> Result<Self, LaunchkeyInitError> {
67 let conn_out = midi_out
68 .connect(port, "launchkey-manager")
69 .map_err(LaunchkeyInitError::MidiConnectError)?;
70 Ok(Self {
71 conn_out: Rc::new(RefCell::new(conn_out)),
72 sku,
73 state: PhantomData,
74 })
75 }
76
77 pub fn default() -> Result<Self, LaunchkeyInitError> {
79 let midi_out = MidiOutput::new("Custom DAW")?;
81
82 let ports = midi_out.ports();
83 if ports.is_empty() {
84 return Err(LaunchkeyInitError::DeviceNotFound("No MIDI output ports found".to_string()));
85 }
86
87 info!("Looking for 'MIDIOUT2' among available MIDI ports...");
89 let out_port = ports
90 .iter()
91 .find(|port| {
92 midi_out
93 .port_name(port)
94 .unwrap_or_default()
95 .contains("MIDIOUT2")
96 })
97 .ok_or_else(|| LaunchkeyInitError::DeviceNotFound("Could not find MIDIOUT2 port".to_string()))?;
98
99 Self::new(midi_out, out_port, LaunchKeySku::Mini)
100 }
101
102 pub fn set_screen_text_global(
104 &mut self,
105 target: GlobalDisplayTarget,
106 arrangement: Arrangement,
107 ) -> Result<(), MidiSendError> {
108 self._send_command(LaunchkeyCommand::SetScreenTextGlobal {
109 target,
110 arrangement,
111 })
112 }
113
114 pub fn send_screen_bitmap(
116 &mut self,
117 target: GlobalDisplayTarget,
118 bitmap: LaunchkeyBitmap,
119 ) -> Result<(), MidiSendError> {
120 self._send_command(LaunchkeyCommand::SendScreenBitmap {
121 target,
122 bitmap: Box::new(bitmap),
123 })
124 }
125
126 pub fn into_daw_mode(mut self) -> Result<LaunchkeyManager<DAWMode>, LaunchkeyError> {
128 self._send_bytes(&ENABLE_DAW_MODE)?;
129 info!("Transitioned to DAW mode");
130 Ok(LaunchkeyManager {
131 conn_out: self.conn_out.clone(),
132 sku: self.sku.clone(),
133 state: PhantomData,
134 })
135 }
136}
137
138impl LaunchkeyManager<DAWMode> {
139 pub fn into_standalone_mode(
141 mut self,
142 ) -> Result<LaunchkeyManager<StandaloneMode>, LaunchkeyError> {
143 self._send_bytes(&DISABLE_DAW_MODE)?;
144 info!("Transitioned to Standalone mode");
145 Ok(LaunchkeyManager {
146 conn_out: self.conn_out.clone(),
147 sku: self.sku.clone(),
148 state: PhantomData,
149 })
150 }
151
152 pub fn setup_default_element_modes(&mut self) -> Result<(), LaunchkeyError> {
154 self.send_command(LaunchkeyCommand::SetPadMode(PadMode::DAW))?;
155 self.send_command(LaunchkeyCommand::SetEncoderMode(EncoderMode::Plugin))?;
156 self.send_command(LaunchkeyCommand::SetFaderMode(FaderMode::Volume))?;
157
158 Ok(())
159 }
160
161 pub fn send_command(&mut self, command: LaunchkeyCommand) -> Result<(), MidiSendError> {
163 debug!("Dispatching command: {:?}", command);
164 self._send_command(command)
165 }
166
167 pub fn send_commands(&mut self, commands: &[LaunchkeyCommand]) -> Result<(), MidiSendError> {
169 debug!("Sending {} MIDI commands to Launchkey", commands.len());
170 for command in commands {
171 self._send_command((*command).clone())?;
172 }
173 Ok(()) }
175}
176
177impl<S: LaunchKeyState + 'static> Drop for LaunchkeyManager<S> {
178 fn drop(&mut self) {
179 if TypeId::of::<S>() == TypeId::of::<DAWMode>() {
181 if let Err(err) = self._send_bytes(&DISABLE_DAW_MODE) {
182 error!("Failed to disable DAW mode during cleanup: {}", err);
183 }
184 }
185 }
186}