1use super::AssetTrait;
2use serde::{Deserialize, Serialize};
3
4use crate::asset::scene::color::SceneInstanceColor;
5
6#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
7pub struct MidiController {
8 pub mapping: MidiControllerMapping,
9}
10
11impl AssetTrait for MidiController {
12 const DIR_NAME: &'static str = "midi_controllers";
13 const NAME: &'static str = "MIDI Controller";
14 const SHOW_NAME_IF_SELECTED: bool = true;
15}
16
17#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
18pub struct MidiControllerMapping {
19 pub input_bindings: Vec<MidiInputBinding>,
20 pub output_bindings: Vec<MidiOutputBinding>,
21 pub color_mappings: Vec<MidiNamedSceneColorMapping>,
22}
23
24#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
25pub struct MidiInputBinding {
26 pub name: String,
27 pub trigger: MidiTrigger,
28 pub action: MidiInputAction,
29}
30
31impl Default for MidiInputBinding {
32 fn default() -> Self {
33 Self {
34 name: "Input".to_owned(),
35 trigger: MidiTrigger::default(),
36 action: MidiInputAction::Tap,
37 }
38 }
39}
40
41#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
42pub struct MidiTrigger {
43 pub status: u8,
44 pub data1: u8,
45 pub match_data1: bool,
46}
47
48impl Default for MidiTrigger {
49 fn default() -> Self {
50 Self {
51 status: 0,
52 data1: 0,
53 match_data1: true,
54 }
55 }
56}
57
58#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
59pub enum MidiInputAction {
60 Tap,
61 SetMainDimmer,
62 SetBlackout,
63 SetSpeedAdd,
64 SetSpeedMultiply,
65 SelectSceneDistributed,
66 SelectScene {
67 target: MidiSceneTarget,
68 },
69 ToggleSceneActive {
70 target: MidiSceneTarget,
71 },
72 SetSceneActive {
73 target: MidiSceneTarget,
74 },
75 FlashScene {
76 target: MidiSceneTarget,
77 },
78 SetSceneOpacity {
79 target: MidiSceneTarget,
80 },
81 SetSceneInputDimmer {
82 target: MidiSceneTarget,
83 },
84 SetSceneBeatOffset {
85 target: MidiSceneTarget,
86 },
87 SetSceneIgnoreMainDimmer {
88 target: MidiSceneTarget,
89 },
90 SetSceneSetOffsetOnFlash {
91 target: MidiSceneTarget,
92 },
93 SetSceneEffectSettingF32 {
94 target: MidiSceneTarget,
95 effect_index: u8,
96 setting_index: u8,
97 },
98 SetSceneEffectOpacity {
99 target: MidiSceneTarget,
100 effect_index: u8,
101 },
102 SetSceneEffectColorShift {
103 target: MidiSceneTarget,
104 effect_index: u8,
105 },
106 SetSceneEffectBeatProgression {
107 target: MidiSceneTarget,
108 effect_index: u8,
109 },
110 SetSceneEffectBeatOffset {
111 target: MidiSceneTarget,
112 effect_index: u8,
113 },
114 SetSceneEffectSpeedExponent {
115 target: MidiSceneTarget,
116 effect_index: u8,
117 },
118 SetSceneEffectGroupIndex {
119 target: MidiSceneTarget,
120 effect_index: u8,
121 },
122}
123
124#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
125pub enum MidiSceneTarget {
126 #[default]
127 Selected,
128 Quick {
129 index: u8,
130 },
131 Grid {
132 row: u8,
133 col: u8,
134 },
135}
136
137#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
138pub struct MidiOutputBinding {
139 pub name: String,
140 pub kind: MidiOutputBindingKind,
141}
142
143impl Default for MidiOutputBinding {
144 fn default() -> Self {
145 Self {
146 name: "Output".to_owned(),
147 kind: MidiOutputBindingKind::Value(MidiValueOutput::default()),
148 }
149 }
150}
151
152#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
153pub enum MidiOutputBindingKind {
154 Value(MidiValueOutput),
155 SceneColorValue(MidiSceneColorValueOutput),
156}
157
158impl Default for MidiOutputBindingKind {
159 fn default() -> Self {
160 Self::Value(MidiValueOutput::default())
161 }
162}
163
164#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
165pub struct MidiValueOutput {
166 pub status: u8,
167 pub data1: u8,
168 pub min: u8,
169 pub max: u8,
170 pub active_value: u8,
171 pub source: MidiValueSource,
172}
173
174impl Default for MidiValueOutput {
175 fn default() -> Self {
176 Self {
177 status: 176,
178 data1: 0,
179 min: 0,
180 max: 127,
181 active_value: 127,
182 source: MidiValueSource::SelectedSceneOpacity,
183 }
184 }
185}
186
187#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default)]
188pub enum MidiValueSource {
189 #[default]
190 SelectedSceneOpacity,
191 SelectedSceneDistributed,
192 SelectedSceneInputDimmer,
193 SelectedSceneBeatOffset,
194 SelectedSceneIgnoreMainDimmer,
195 SelectedSceneSetOffsetOnFlash,
196 MainDimmer,
197 BeatFlankPulse {
198 start_beat: f32,
199 end_beat: f32,
200 blackout_blink_value: Option<u8>,
201 },
202 Blackout {
203 inverted: bool,
204 blink: bool,
205 },
206 SceneOpacity {
207 target: MidiSceneTarget,
208 },
209 SceneInputDimmer {
210 target: MidiSceneTarget,
211 },
212 SceneBeatOffset {
213 target: MidiSceneTarget,
214 },
215 SceneIgnoreMainDimmer {
216 target: MidiSceneTarget,
217 },
218 SceneSetOffsetOnFlash {
219 target: MidiSceneTarget,
220 },
221 SceneActive {
222 target: MidiSceneTarget,
223 },
224 SceneFlashed {
225 target: MidiSceneTarget,
226 },
227 SceneEffectSettingF32 {
228 target: MidiSceneTarget,
229 effect_index: u8,
230 setting_index: u8,
231 },
232 SceneEffectOpacity {
233 target: MidiSceneTarget,
234 effect_index: u8,
235 },
236 SceneEffectColorShift {
237 target: MidiSceneTarget,
238 effect_index: u8,
239 },
240 SceneEffectBeatProgression {
241 target: MidiSceneTarget,
242 effect_index: u8,
243 },
244 SceneEffectBeatOffset {
245 target: MidiSceneTarget,
246 effect_index: u8,
247 },
248 SceneEffectSpeedExponent {
249 target: MidiSceneTarget,
250 effect_index: u8,
251 },
252 SceneEffectGroupIndex {
253 target: MidiSceneTarget,
254 effect_index: u8,
255 },
256}
257
258impl Eq for MidiValueSource {}
259
260#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
261pub enum MidiColorSource {
262 SceneColor { target: MidiSceneTarget },
263}
264
265impl Default for MidiColorSource {
266 fn default() -> Self {
267 Self::SceneColor {
268 target: MidiSceneTarget::Selected,
269 }
270 }
271}
272
273#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
274pub struct MidiSceneColorValueOutput {
275 pub source: MidiColorSource,
276 pub mapping_name: String,
277 pub status: u8,
278 pub data1: u8,
279}
280
281impl Default for MidiSceneColorValueOutput {
282 fn default() -> Self {
283 Self {
284 source: MidiColorSource::default(),
285 mapping_name: String::new(),
286 status: 144,
287 data1: 0,
288 }
289 }
290}
291
292#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
293pub struct MidiNamedSceneColorMapping {
294 pub name: String,
295 pub active: MidiSceneColorMessageMap,
296 pub inactive: MidiSceneColorMessageMap,
297 pub flashed: MidiSceneColorMessageMap,
298}
299
300impl Default for MidiNamedSceneColorMapping {
301 fn default() -> Self {
302 let default_map = MidiSceneColorMessageMap::default();
303 Self {
304 name: "Color Mapping".to_owned(),
305 active: default_map.clone(),
306 inactive: default_map.clone(),
307 flashed: default_map,
308 }
309 }
310}
311
312#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
313pub struct MidiSceneColorMessage {
314 pub value: u8,
315}
316
317#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
318pub struct MidiSceneColorMessageMap {
319 pub red: MidiSceneColorMessage,
320 pub green: MidiSceneColorMessage,
321 pub blue: MidiSceneColorMessage,
322 pub white: MidiSceneColorMessage,
323 pub orange: MidiSceneColorMessage,
324 pub yellow: MidiSceneColorMessage,
325 pub purple: MidiSceneColorMessage,
326 pub pink: MidiSceneColorMessage,
327 pub black: MidiSceneColorMessage,
328}
329
330impl Default for MidiSceneColorMessageMap {
331 fn default() -> Self {
332 Self {
333 red: MidiSceneColorMessage { value: 5 },
334 green: MidiSceneColorMessage { value: 21 },
335 blue: MidiSceneColorMessage { value: 45 },
336 white: MidiSceneColorMessage { value: 3 },
337 orange: MidiSceneColorMessage { value: 9 },
338 yellow: MidiSceneColorMessage { value: 13 },
339 purple: MidiSceneColorMessage { value: 49 },
340 pink: MidiSceneColorMessage { value: 53 },
341 black: MidiSceneColorMessage { value: 0 },
342 }
343 }
344}
345
346impl MidiSceneColorMessageMap {
347 pub fn message_for_color(&self, color: SceneInstanceColor) -> &MidiSceneColorMessage {
348 match color {
349 SceneInstanceColor::Red => &self.red,
350 SceneInstanceColor::Green => &self.green,
351 SceneInstanceColor::Blue => &self.blue,
352 SceneInstanceColor::White => &self.white,
353 SceneInstanceColor::Orange => &self.orange,
354 SceneInstanceColor::Yellow => &self.yellow,
355 SceneInstanceColor::Purple => &self.purple,
356 SceneInstanceColor::Pink => &self.pink,
357 SceneInstanceColor::Black => &self.black,
358 }
359 }
360}
361
362pub fn normalize_controller_key(port_name: &str) -> String {
366 let trimmed = port_name.trim();
367 if let Some(prefix) = trimmed.strip_suffix(']')
368 && let Some((base, suffix)) = prefix.rsplit_once(" [")
369 && !base.is_empty()
370 && !suffix.is_empty()
371 && suffix.chars().all(|ch| ch.is_ascii_digit())
372 {
373 return base.to_owned();
374 }
375
376 trimmed.to_owned()
377}