1use std::collections::{BTreeMap, BTreeSet, VecDeque};
4
5use crate::buffer::Buffer;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq)]
8pub enum Unit {
9 Amplitude,
10 Hertz,
11 Q,
12 Decibels,
13 Seconds,
14 Radians,
15 Ratio,
16}
17
18impl Unit {
19 pub fn name(self) -> &'static str {
20 match self {
21 Unit::Amplitude => "amplitude",
22 Unit::Hertz => "hz",
23 Unit::Q => "q",
24 Unit::Decibels => "db",
25 Unit::Seconds => "seconds",
26 Unit::Radians => "radians",
27 Unit::Ratio => "ratio",
28 }
29 }
30}
31
32#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub enum SignalKind {
34 Audio,
35 Control(Unit),
36}
37
38impl SignalKind {
39 pub fn is_audio(self) -> bool {
40 self == SignalKind::Audio
41 }
42
43 pub fn unit(self) -> Unit {
44 match self {
45 SignalKind::Audio => Unit::Amplitude,
46 SignalKind::Control(u) => u,
47 }
48 }
49}
50
51#[derive(Clone, Debug, PartialEq)]
52pub struct LedgerEntry {
53 pub node: String,
54 pub channel: Option<usize>,
55 pub depth: usize,
56 pub kind: SignalKind,
57 pub rms: f64,
58 pub peak: f64,
59 pub min: f64,
60 pub max: f64,
61 pub share: Option<f64>,
64 pub clipped: Option<bool>,
67}
68
69pub struct Sources<'a> {
71 pub read: &'a BTreeMap<String, usize>,
72 pub own: &'a BTreeMap<String, usize>,
73 pub attributed: &'a BTreeSet<String>,
74 pub widths: &'a [usize],
75}
76
77pub struct Tally {
79 rows: Vec<Row>,
80}
81
82struct Row {
83 node: String,
84 depth: usize,
85 kind: SignalKind,
86 attributed: bool,
87 read: Option<usize>,
88 reader: Option<usize>,
89 channels: Vec<Sums>,
90}
91
92#[derive(Clone, Copy)]
93struct Sums {
94 squares: f64,
95 count: usize,
96 peak: f64,
97 min: f64,
98 max: f64,
99 product: f64,
100 energy: f64,
101}
102
103impl Sums {
104 fn new() -> Sums {
105 let zero: f64 = std::iter::empty::<f64>().sum();
107 Sums {
108 squares: zero,
109 count: 0,
110 peak: 0.0,
111 min: f64::INFINITY,
112 max: f64::NEG_INFINITY,
113 product: zero,
114 energy: zero,
115 }
116 }
117}
118
119impl Tally {
120 pub fn new(
121 sources: &Sources,
122 deps: &BTreeMap<String, Vec<String>>,
123 kinds: &BTreeMap<String, SignalKind>,
124 target: &str,
125 depth_limit: usize,
126 ) -> Tally {
127 let width = |path: &str| sources.read.get(path).map_or(1, |s| sources.widths[*s]);
128 let mut seen: BTreeSet<&str> = BTreeSet::new();
129 let mut queue: VecDeque<(&str, usize, &str)> = VecDeque::new();
130 let mut rows = Vec::new();
131 queue.push_back((target, 0, target));
132 seen.insert(target);
133 while let Some((path, depth, reader)) = queue.pop_front() {
134 rows.push(Row {
136 node: path.to_string(),
137 depth,
138 kind: kinds.get(path).copied().unwrap_or(SignalKind::Audio),
139 attributed: path == target || sources.attributed.contains(path),
140 read: sources.read.get(path).copied(),
141 reader: sources
142 .own
143 .get(reader)
144 .copied()
145 .filter(|s| sources.widths[*s] > 0),
146 channels: vec![Sums::new(); width(path)],
147 });
148 if depth >= depth_limit {
149 continue;
150 }
151 for child in deps.get(path).map(Vec::as_slice).unwrap_or(&[]) {
152 if seen.insert(child.as_str()) {
153 let key = deps
154 .get_key_value(child.as_str())
155 .map(|(k, _)| k.as_str())
156 .unwrap_or(child.as_str());
157 queue.push_back((key, depth + 1, path));
158 }
159 }
160 }
161 Tally { rows }
162 }
163
164 pub fn add(&mut self, held: &[Option<&Buffer>], range: std::ops::Range<usize>) {
165 let empty: &[f64] = &[];
166 let slice = |source: Option<usize>, c: usize| -> Option<std::borrow::Cow<'_, [f64]>> {
167 let buffer = held[source?]?;
168 Some(buffer.plane_slice(c % buffer.width(), range.clone()))
169 };
170 for row in &mut self.rows {
171 for (c, sums) in row.channels.iter_mut().enumerate() {
172 let x = match row.read {
173 Some(source) => slice(Some(source), c).expect("a row's own source"),
174 None => std::borrow::Cow::Borrowed(empty),
175 };
176 sums.count += x.len();
177 sums.squares = x.iter().map(|&v| v * v).fold(sums.squares, |a, b| a + b);
178 sums.peak = x.iter().fold(sums.peak, |a, &v| a.max(v.abs()));
179 sums.min = x.iter().fold(sums.min, |a, &v| a.min(v));
180 sums.max = x.iter().fold(sums.max, |a, &v| a.max(v));
181 if let Some(y) = slice(row.reader, c) {
182 sums.energy = y.iter().map(|&v| v * v).fold(sums.energy, |a, b| a + b);
183 let products = x.iter().zip(y.iter()).map(|(a, b)| a * b);
184 sums.product = products.fold(sums.product, |a, b| a + b);
185 }
186 }
187 }
188 }
189
190 pub fn entries(&self) -> Vec<LedgerEntry> {
191 let mut out = Vec::new();
192 for row in &self.rows {
193 let components = row.channels.len();
194 let gated = row.kind.is_audio();
195 for (c, sums) in row.channels.iter().enumerate() {
196 let rms = match sums.count {
197 0 => 0.0,
198 count => (sums.squares / count as f64).sqrt(),
199 };
200 let share = || match (row.reader, sums.energy > 0.0) {
202 (Some(_), true) => sums.product / sums.energy,
203 _ => 0.0,
204 };
205 out.push(LedgerEntry {
206 node: row.node.clone(),
207 channel: (components > 1).then_some(c),
208 depth: row.depth,
209 kind: row.kind,
210 rms,
211 peak: sums.peak,
212 min: sums.min,
213 max: sums.max,
214 share: (gated && row.attributed).then(share),
215 clipped: gated.then_some(sums.peak > 1.0),
216 });
217 }
218 }
219 out
220 }
221}