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}