use crate::storage::{
asset::{
Asset,
project::Project,
scene::{Scene, grid::GridLocation, instance::SceneInstance},
},
asset_id::AssetId,
collections::Collections,
};
use deku::prelude::*;
use kanal::Receiver;
use serde::{Deserialize, Serialize};
const ARTNET_CONTROL_SLOTS: usize = 10;
const SCENE_SPECIFIC_PARAMETERS: usize = 10;
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
pub struct ArtnetControlConfig {
pub active: bool,
pub universe: u16,
}
impl Default for ArtnetControlConfig {
fn default() -> Self {
Self {
active: false,
universe: 1337,
}
}
}
#[allow(dead_code)]
#[derive(Debug, DekuRead, Default, Copy, Clone)]
pub struct ArtnetSceneControlState {
pub scene_index: u8, pub group_index: u8,
pub opacity: u8,
pub offset: u8,
pub speed_multiplier: u8,
pub color_mode: u8, pub pallet_override: u8,
pub dummy: [u8; 6],
pub scene_specific_parameters: [u8; SCENE_SPECIFIC_PARAMETERS],
}
pub struct ExternalControlState {
prev_values: [ArtnetSceneControlState; ARTNET_CONTROL_SLOTS],
artnet_control_receiver: Receiver<Vec<u8>>,
}
impl ExternalControlState {
pub fn new(artnet_control_receiver: Receiver<Vec<u8>>) -> Self {
Self {
prev_values: [ArtnetSceneControlState::default(); ARTNET_CONTROL_SLOTS],
artnet_control_receiver,
}
}
pub fn process_events(&mut self, project: &mut Option<Project>, collections: &Collections) {
while let Ok(Some(dmx_data)) = self.artnet_control_receiver.try_recv() {
self.process_dmx(project, &dmx_data, collections);
}
}
fn process_dmx(
&mut self,
project: &mut Option<Project>,
dmx_data: &[u8],
collections: &Collections,
) {
let mut remaining = (dmx_data, 0);
let mut scene_state: ArtnetSceneControlState;
for i in 0..ARTNET_CONTROL_SLOTS {
(remaining, scene_state) = match ArtnetSceneControlState::from_bytes(remaining) {
Ok(result) => result,
Err(_) => break,
};
if let Some(project) = project {
let grid_location = project
.grid_location_from_continuous_index(i, &GridLocation { row: 0, col: 0 });
let scene_instance = project.scenes_instances_grid.get_mut(&grid_location);
let asset_id =
Asset::get_asset_from_index(scene_state.scene_index as usize, collections)
.map(|asset| asset.id);
let scene_data = match (asset_id, scene_instance) {
(None, _) => {
project.scenes_instances_grid.remove(&grid_location);
None
}
(Some(asset_id), None) => {
project.add_scene_instance(
grid_location,
SceneInstance::from_scene_id(asset_id, collections),
);
project
.scenes_instances_grid
.get_mut(&grid_location)
.map(|scene_instance| (asset_id, scene_instance))
}
(Some(asset_id), Some(scene_instance)) => Some((asset_id, scene_instance)),
};
if let Some((asset_id, scene_instance)) = scene_data {
let prev_state = &self.prev_values[i];
update_scene_instance(
&mut scene_state,
asset_id,
scene_instance,
prev_state,
collections,
);
}
} else {
tracing::warn!("Received Artnet DMX data, but no project is loaded");
}
self.prev_values[i] = scene_state;
}
}
}
fn update_scene_instance(
scene_state: &mut ArtnetSceneControlState,
asset_id: AssetId<Scene>,
scene_instance: &mut SceneInstance,
prev_state: &ArtnetSceneControlState,
collections: &Collections,
) {
if prev_state.scene_index != scene_state.scene_index {
scene_instance.scene = Asset::get(asset_id, collections)
.unwrap_or_default()
.data
.clone();
}
scene_instance.active = scene_state.opacity != 0;
scene_instance.opacity.multiplier = scene_state.opacity as f32 / 255.0;
scene_instance.beat_progression_offset.multiplier = scene_state.speed_multiplier as f32 / 127.0;
}