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