Skip to main content

gled_model/asset/midi_controller/
output.rs

1//! What a controller is sent back: values that follow gled's state, and
2//! scene colors through a mapping to the controller's own color messages.
3
4use super::{MidiEffectTarget, MidiSceneTarget};
5use serde::{Deserialize, Serialize};
6use strum::{Display, EnumDiscriminants, EnumIter};
7
8use crate::asset::scene::color::SceneInstanceColor;
9
10#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
11pub struct MidiOutputBinding {
12    pub name: String,
13    pub kind: MidiOutputBindingKind,
14    /// The mode the binding works in, by its position; `None` in all.
15    #[serde(default)]
16    pub mode: Option<u8>,
17}
18
19impl Default for MidiOutputBinding {
20    fn default() -> Self {
21        Self {
22            name: "Output".to_owned(),
23            kind: MidiOutputBindingKind::Value(MidiValueOutput::default()),
24            mode: None,
25        }
26    }
27}
28
29/// `MidiOutputBindingSelector` is the kind without its settings; it reads as
30/// the label the controller window shows.
31#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, EnumDiscriminants)]
32#[strum_discriminants(name(MidiOutputBindingSelector), derive(Display, EnumIter))]
33pub enum MidiOutputBindingKind {
34    #[strum_discriminants(strum(serialize = "Single Value"))]
35    Value(MidiValueOutput),
36    #[strum_discriminants(strum(serialize = "Scene Color Mapping"))]
37    SceneColorValue(MidiSceneColorValueOutput),
38    /// A level meter in the Mackie Control format.
39    #[strum_discriminants(strum(serialize = "Mackie Meter"))]
40    Meter(MidiMeterOutput),
41}
42
43/// A Mackie Control level meter: channel pressure on channel 1 with the
44/// strip in the high nibble and 0–12 lit LEDs in the low one. The surface
45/// lets a meter fall by itself, so it is sent again while it shows a level.
46#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
47pub struct MidiMeterOutput {
48    /// The channel strip, 0–7.
49    pub strip: u8,
50    pub source: MidiValueSource,
51}
52
53impl MidiMeterOutput {
54    /// The highest level; 12 LEDs lit.
55    const MAX_LEVEL: u8 = 12;
56
57    /// The message showing `value` (0–1) on the meter.
58    pub fn message(&self, value: f32) -> [u8; 2] {
59        let level = (value.clamp(0.0, 1.0) * f32::from(Self::MAX_LEVEL)).round() as u8;
60        [0xD0, ((self.strip & 0x07) << 4) | level]
61    }
62}
63
64/// A value of an effect, for controls that show or set it.
65#[derive(
66    Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy, Hash, Default, Display, EnumIter,
67)]
68pub enum MidiEffectParameter {
69    #[default]
70    Opacity,
71    #[strum(serialize = "Beat Progression")]
72    BeatProgression,
73    #[strum(serialize = "Color Shift")]
74    ColorShift,
75    #[strum(serialize = "Beat Offset")]
76    BeatOffset,
77    #[strum(serialize = "Setting 1")]
78    Setting0,
79    #[strum(serialize = "Setting 2")]
80    Setting1,
81    #[strum(serialize = "Setting 3")]
82    Setting2,
83    #[strum(serialize = "Setting 4")]
84    Setting3,
85    #[strum(serialize = "Setting 5")]
86    Setting4,
87    #[strum(serialize = "Setting 6")]
88    Setting5,
89}
90
91impl MidiOutputBindingKind {
92    /// The value source the output reads, if it reads one.
93    pub fn source(&self) -> Option<&MidiValueSource> {
94        match self {
95            Self::Value(output) => Some(&output.source),
96            Self::Meter(meter) => Some(&meter.source),
97            Self::SceneColorValue(_) => None,
98        }
99    }
100
101    pub fn source_mut(&mut self) -> Option<&mut MidiValueSource> {
102        match self {
103            Self::Value(output) => Some(&mut output.source),
104            Self::Meter(meter) => Some(&mut meter.source),
105            Self::SceneColorValue(_) => None,
106        }
107    }
108}
109
110impl MidiEffectParameter {
111    /// The animation setting at `index` (`AnimationConfig::floats`).
112    pub fn setting(index: u8) -> Option<Self> {
113        Some(match index {
114            0 => Self::Setting0,
115            1 => Self::Setting1,
116            2 => Self::Setting2,
117            3 => Self::Setting3,
118            4 => Self::Setting4,
119            5 => Self::Setting5,
120            _ => return None,
121        })
122    }
123}
124
125impl Default for MidiOutputBindingKind {
126    fn default() -> Self {
127        Self::Value(MidiValueOutput::default())
128    }
129}
130
131#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
132pub struct MidiValueOutput {
133    pub status: u8,
134    pub data1: u8,
135    pub min: u8,
136    pub max: u8,
137    pub active_value: u8,
138    pub source: MidiValueSource,
139}
140
141impl Default for MidiValueOutput {
142    fn default() -> Self {
143        Self {
144            status: 176,
145            data1: 0,
146            min: 0,
147            max: 127,
148            active_value: 127,
149            source: MidiValueSource::SelectedSceneOpacity,
150        }
151    }
152}
153
154/// `MidiValueSourceKind` is the source without its settings; it reads as
155/// the label the controller window shows.
156#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default, EnumDiscriminants)]
157#[strum_discriminants(
158    name(MidiValueSourceKind),
159    derive(Display, EnumIter),
160    strum(serialize_all = "title_case")
161)]
162pub enum MidiValueSource {
163    #[default]
164    SelectedSceneOpacity,
165    #[strum_discriminants(strum(serialize = "Selected Scene (Distributed 0-127)"))]
166    SelectedSceneDistributed,
167    /// The selected effect's position spread over the selected scene's
168    /// effects, as `SelectSceneEffectDistributed` takes it.
169    #[strum_discriminants(strum(serialize = "Selected Effect (Distributed 0-127)"))]
170    SelectedEffectDistributed,
171    /// The selected scene's opacity as it is right now, with its curve,
172    /// sound trigger and input dimmer applied.
173    #[strum_discriminants(strum(serialize = "Selected Scene Opacity Live"))]
174    SelectedSceneOpacityLive,
175    /// An effect's value of the selected scene as it is right now, with
176    /// its curve and sound trigger applied, as a share of its range.
177    #[strum_discriminants(strum(serialize = "Effect Live Value"))]
178    SelectedSceneEffectLive {
179        effect: MidiEffectTarget,
180        parameter: MidiEffectParameter,
181    },
182    /// Whether the effect at `index` is the selected one, for a button's
183    /// light.
184    #[strum_discriminants(strum(serialize = "Effect Selected"))]
185    EffectSelected {
186        index: u8,
187    },
188    SelectedSceneInputDimmer,
189    SelectedSceneBeatOffset,
190    SelectedSceneIgnoreMainDimmer,
191    SelectedSceneSetOffsetOnFlash,
192    MainDimmer,
193    #[strum_discriminants(strum(serialize = "Beat Flank"))]
194    BeatFlankPulse {
195        start_beat: f32,
196        end_beat: f32,
197        blackout_blink_value: Option<u8>,
198    },
199    Blackout {
200        inverted: bool,
201        blink: bool,
202    },
203    SceneOpacity {
204        target: MidiSceneTarget,
205    },
206    /// The target's opacity as it is right now, with its curve, sound
207    /// trigger and input dimmer applied.
208    SceneOpacityLive {
209        target: MidiSceneTarget,
210    },
211    SceneInputDimmer {
212        target: MidiSceneTarget,
213    },
214    SceneBeatOffset {
215        target: MidiSceneTarget,
216    },
217    SceneIgnoreMainDimmer {
218        target: MidiSceneTarget,
219    },
220    SceneSetOffsetOnFlash {
221        target: MidiSceneTarget,
222    },
223    SceneActive {
224        target: MidiSceneTarget,
225    },
226    SceneFlashed {
227        target: MidiSceneTarget,
228    },
229    /// Whether the target is the selected scene, for a button's light.
230    SceneSelected {
231        target: MidiSceneTarget,
232    },
233    /// Whether the controller is in mode `mode`, for a button's light.
234    ModeActive {
235        mode: u8,
236    },
237    #[strum_discriminants(strum(serialize = "Scene Effect Setting (n,n) f32"))]
238    SceneEffectSettingF32 {
239        target: MidiSceneTarget,
240        #[serde(rename = "effect_index")]
241        effect: MidiEffectTarget,
242        setting_index: u8,
243    },
244    #[strum_discriminants(strum(serialize = "Effect Opacity"))]
245    SceneEffectOpacity {
246        target: MidiSceneTarget,
247        #[serde(rename = "effect_index")]
248        effect: MidiEffectTarget,
249    },
250    #[strum_discriminants(strum(serialize = "Effect Color Shift"))]
251    SceneEffectColorShift {
252        target: MidiSceneTarget,
253        #[serde(rename = "effect_index")]
254        effect: MidiEffectTarget,
255    },
256    #[strum_discriminants(strum(serialize = "Effect Beat Progression"))]
257    SceneEffectBeatProgression {
258        target: MidiSceneTarget,
259        #[serde(rename = "effect_index")]
260        effect: MidiEffectTarget,
261    },
262    #[strum_discriminants(strum(serialize = "Effect Beat Offset"))]
263    SceneEffectBeatOffset {
264        target: MidiSceneTarget,
265        #[serde(rename = "effect_index")]
266        effect: MidiEffectTarget,
267    },
268    #[strum_discriminants(strum(serialize = "Effect Speed Exponent"))]
269    SceneEffectSpeedExponent {
270        target: MidiSceneTarget,
271        #[serde(rename = "effect_index")]
272        effect: MidiEffectTarget,
273    },
274    /// The effect's layout, encoded as `SetSceneEffectLayout` takes it: 0
275    /// inherits the tile's layout, 1 is the topology, 2… the layouts in stack
276    /// order. Was the effect's group index before the patch.
277    #[serde(alias = "SceneEffectGroupIndex")]
278    #[strum_discriminants(strum(serialize = "Effect Layout"))]
279    SceneEffectLayout {
280        target: MidiSceneTarget,
281        #[serde(rename = "effect_index")]
282        effect: MidiEffectTarget,
283    },
284}
285
286impl Eq for MidiValueSource {}
287
288#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Display)]
289pub enum MidiColorSource {
290    #[strum(serialize = "Scene Color")]
291    SceneColor { target: MidiSceneTarget },
292}
293
294impl Default for MidiColorSource {
295    fn default() -> Self {
296        Self::SceneColor {
297            target: MidiSceneTarget::Selected,
298        }
299    }
300}
301
302#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
303pub struct MidiSceneColorValueOutput {
304    pub source: MidiColorSource,
305    pub mapping_name: String,
306    pub status: u8,
307    pub data1: u8,
308}
309
310impl Default for MidiSceneColorValueOutput {
311    fn default() -> Self {
312        Self {
313            source: MidiColorSource::default(),
314            mapping_name: String::new(),
315            status: 144,
316            data1: 0,
317        }
318    }
319}
320
321#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
322pub struct MidiNamedSceneColorMapping {
323    pub name: String,
324    pub active: MidiSceneColorMessageMap,
325    pub inactive: MidiSceneColorMessageMap,
326    pub flashed: MidiSceneColorMessageMap,
327}
328
329impl Default for MidiNamedSceneColorMapping {
330    fn default() -> Self {
331        let default_map = MidiSceneColorMessageMap::default();
332        Self {
333            name: "Color Mapping".to_owned(),
334            active: default_map.clone(),
335            inactive: default_map.clone(),
336            flashed: default_map,
337        }
338    }
339}
340
341#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
342pub struct MidiSceneColorMessage {
343    pub value: u8,
344}
345
346#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
347pub struct MidiSceneColorMessageMap {
348    pub red: MidiSceneColorMessage,
349    pub green: MidiSceneColorMessage,
350    pub blue: MidiSceneColorMessage,
351    pub white: MidiSceneColorMessage,
352    pub orange: MidiSceneColorMessage,
353    pub yellow: MidiSceneColorMessage,
354    pub purple: MidiSceneColorMessage,
355    pub pink: MidiSceneColorMessage,
356    pub black: MidiSceneColorMessage,
357}
358
359impl Default for MidiSceneColorMessageMap {
360    fn default() -> Self {
361        Self {
362            red: MidiSceneColorMessage { value: 5 },
363            green: MidiSceneColorMessage { value: 21 },
364            blue: MidiSceneColorMessage { value: 45 },
365            white: MidiSceneColorMessage { value: 3 },
366            orange: MidiSceneColorMessage { value: 9 },
367            yellow: MidiSceneColorMessage { value: 13 },
368            purple: MidiSceneColorMessage { value: 49 },
369            pink: MidiSceneColorMessage { value: 53 },
370            black: MidiSceneColorMessage { value: 0 },
371        }
372    }
373}
374
375impl MidiSceneColorMessageMap {
376    pub fn message_for_color(&self, color: SceneInstanceColor) -> &MidiSceneColorMessage {
377        match color {
378            SceneInstanceColor::Red => &self.red,
379            SceneInstanceColor::Green => &self.green,
380            SceneInstanceColor::Blue => &self.blue,
381            SceneInstanceColor::White => &self.white,
382            SceneInstanceColor::Orange => &self.orange,
383            SceneInstanceColor::Yellow => &self.yellow,
384            SceneInstanceColor::Purple => &self.purple,
385            SceneInstanceColor::Pink => &self.pink,
386            SceneInstanceColor::Black => &self.black,
387        }
388    }
389}