gled-model 2.32.1

The data model of gled: projects, scenes, effects, animations, curves and assets
Documentation
//! What a controller is sent back: values that follow gled's state, and
//! scene colors through a mapping to the controller's own color messages.

use super::{MidiEffectTarget, MidiSceneTarget};
use serde::{Deserialize, Serialize};
use strum::{Display, EnumDiscriminants, EnumIter};

use crate::asset::scene::color::SceneInstanceColor;

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct MidiOutputBinding {
    pub name: String,
    pub kind: MidiOutputBindingKind,
    /// The mode the binding works in, by its position; `None` in all.
    #[serde(default)]
    pub mode: Option<u8>,
}

impl Default for MidiOutputBinding {
    fn default() -> Self {
        Self {
            name: "Output".to_owned(),
            kind: MidiOutputBindingKind::Value(MidiValueOutput::default()),
            mode: None,
        }
    }
}

/// `MidiOutputBindingSelector` is the kind without its settings; it reads as
/// the label the controller window shows.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, EnumDiscriminants)]
#[strum_discriminants(name(MidiOutputBindingSelector), derive(Display, EnumIter))]
pub enum MidiOutputBindingKind {
    #[strum_discriminants(strum(serialize = "Single Value"))]
    Value(MidiValueOutput),
    #[strum_discriminants(strum(serialize = "Scene Color Mapping"))]
    SceneColorValue(MidiSceneColorValueOutput),
    /// A level meter in the Mackie Control format.
    #[strum_discriminants(strum(serialize = "Mackie Meter"))]
    Meter(MidiMeterOutput),
}

/// A Mackie Control level meter: channel pressure on channel 1 with the
/// strip in the high nibble and 0–12 lit LEDs in the low one. The surface
/// lets a meter fall by itself, so it is sent again while it shows a level.
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
pub struct MidiMeterOutput {
    /// The channel strip, 0–7.
    pub strip: u8,
    pub source: MidiValueSource,
}

impl MidiMeterOutput {
    /// The highest level; 12 LEDs lit.
    const MAX_LEVEL: u8 = 12;

    /// The message showing `value` (0–1) on the meter.
    pub fn message(&self, value: f32) -> [u8; 2] {
        let level = (value.clamp(0.0, 1.0) * f32::from(Self::MAX_LEVEL)).round() as u8;
        [0xD0, ((self.strip & 0x07) << 4) | level]
    }
}

/// A value of an effect, for controls that show or set it.
#[derive(
    Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy, Hash, Default, Display, EnumIter,
)]
pub enum MidiEffectParameter {
    #[default]
    Opacity,
    #[strum(serialize = "Beat Progression")]
    BeatProgression,
    #[strum(serialize = "Color Shift")]
    ColorShift,
    #[strum(serialize = "Beat Offset")]
    BeatOffset,
    #[strum(serialize = "Setting 1")]
    Setting0,
    #[strum(serialize = "Setting 2")]
    Setting1,
    #[strum(serialize = "Setting 3")]
    Setting2,
    #[strum(serialize = "Setting 4")]
    Setting3,
    #[strum(serialize = "Setting 5")]
    Setting4,
    #[strum(serialize = "Setting 6")]
    Setting5,
}

impl MidiOutputBindingKind {
    /// The value source the output reads, if it reads one.
    pub fn source(&self) -> Option<&MidiValueSource> {
        match self {
            Self::Value(output) => Some(&output.source),
            Self::Meter(meter) => Some(&meter.source),
            Self::SceneColorValue(_) => None,
        }
    }

    pub fn source_mut(&mut self) -> Option<&mut MidiValueSource> {
        match self {
            Self::Value(output) => Some(&mut output.source),
            Self::Meter(meter) => Some(&mut meter.source),
            Self::SceneColorValue(_) => None,
        }
    }
}

impl MidiEffectParameter {
    /// The animation setting at `index` (`AnimationConfig::floats`).
    pub fn setting(index: u8) -> Option<Self> {
        Some(match index {
            0 => Self::Setting0,
            1 => Self::Setting1,
            2 => Self::Setting2,
            3 => Self::Setting3,
            4 => Self::Setting4,
            5 => Self::Setting5,
            _ => return None,
        })
    }
}

impl Default for MidiOutputBindingKind {
    fn default() -> Self {
        Self::Value(MidiValueOutput::default())
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct MidiValueOutput {
    pub status: u8,
    pub data1: u8,
    pub min: u8,
    pub max: u8,
    pub active_value: u8,
    pub source: MidiValueSource,
}

impl Default for MidiValueOutput {
    fn default() -> Self {
        Self {
            status: 176,
            data1: 0,
            min: 0,
            max: 127,
            active_value: 127,
            source: MidiValueSource::SelectedSceneOpacity,
        }
    }
}

/// `MidiValueSourceKind` is the source without its settings; it reads as
/// the label the controller window shows.
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default, EnumDiscriminants)]
#[strum_discriminants(
    name(MidiValueSourceKind),
    derive(Display, EnumIter),
    strum(serialize_all = "title_case")
)]
pub enum MidiValueSource {
    #[default]
    SelectedSceneOpacity,
    #[strum_discriminants(strum(serialize = "Selected Scene (Distributed 0-127)"))]
    SelectedSceneDistributed,
    /// The selected effect's position spread over the selected scene's
    /// effects, as `SelectSceneEffectDistributed` takes it.
    #[strum_discriminants(strum(serialize = "Selected Effect (Distributed 0-127)"))]
    SelectedEffectDistributed,
    /// The selected scene's opacity as it is right now, with its curve,
    /// sound trigger and input dimmer applied.
    #[strum_discriminants(strum(serialize = "Selected Scene Opacity Live"))]
    SelectedSceneOpacityLive,
    /// An effect's value of the selected scene as it is right now, with
    /// its curve and sound trigger applied, as a share of its range.
    #[strum_discriminants(strum(serialize = "Effect Live Value"))]
    SelectedSceneEffectLive {
        effect: MidiEffectTarget,
        parameter: MidiEffectParameter,
    },
    /// Whether the effect at `index` is the selected one, for a button's
    /// light.
    #[strum_discriminants(strum(serialize = "Effect Selected"))]
    EffectSelected {
        index: u8,
    },
    SelectedSceneInputDimmer,
    SelectedSceneBeatOffset,
    SelectedSceneIgnoreMainDimmer,
    SelectedSceneSetOffsetOnFlash,
    MainDimmer,
    #[strum_discriminants(strum(serialize = "Beat Flank"))]
    BeatFlankPulse {
        start_beat: f32,
        end_beat: f32,
        blackout_blink_value: Option<u8>,
    },
    Blackout {
        inverted: bool,
        blink: bool,
    },
    SceneOpacity {
        target: MidiSceneTarget,
    },
    SceneInputDimmer {
        target: MidiSceneTarget,
    },
    SceneBeatOffset {
        target: MidiSceneTarget,
    },
    SceneIgnoreMainDimmer {
        target: MidiSceneTarget,
    },
    SceneSetOffsetOnFlash {
        target: MidiSceneTarget,
    },
    SceneActive {
        target: MidiSceneTarget,
    },
    SceneFlashed {
        target: MidiSceneTarget,
    },
    /// Whether the target is the selected scene, for a button's light.
    SceneSelected {
        target: MidiSceneTarget,
    },
    /// Whether the controller is in mode `mode`, for a button's light.
    ModeActive {
        mode: u8,
    },
    #[strum_discriminants(strum(serialize = "Scene Effect Setting (n,n) f32"))]
    SceneEffectSettingF32 {
        target: MidiSceneTarget,
        #[serde(rename = "effect_index")]
        effect: MidiEffectTarget,
        setting_index: u8,
    },
    #[strum_discriminants(strum(serialize = "Effect Opacity"))]
    SceneEffectOpacity {
        target: MidiSceneTarget,
        #[serde(rename = "effect_index")]
        effect: MidiEffectTarget,
    },
    #[strum_discriminants(strum(serialize = "Effect Color Shift"))]
    SceneEffectColorShift {
        target: MidiSceneTarget,
        #[serde(rename = "effect_index")]
        effect: MidiEffectTarget,
    },
    #[strum_discriminants(strum(serialize = "Effect Beat Progression"))]
    SceneEffectBeatProgression {
        target: MidiSceneTarget,
        #[serde(rename = "effect_index")]
        effect: MidiEffectTarget,
    },
    #[strum_discriminants(strum(serialize = "Effect Beat Offset"))]
    SceneEffectBeatOffset {
        target: MidiSceneTarget,
        #[serde(rename = "effect_index")]
        effect: MidiEffectTarget,
    },
    #[strum_discriminants(strum(serialize = "Effect Speed Exponent"))]
    SceneEffectSpeedExponent {
        target: MidiSceneTarget,
        #[serde(rename = "effect_index")]
        effect: MidiEffectTarget,
    },
    /// The effect's layout, encoded as `SetSceneEffectLayout` takes it: 0
    /// inherits the tile's layout, 1 is the topology, 2… the layouts in stack
    /// order. Was the effect's group index before the patch.
    #[serde(alias = "SceneEffectGroupIndex")]
    #[strum_discriminants(strum(serialize = "Effect Layout"))]
    SceneEffectLayout {
        target: MidiSceneTarget,
        #[serde(rename = "effect_index")]
        effect: MidiEffectTarget,
    },
}

impl Eq for MidiValueSource {}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Display)]
pub enum MidiColorSource {
    #[strum(serialize = "Scene Color")]
    SceneColor { target: MidiSceneTarget },
}

impl Default for MidiColorSource {
    fn default() -> Self {
        Self::SceneColor {
            target: MidiSceneTarget::Selected,
        }
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct MidiSceneColorValueOutput {
    pub source: MidiColorSource,
    pub mapping_name: String,
    pub status: u8,
    pub data1: u8,
}

impl Default for MidiSceneColorValueOutput {
    fn default() -> Self {
        Self {
            source: MidiColorSource::default(),
            mapping_name: String::new(),
            status: 144,
            data1: 0,
        }
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct MidiNamedSceneColorMapping {
    pub name: String,
    pub active: MidiSceneColorMessageMap,
    pub inactive: MidiSceneColorMessageMap,
    pub flashed: MidiSceneColorMessageMap,
}

impl Default for MidiNamedSceneColorMapping {
    fn default() -> Self {
        let default_map = MidiSceneColorMessageMap::default();
        Self {
            name: "Color Mapping".to_owned(),
            active: default_map.clone(),
            inactive: default_map.clone(),
            flashed: default_map,
        }
    }
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
pub struct MidiSceneColorMessage {
    pub value: u8,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct MidiSceneColorMessageMap {
    pub red: MidiSceneColorMessage,
    pub green: MidiSceneColorMessage,
    pub blue: MidiSceneColorMessage,
    pub white: MidiSceneColorMessage,
    pub orange: MidiSceneColorMessage,
    pub yellow: MidiSceneColorMessage,
    pub purple: MidiSceneColorMessage,
    pub pink: MidiSceneColorMessage,
    pub black: MidiSceneColorMessage,
}

impl Default for MidiSceneColorMessageMap {
    fn default() -> Self {
        Self {
            red: MidiSceneColorMessage { value: 5 },
            green: MidiSceneColorMessage { value: 21 },
            blue: MidiSceneColorMessage { value: 45 },
            white: MidiSceneColorMessage { value: 3 },
            orange: MidiSceneColorMessage { value: 9 },
            yellow: MidiSceneColorMessage { value: 13 },
            purple: MidiSceneColorMessage { value: 49 },
            pink: MidiSceneColorMessage { value: 53 },
            black: MidiSceneColorMessage { value: 0 },
        }
    }
}

impl MidiSceneColorMessageMap {
    pub fn message_for_color(&self, color: SceneInstanceColor) -> &MidiSceneColorMessage {
        match color {
            SceneInstanceColor::Red => &self.red,
            SceneInstanceColor::Green => &self.green,
            SceneInstanceColor::Blue => &self.blue,
            SceneInstanceColor::White => &self.white,
            SceneInstanceColor::Orange => &self.orange,
            SceneInstanceColor::Yellow => &self.yellow,
            SceneInstanceColor::Purple => &self.purple,
            SceneInstanceColor::Pink => &self.pink,
            SceneInstanceColor::Black => &self.black,
        }
    }
}