gled-core 2.31.0

The engine of gled without a user interface: inputs, timing, rendering, Art-Net/DMX output and storage
Documentation
//! How a value output turns gled's state into a MIDI value.

use crate::midi::state::MidiState;

pub(super) fn scale_to_range(value: f32, min: u8, max: u8) -> u8 {
    let min = f32::from(min);
    let max = f32::from(max);
    let value = value.clamp(0.0, 1.0);
    (min + (max - min) * value).round() as u8
}

pub(super) fn binary_output_value(active: bool, active_value: u8) -> u8 {
    if active { active_value } else { 0 }
}

pub(super) fn beat_range_contains(position: f32, start: f32, end: f32) -> bool {
    let start = start.rem_euclid(4.0);
    let end = end.rem_euclid(4.0);

    if start <= end {
        position >= start && position <= end
    } else {
        position >= start || position <= end
    }
}

pub(super) fn blackout_output_active(state: &MidiState, inverted: bool, blink: bool) -> bool {
    let base_active = if inverted {
        !state.blackout
    } else {
        state.blackout
    };
    if !base_active || !blink {
        return base_active;
    }

    let beats_per_second = (state.beats_per_minute / 60.0).max(f32::EPSILON);
    let elapsed_seconds = state.beat_progression / beats_per_second;
    (elapsed_seconds * 2.0).rem_euclid(1.0) < 0.5
}

pub(super) fn distributed_scene_index_from_midi(value: u8, scene_count: usize) -> Option<usize> {
    if scene_count == 0 {
        return None;
    }

    Some((((usize::from(value) + 1) * scene_count).saturating_sub(1)) / 128)
}

pub(super) fn value_for_distributed_scene_index(index: usize, scene_count: usize) -> u8 {
    if scene_count == 0 {
        return 0;
    }

    for value in 0u8..=127u8 {
        if distributed_scene_index_from_midi(value, scene_count) == Some(index) {
            let mut last = value;
            for candidate in value..=127u8 {
                if distributed_scene_index_from_midi(candidate, scene_count) == Some(index) {
                    last = candidate;
                } else {
                    break;
                }
            }
            return ((u16::from(value) + u16::from(last)) / 2) as u8;
        }
    }

    // More scenes than values can make some indices unreachable; use monotonic fallback.
    if scene_count <= 1 {
        return 0;
    }
    ((index.min(scene_count - 1) * 127) / (scene_count - 1)) as u8
}