Skip to main content

sva_engine/render/
until.rs

1// Concern: the condition a render stops at, and the first sample it holds at | Non-concern: parsing it, where a render's range ends (render/mod.rs) | IO: (Until, the root's samples) -> a sample
2
3use std::fmt;
4
5/// Comparisons over `t` and the measured level, joined by `and` and `or`.
6#[derive(Clone, Debug, PartialEq)]
7pub enum Until {
8    Holds(Term, Cmp, Term),
9    All(Box<Until>, Box<Until>),
10    Any(Box<Until>, Box<Until>),
11}
12
13/// `t` and a number are seconds; `envelope(t)` is the RMS of the `envelope` frame holding `t`.
14#[derive(Clone, Copy, Debug, PartialEq)]
15pub enum Term {
16    Time,
17    Number(f64),
18    Envelope,
19}
20
21#[derive(Clone, Copy, Debug, PartialEq, Eq)]
22pub enum Cmp {
23    Lt,
24    Le,
25    Gt,
26    Ge,
27}
28
29impl Cmp {
30    fn holds(self, a: f64, b: f64) -> bool {
31        match self {
32            Cmp::Lt => a < b,
33            Cmp::Le => a <= b,
34            Cmp::Gt => a > b,
35            Cmp::Ge => a >= b,
36        }
37    }
38}
39
40impl Until {
41    pub fn checked(&self) -> Result<(), String> {
42        match self {
43            Until::All(a, b) | Until::Any(a, b) => {
44                a.checked()?;
45                b.checked()
46            }
47            Until::Holds(Term::Time, _, Term::Time) => Err("`t` compares with a number".into()),
48            Until::Holds(Term::Time, _, Term::Envelope)
49            | Until::Holds(Term::Envelope, _, Term::Time) => {
50                Err("`t` is a time and `envelope(t)` a level".into())
51            }
52            Until::Holds(..) => Ok(()),
53        }
54    }
55
56    fn times(&self, out: &mut Vec<f64>) {
57        match self {
58            Until::All(a, b) | Until::Any(a, b) => {
59                a.times(out);
60                b.times(out);
61            }
62            Until::Holds(Term::Time, _, Term::Number(v))
63            | Until::Holds(Term::Number(v), _, Term::Time) => out.push(*v),
64            Until::Holds(..) => {}
65        }
66    }
67
68    /// A level is known only once its whole frame is.
69    pub(crate) fn first(&self, known: &Known, from: i64, to: i64) -> Option<i64> {
70        let frame = known.frame as i64;
71        let first_frame = (from - known.start).div_euclid(frame);
72        let mut candidates: Vec<i64> = (first_frame..)
73            .map(|k| known.start + k * frame)
74            .take_while(|n| *n < to)
75            .map(|n| n.max(from))
76            .collect();
77        let mut times = Vec::new();
78        self.times(&mut times);
79        let sr = f64::from(known.rate);
80        for v in times {
81            let n = (v * sr).round() as i64;
82            candidates.extend((n - 1..=n + 1).filter(|n| (from..to).contains(n)));
83        }
84        candidates.sort_unstable();
85        candidates.dedup();
86        candidates
87            .into_iter()
88            .find(|n| self.holds(known, *n) == Some(true))
89    }
90
91    fn holds(&self, known: &Known, n: i64) -> Option<bool> {
92        match self {
93            Until::All(a, b) => match (a.holds(known, n), b.holds(known, n)) {
94                (Some(false), _) | (_, Some(false)) => Some(false),
95                (Some(true), Some(true)) => Some(true),
96                _ => None,
97            },
98            Until::Any(a, b) => match (a.holds(known, n), b.holds(known, n)) {
99                (Some(true), _) | (_, Some(true)) => Some(true),
100                (Some(false), Some(false)) => Some(false),
101                _ => None,
102            },
103            Until::Holds(a, cmp, b) => Some(cmp.holds(known.value(*a, n)?, known.value(*b, n)?)),
104        }
105    }
106}
107
108/// The root's channels from `base` on, framed from `start` as the `envelope`
109/// representation frames it; `ended` says no sample follows the last one held.
110pub(crate) struct Known<'a> {
111    planes: Vec<&'a [f64]>,
112    base: i64,
113    start: i64,
114    frame: usize,
115    rate: u32,
116    ended: bool,
117}
118
119impl<'a> Known<'a> {
120    pub(crate) fn new(
121        planes: Vec<&'a [f64]>,
122        base: i64,
123        start: i64,
124        frame: usize,
125        rate: u32,
126        ended: bool,
127    ) -> Self {
128        Known {
129            planes,
130            base,
131            start,
132            frame: frame.max(1),
133            rate,
134            ended,
135        }
136    }
137
138    /// A frame the render ends inside is as long as what it holds, as in `envelope`.
139    fn frame_level(&self, n: i64) -> Option<f64> {
140        let frame = self.frame as i64;
141        let from = self.start + (n - self.start).div_euclid(frame) * frame;
142        let end = self.base + self.planes.iter().map(|p| p.len()).min().unwrap_or(0) as i64;
143        if from < self.base {
144            return None;
145        }
146        let to = match from + frame <= end {
147            true => from + frame,
148            false if self.ended => end,
149            false => return None,
150        };
151        let held: Vec<&[f64]> = self
152            .planes
153            .iter()
154            .map(|p| &p[(from - self.base) as usize..(to - self.base) as usize])
155            .collect();
156        Some(sva_samples::measure::envelope::level(&held))
157    }
158
159    fn value(&self, term: Term, n: i64) -> Option<f64> {
160        match term {
161            Term::Time => Some(n as f64 / f64::from(self.rate)),
162            Term::Number(v) => Some(v),
163            Term::Envelope => self.frame_level(n),
164        }
165    }
166}
167
168impl fmt::Display for Term {
169    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170        match self {
171            Term::Time => write!(f, "t"),
172            Term::Number(v) => write!(f, "{v}"),
173            Term::Envelope => write!(f, "envelope(t)"),
174        }
175    }
176}
177
178/// A level beside a number prints the number in dB, a time beside one in seconds.
179impl fmt::Display for Until {
180    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181        let side = |term: &Term, other: &Term| match (term, other) {
182            (Term::Number(v), Term::Envelope) => format!("{:.1}db", 20.0 * v.log10()),
183            (Term::Number(v), Term::Time) => format!("{v}s"),
184            (term, _) => term.to_string(),
185        };
186        match self {
187            Until::All(a, b) => write!(f, "({a} and {b})"),
188            Until::Any(a, b) => write!(f, "({a} or {b})"),
189            Until::Holds(a, cmp, b) => {
190                let op = match cmp {
191                    Cmp::Lt => "<",
192                    Cmp::Le => "<=",
193                    Cmp::Gt => ">",
194                    Cmp::Ge => ">=",
195                };
196                write!(f, "{} {op} {}", side(a, b), side(b, a))
197            }
198        }
199    }
200}