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    /// On between `start_beat` and `end_beat` of each 4-beat bar, or of
195    /// each beat (0–1) with `every_beat`.
196    BeatFlankPulse {
197        start_beat: f32,
198        end_beat: f32,
199        blackout_blink_value: Option<u8>,
200        #[serde(default)]
201        every_beat: bool,
202    },
203    Blackout {
204        inverted: bool,
205        blink: bool,
206    },
207    SceneOpacity {
208        target: MidiSceneTarget,
209    },
210    /// The target's opacity as it is right now, with its curve, sound
211    /// trigger and input dimmer applied.
212    SceneOpacityLive {
213        target: MidiSceneTarget,
214    },
215    SceneInputDimmer {
216        target: MidiSceneTarget,
217    },
218    SceneBeatOffset {
219        target: MidiSceneTarget,
220    },
221    SceneIgnoreMainDimmer {
222        target: MidiSceneTarget,
223    },
224    SceneSetOffsetOnFlash {
225        target: MidiSceneTarget,
226    },
227    SceneActive {
228        target: MidiSceneTarget,
229    },
230    SceneFlashed {
231        target: MidiSceneTarget,
232    },
233    /// Whether the target is the selected scene, for a button's light.
234    SceneSelected {
235        target: MidiSceneTarget,
236    },
237    /// Whether the controller is in mode `mode`, for a button's light.
238    ModeActive {
239        mode: u8,
240    },
241    #[strum_discriminants(strum(serialize = "Scene Effect Setting (n,n) f32"))]
242    SceneEffectSettingF32 {
243        target: MidiSceneTarget,
244        #[serde(rename = "effect_index")]
245        effect: MidiEffectTarget,
246        setting_index: u8,
247    },
248    #[strum_discriminants(strum(serialize = "Effect Opacity"))]
249    SceneEffectOpacity {
250        target: MidiSceneTarget,
251        #[serde(rename = "effect_index")]
252        effect: MidiEffectTarget,
253    },
254    #[strum_discriminants(strum(serialize = "Effect Color Shift"))]
255    SceneEffectColorShift {
256        target: MidiSceneTarget,
257        #[serde(rename = "effect_index")]
258        effect: MidiEffectTarget,
259    },
260    #[strum_discriminants(strum(serialize = "Effect Beat Progression"))]
261    SceneEffectBeatProgression {
262        target: MidiSceneTarget,
263        #[serde(rename = "effect_index")]
264        effect: MidiEffectTarget,
265    },
266    #[strum_discriminants(strum(serialize = "Effect Beat Offset"))]
267    SceneEffectBeatOffset {
268        target: MidiSceneTarget,
269        #[serde(rename = "effect_index")]
270        effect: MidiEffectTarget,
271    },
272    #[strum_discriminants(strum(serialize = "Effect Speed Exponent"))]
273    SceneEffectSpeedExponent {
274        target: MidiSceneTarget,
275        #[serde(rename = "effect_index")]
276        effect: MidiEffectTarget,
277    },
278    /// The effect's layout, encoded as `SetSceneEffectLayout` takes it: 0
279    /// inherits the tile's layout, 1 is the topology, 2… the layouts in stack
280    /// order. Was the effect's group index before the patch.
281    #[serde(alias = "SceneEffectGroupIndex")]
282    #[strum_discriminants(strum(serialize = "Effect Layout"))]
283    SceneEffectLayout {
284        target: MidiSceneTarget,
285        #[serde(rename = "effect_index")]
286        effect: MidiEffectTarget,
287    },
288}
289
290impl Eq for MidiValueSource {}
291
292#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Display)]
293pub enum MidiColorSource {
294    #[strum(serialize = "Scene Color")]
295    SceneColor { target: MidiSceneTarget },
296}
297
298impl Default for MidiColorSource {
299    fn default() -> Self {
300        Self::SceneColor {
301            target: MidiSceneTarget::Selected,
302        }
303    }
304}
305
306#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
307pub struct MidiSceneColorValueOutput {
308    pub source: MidiColorSource,
309    pub mapping_name: String,
310    pub status: u8,
311    pub data1: u8,
312}
313
314impl Default for MidiSceneColorValueOutput {
315    fn default() -> Self {
316        Self {
317            source: MidiColorSource::default(),
318            mapping_name: String::new(),
319            status: 144,
320            data1: 0,
321        }
322    }
323}
324
325#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
326pub struct MidiNamedSceneColorMapping {
327    pub name: String,
328    pub active: MidiSceneColorMessageMap,
329    pub inactive: MidiSceneColorMessageMap,
330    pub flashed: MidiSceneColorMessageMap,
331}
332
333impl Default for MidiNamedSceneColorMapping {
334    fn default() -> Self {
335        let default_map = MidiSceneColorMessageMap::default();
336        Self {
337            name: "Color Mapping".to_owned(),
338            active: default_map.clone(),
339            inactive: default_map.clone(),
340            flashed: default_map,
341        }
342    }
343}
344
345#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
346pub struct MidiSceneColorMessage {
347    pub value: u8,
348}
349
350#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
351pub struct MidiSceneColorMessageMap {
352    pub red: MidiSceneColorMessage,
353    pub green: MidiSceneColorMessage,
354    pub blue: MidiSceneColorMessage,
355    pub white: MidiSceneColorMessage,
356    pub orange: MidiSceneColorMessage,
357    pub yellow: MidiSceneColorMessage,
358    pub purple: MidiSceneColorMessage,
359    pub pink: MidiSceneColorMessage,
360    pub black: MidiSceneColorMessage,
361}
362
363impl Default for MidiSceneColorMessageMap {
364    fn default() -> Self {
365        Self {
366            red: MidiSceneColorMessage { value: 5 },
367            green: MidiSceneColorMessage { value: 21 },
368            blue: MidiSceneColorMessage { value: 45 },
369            white: MidiSceneColorMessage { value: 3 },
370            orange: MidiSceneColorMessage { value: 9 },
371            yellow: MidiSceneColorMessage { value: 13 },
372            purple: MidiSceneColorMessage { value: 49 },
373            pink: MidiSceneColorMessage { value: 53 },
374            black: MidiSceneColorMessage { value: 0 },
375        }
376    }
377}
378
379impl MidiSceneColorMessageMap {
380    pub fn message_for_color(&self, color: SceneInstanceColor) -> &MidiSceneColorMessage {
381        match color {
382            SceneInstanceColor::Red => &self.red,
383            SceneInstanceColor::Green => &self.green,
384            SceneInstanceColor::Blue => &self.blue,
385            SceneInstanceColor::White => &self.white,
386            SceneInstanceColor::Orange => &self.orange,
387            SceneInstanceColor::Yellow => &self.yellow,
388            SceneInstanceColor::Purple => &self.purple,
389            SceneInstanceColor::Pink => &self.pink,
390            SceneInstanceColor::Black => &self.black,
391        }
392    }
393}