Skip to main content

rlx_core/preset/schema/
hold.rs

1//! The musical edge a `[hold]` entry re-samples its binding on.
2//!
3//! A compiled shape rather than an on-disk one, in [`Easing`](super::Easing)'s
4//! position and for its reason -- a preset's `[hold]` table is what produces it,
5//! and `raw::RawHold` is what the table deserializes into before validation.
6
7/// When a held binding takes a new value (ADR-0180 rule 2). Between edges the
8/// scene keeps reading the value taken at the last one.
9///
10/// The two named edges are counters the analysis frame already publishes, not
11/// conditions this type evaluates: `beat` is the frame's own one-frame beat
12/// gate and `bar` is a change in its bar counter. A [`Period`](Self::Period)
13/// re-samples on the render clock instead, which is the only edge in the
14/// vocabulary that exists on a silent stream.
15#[derive(Debug, Clone, Copy, PartialEq)]
16pub enum HoldEdge {
17    /// Every frame the analysis frame's beat gate fires.
18    Beat,
19    /// Every change of the analysis frame's bar counter. That counter is the
20    /// confidence-gated downbeat estimate where the tracker has locked and its
21    /// own tempo-driven count everywhere else, so this steps on something
22    /// musical without promising it is the downbeat.
23    Bar,
24    /// Every `n` seconds of render time, `n > 0`. Re-sampling restarts the
25    /// interval from the frame it happened on, so a long frame delays the next
26    /// edge rather than banking a debt of them.
27    Period(f32),
28}
29
30impl HoldEdge {
31    /// The two named edges, for the load error's "expected one of" listing and
32    /// for the reference, which renders this rather than restating it.
33    pub const NAMED: [HoldEdge; 2] = [HoldEdge::Beat, HoldEdge::Bar];
34
35    /// Parse a named edge, or `None` if the word is not one. A period is not
36    /// spelled here -- it arrives as a number, or as a numeric string.
37    pub fn from_name(name: &str) -> Option<Self> {
38        Some(match name {
39            "beat" => HoldEdge::Beat,
40            "bar" => HoldEdge::Bar,
41            _ => return None,
42        })
43    }
44
45    /// The canonical name of a named edge -- [`from_name`](Self::from_name)'s
46    /// inverse. A period has no name and renders as its own number.
47    pub fn as_str(self) -> &'static str {
48        match self {
49            HoldEdge::Beat => "beat",
50            HoldEdge::Bar => "bar",
51            HoldEdge::Period(_) => "seconds",
52        }
53    }
54}