gled-core 2.31.2

The engine of gled without a user interface: inputs, timing, rendering, Art-Net/DMX output and storage
Documentation
//! The state feedback a subscriber receives: project, timing, palette, layouts
//! and every scene instance.

use rosc::OscType;
use std::collections::HashMap;
use std::net::{SocketAddr, UdpSocket};
use std::time::Instant;

use crate::input::osc::OscStateSnapshot;
use crate::storage::asset::project::GridHighlight;

use super::scene::send_scene_instance_feedback;
use super::{osc_int, send_feedback};

pub(super) fn broadcast_snapshot(
    socket: &UdpSocket,
    subscribers: &HashMap<SocketAddr, Instant>,
    snapshot: &OscStateSnapshot,
) {
    for subscriber in subscribers.keys().copied() {
        send_feedback(
            socket,
            subscriber,
            "/project/main_dimmer",
            vec![OscType::Float(
                snapshot.project.as_ref().map_or(1.0, |p| p.main_dimmer),
            )],
        );
        send_feedback(
            socket,
            subscriber,
            "/project/blackout",
            vec![OscType::Bool(snapshot.blackout)],
        );
        send_feedback(
            socket,
            subscriber,
            "/timing/bpm",
            vec![OscType::Float(snapshot.beats_per_minute)],
        );
        send_feedback(
            socket,
            subscriber,
            "/timing/beat_progression",
            vec![OscType::Float(snapshot.beat_progression)],
        );

        if let Some(project) = snapshot.project.as_ref() {
            // Project palette
            send_feedback(
                socket,
                subscriber,
                "/project/palette/enabled",
                vec![OscType::Bool(project.palette.is_some())],
            );
            if let Some(palette) = &project.palette {
                let [r, g, b] = palette.primary.rgb();
                send_feedback(
                    socket,
                    subscriber,
                    "/project/palette/primary",
                    vec![OscType::Float(r), OscType::Float(g), OscType::Float(b)],
                );
                let [r, g, b] = palette.secondary.rgb();
                send_feedback(
                    socket,
                    subscriber,
                    "/project/palette/secondary",
                    vec![OscType::Float(r), OscType::Float(g), OscType::Float(b)],
                );
                for (n, color) in palette.gradient.iter().enumerate() {
                    let [r, g, b] = color.rgb();
                    send_feedback(
                        socket,
                        subscriber,
                        &format!("/project/palette/gradient/{n}"),
                        vec![OscType::Float(r), OscType::Float(g), OscType::Float(b)],
                    );
                }
            }

            // The layout stack, by the index `.../effect/{i}/layout` takes:
            // 0 is the topology.
            let layout_refs = project.patch.layout_refs();
            send_feedback(
                socket,
                subscriber,
                "/project/layouts/count",
                vec![osc_int(layout_refs.len())],
            );
            for layout_ref in layout_refs {
                if let Some(layout) = project.patch.layout(layout_ref) {
                    send_feedback(
                        socket,
                        subscriber,
                        &format!("/project/layouts/{}", layout_ref.stack_index()),
                        vec![OscType::String(layout.name.clone())],
                    );
                }
            }

            // All grid scenes
            for (location, scene_instance) in &project.scenes_instances_grid {
                let (col, row) = (location.col, location.row);
                send_scene_instance_feedback(
                    socket,
                    subscriber,
                    &format!("/scene/grid/{col}/{row}"),
                    scene_instance,
                );
                match project.grid_highlight {
                    GridHighlight::Row if row == project.quick_row_index() => {
                        send_scene_instance_feedback(
                            socket,
                            subscriber,
                            &format!("/scene/quick/{col}"),
                            scene_instance,
                        );
                    }
                    GridHighlight::Column if col == project.grid_width().saturating_sub(1) => {
                        send_scene_instance_feedback(
                            socket,
                            subscriber,
                            &format!("/scene/quick/{row}"),
                            scene_instance,
                        );
                    }
                    GridHighlight::None | GridHighlight::Row | GridHighlight::Column => {}
                }
            }

            // Selected scene
            if let Some(scene_instance) = project
                .scenes_instances_grid
                .get(&snapshot.selected_scene_instance)
            {
                send_scene_instance_feedback(socket, subscriber, "/scene/selected", scene_instance);
            }
        }
    }
}