use gled_model::asset::midi_controller::{
MidiColorSource, MidiEffectParameter, MidiEffectTarget, MidiInputAction, MidiOutputBindingKind,
MidiSceneTarget, MidiValueSource,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) enum Slot {
Row,
Column,
Quick,
Effect,
Animation,
Setting,
}
fn target_numbers(target: &mut MidiSceneTarget) -> Vec<(Slot, &mut u8)> {
match target {
MidiSceneTarget::Selected => Vec::new(),
MidiSceneTarget::Quick { index } => vec![(Slot::Quick, index)],
MidiSceneTarget::Grid { row, col } => vec![(Slot::Row, row), (Slot::Column, col)],
MidiSceneTarget::Row { col } => vec![(Slot::Column, col)],
MidiSceneTarget::Column { row } => vec![(Slot::Row, row)],
}
}
fn effect_numbers(effect: &mut MidiEffectTarget) -> Vec<(Slot, &mut u8)> {
match effect {
MidiEffectTarget::Selected => Vec::new(),
MidiEffectTarget::Index { index } => vec![(Slot::Effect, index)],
}
}
fn action_numbers_mut(action: &mut MidiInputAction) -> Vec<(Slot, &mut u8)> {
use MidiInputAction as A;
match action {
A::SelectScene { target }
| A::ToggleSceneActive { target }
| A::SetSceneActive { target }
| A::FlashScene { target }
| A::SetSceneOpacity { target }
| A::SetSceneInputDimmer { target }
| A::SetSceneBeatOffset { target }
| A::SetSceneIgnoreMainDimmer { target }
| A::SetSceneSetOffsetOnFlash { target } => target_numbers(target),
A::SetSceneEffectSettingF32 {
target,
effect,
setting_index,
} => {
let mut numbers = vec![(Slot::Setting, setting_index)];
numbers.extend(effect_numbers(effect));
numbers.extend(target_numbers(target));
numbers
}
A::SetSceneEffectOpacity { target, effect }
| A::SetSceneEffectColorShift { target, effect }
| A::SetSceneEffectBeatProgression { target, effect }
| A::SetSceneEffectBeatOffset { target, effect }
| A::SetSceneEffectSpeedExponent { target, effect }
| A::SetSceneEffectLayout { target, effect } => {
let mut numbers = effect_numbers(effect);
numbers.extend(target_numbers(target));
numbers
}
A::SelectSceneEffect { index } => vec![(Slot::Animation, index)],
A::Tap
| A::SetMainDimmer
| A::SetMainDimmerRelative { .. }
| A::SetBlackout
| A::ToggleBlackout
| A::SetSpeedAdd
| A::SetSpeedMultiply
| A::SetSpeedRelative { .. }
| A::SelectSceneDistributed
| A::SelectSceneRelative { .. }
| A::SelectSceneRow { .. }
| A::SelectSceneColumn { .. }
| A::SelectMode { .. }
| A::SelectSceneEffectRelative { .. }
| A::SelectSceneEffectDistributed => Vec::new(),
}
}
fn source_numbers_mut(source: &mut MidiValueSource) -> Vec<(Slot, &mut u8)> {
use MidiValueSource as S;
match source {
S::SceneOpacity { target }
| S::SceneOpacityLive { target }
| S::SceneInputDimmer { target }
| S::SceneBeatOffset { target }
| S::SceneIgnoreMainDimmer { target }
| S::SceneSetOffsetOnFlash { target }
| S::SceneActive { target }
| S::SceneFlashed { target }
| S::SceneSelected { target } => target_numbers(target),
S::SceneEffectSettingF32 {
target,
effect,
setting_index,
} => {
let mut numbers = vec![(Slot::Setting, setting_index)];
numbers.extend(effect_numbers(effect));
numbers.extend(target_numbers(target));
numbers
}
S::SceneEffectOpacity { target, effect }
| S::SceneEffectColorShift { target, effect }
| S::SceneEffectBeatProgression { target, effect }
| S::SceneEffectBeatOffset { target, effect }
| S::SceneEffectSpeedExponent { target, effect }
| S::SceneEffectLayout { target, effect } => {
let mut numbers = effect_numbers(effect);
numbers.extend(target_numbers(target));
numbers
}
S::SelectedSceneEffectLive { effect, .. } => effect_numbers(effect),
S::EffectSelected { index } => vec![(Slot::Animation, index)],
S::SelectedSceneOpacity
| S::SelectedSceneDistributed
| S::SelectedEffectDistributed
| S::SelectedSceneOpacityLive
| S::SelectedSceneInputDimmer
| S::SelectedSceneBeatOffset
| S::SelectedSceneIgnoreMainDimmer
| S::SelectedSceneSetOffsetOnFlash
| S::MainDimmer
| S::BeatFlankPulse { .. }
| S::Blackout { .. }
| S::ModeActive { .. } => Vec::new(),
}
}
pub(crate) fn action_numbers(action: &MidiInputAction) -> Vec<(Slot, u8)> {
let mut action = action.clone();
action_numbers_mut(&mut action)
.into_iter()
.map(|(slot, number)| (slot, *number))
.collect()
}
pub(crate) fn set_action_number(action: &mut MidiInputAction, slot: Slot, value: u8) {
for (number_slot, number) in action_numbers_mut(action) {
if number_slot == slot {
*number = value;
}
}
}
pub(crate) fn source_numbers(source: &MidiValueSource) -> Vec<(Slot, u8)> {
let mut numbers = Vec::new();
if let MidiValueSource::SelectedSceneEffectLive { parameter, .. } = source
&& let Some(setting) = setting_of(*parameter)
{
numbers.push((Slot::Setting, setting));
}
let mut source = source.clone();
numbers.extend(
source_numbers_mut(&mut source)
.into_iter()
.map(|(slot, number)| (slot, *number)),
);
numbers
}
pub(crate) fn set_source_number(source: &mut MidiValueSource, slot: Slot, value: u8) {
if let MidiValueSource::SelectedSceneEffectLive { parameter, .. } = source
&& slot == Slot::Setting
&& setting_of(*parameter).is_some()
&& let Some(setting) = MidiEffectParameter::setting(value)
{
*parameter = setting;
}
for (number_slot, number) in source_numbers_mut(source) {
if number_slot == slot {
*number = value;
}
}
}
fn setting_of(parameter: MidiEffectParameter) -> Option<u8> {
(0..6).find(|&index| MidiEffectParameter::setting(index) == Some(parameter))
}
pub(crate) fn output_numbers(kind: &MidiOutputBindingKind) -> Vec<(Slot, u8)> {
match kind {
MidiOutputBindingKind::SceneColorValue(color) => {
let MidiColorSource::SceneColor { target } = &color.source;
let mut target = target.clone();
target_numbers(&mut target)
.into_iter()
.map(|(slot, number)| (slot, *number))
.collect()
}
kind => kind.source().map(source_numbers).unwrap_or_default(),
}
}
pub(crate) fn set_output_number(kind: &mut MidiOutputBindingKind, slot: Slot, value: u8) {
match kind {
MidiOutputBindingKind::SceneColorValue(color) => {
let MidiColorSource::SceneColor { target } = &mut color.source;
for (number_slot, number) in target_numbers(target) {
if number_slot == slot {
*number = value;
}
}
}
kind => {
if let Some(source) = kind.source_mut() {
set_source_number(source, slot, value);
}
}
}
}
pub(crate) fn action_shape(action: &MidiInputAction) -> MidiInputAction {
let mut shape = action.clone();
for (slot, _) in action_numbers(action) {
set_action_number(&mut shape, slot, 0);
}
shape
}
pub(crate) fn source_shape(source: &MidiValueSource) -> MidiValueSource {
let mut shape = source.clone();
for (slot, _) in source_numbers(source) {
set_source_number(&mut shape, slot, 0);
}
shape
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_setting_names_its_setting_effect_and_scene() {
let action = MidiInputAction::SetSceneEffectSettingF32 {
target: MidiSceneTarget::Grid { row: 1, col: 2 },
effect: MidiEffectTarget::Index { index: 3 },
setting_index: 4,
};
assert_eq!(
action_numbers(&action),
[
(Slot::Setting, 4),
(Slot::Effect, 3),
(Slot::Row, 1),
(Slot::Column, 2)
]
);
}
#[test]
fn actions_on_different_scenes_have_the_same_shape() {
let flash = |col| MidiInputAction::FlashScene {
target: MidiSceneTarget::Row { col },
};
assert_eq!(action_shape(&flash(1)), action_shape(&flash(5)));
assert_ne!(
action_shape(&flash(1)),
action_shape(&MidiInputAction::FlashScene {
target: MidiSceneTarget::Column { row: 1 }
})
);
}
#[test]
fn a_live_setting_counts_as_its_setting() {
let mut source = MidiValueSource::SelectedSceneEffectLive {
effect: MidiEffectTarget::Selected,
parameter: MidiEffectParameter::Setting1,
};
assert_eq!(source_numbers(&source), [(Slot::Setting, 1)]);
set_source_number(&mut source, Slot::Setting, 3);
assert_eq!(
source,
MidiValueSource::SelectedSceneEffectLive {
effect: MidiEffectTarget::Selected,
parameter: MidiEffectParameter::Setting3,
}
);
}
}