Skip to main content

gled_model/sound/
trigger_ref.rs

1//! A sound trigger as part of a curve: its settings and a stable id. The
2//! settings live in the project, so they are saved, cloned and synced like
3//! every other value. The running trigger state (filtered spectrum, envelope)
4//! lives in `SoundData`, keyed by the id, and follows these settings through
5//! `SoundData::sync_triggers`.
6
7use std::collections::HashMap;
8
9use serde::{Deserialize, Serialize};
10use uuid::Uuid;
11
12use super::trigger_params::{FREQ_BINS, SoundTriggerParams};
13
14/// Settings of one sound trigger. Projects saved before triggers had an id get
15/// a fresh one when they are loaded.
16#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
17pub struct SoundTriggerRef {
18    #[serde(default = "Uuid::new_v4")]
19    pub id: Uuid,
20    pub params: SoundTriggerParams,
21    pub f_center: f32,
22    pub f_radius: f32,
23    pub(crate) averaging_time: f32,
24}
25
26impl Default for SoundTriggerRef {
27    fn default() -> Self {
28        Self {
29            id: Uuid::new_v4(),
30            params: SoundTriggerParams::default(),
31            f_center: 8200.,
32            f_radius: 990.,
33            averaging_time: 0.1,
34        }
35    }
36}
37
38/// The current output of the running sound triggers, as curves read it. On
39/// the machine with the audio input this is `SoundData`; a remote client gets
40/// the levels from the server.
41pub trait SoundLevels {
42    /// Output level of a trigger in 0..=1, 0 while it is not running.
43    fn level(&self, id: &Uuid) -> f32;
44    /// The current spectrum, as the little-endian `f32` bytes effects read.
45    fn spectrum_bytes(&self) -> [u8; FREQ_BINS * 4];
46}
47
48/// The settings of every sound trigger a value uses, by id.
49pub type SoundTriggerSet = HashMap<Uuid, SoundTriggerParams>;
50
51/// Values that can contain sound triggers.
52pub trait CollectSoundTriggers {
53    /// Adds the triggers of `self` to `triggers`. When two triggers share an
54    /// id, the first one wins; copies of a value share their trigger.
55    fn collect_sound_triggers(&self, triggers: &mut SoundTriggerSet);
56}
57
58impl CollectSoundTriggers for SoundTriggerRef {
59    fn collect_sound_triggers(&self, triggers: &mut SoundTriggerSet) {
60        triggers.entry(self.id).or_insert(self.params);
61    }
62}
63
64impl<T: CollectSoundTriggers> CollectSoundTriggers for Option<T> {
65    fn collect_sound_triggers(&self, triggers: &mut SoundTriggerSet) {
66        if let Some(value) = self {
67            value.collect_sound_triggers(triggers);
68        }
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn loads_triggers_saved_without_an_id() {
78        let json = serde_json::to_string(&SoundTriggerRef::default()).expect("serializes");
79        let mut value: serde_json::Value = serde_json::from_str(&json).expect("json");
80        value.as_object_mut().expect("object").remove("id");
81        let old: SoundTriggerRef = serde_json::from_value(value).expect("loads without id");
82        assert_eq!(old.f_center, 8200.);
83    }
84
85    #[test]
86    fn copies_share_their_trigger() {
87        let trigger = SoundTriggerRef::default();
88        let mut set = SoundTriggerSet::new();
89        trigger.collect_sound_triggers(&mut set);
90        trigger.clone().collect_sound_triggers(&mut set);
91        assert_eq!(set.len(), 1);
92    }
93}