1use crate::{Error, Result};
3use std::collections::BTreeMap;
4use std::str::FromStr;
5
6pub const MAX_SYNTH_CONTROLS: usize = 65536;
8pub(super) const PARAMETERS: usize = 9;
9
10#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
14#[repr(usize)]
15pub enum ControlParameter {
16 Expression,
18 Pan,
20 CutoffHz,
22 ResonanceQ,
24 PitchCents,
26 PitchLfoDepth,
28 CutoffLfoDepth,
30 AmplitudeLfoDepth,
32 PanLfoDepth,
34}
35impl ControlParameter {
36 pub(super) fn validate(self, value: f64) -> Result<()> {
37 let (min, max) = match self {
38 Self::Expression => (0.0, 1.0),
39 Self::Pan => (-1.0, 1.0),
40 Self::CutoffHz => (1.0, 96000.0),
41 Self::ResonanceQ => (0.5, 10.0),
42 Self::PitchCents => (-2400.0, 2400.0),
43 Self::PitchLfoDepth => (0.0, 2400.0),
44 Self::CutoffLfoDepth => (0.0, 9600.0),
45 Self::AmplitudeLfoDepth | Self::PanLfoDepth => (0.0, 100.0),
46 };
47 if !value.is_finite() || !(min..=max).contains(&value) {
48 return Err(Error::invalid(
49 "Synth control value exceeds its parameter range",
50 ));
51 }
52 Ok(())
53 }
54}
55impl FromStr for ControlParameter {
56 type Err = Error;
57 fn from_str(name: &str) -> Result<Self> {
58 Ok(match name {
59 "expression" => Self::Expression,
60 "pan" => Self::Pan,
61 "cutoff-hz" => Self::CutoffHz,
62 "resonance-q" => Self::ResonanceQ,
63 "pitch-cents" => Self::PitchCents,
64 "pitch-lfo-depth" => Self::PitchLfoDepth,
65 "cutoff-lfo-depth" => Self::CutoffLfoDepth,
66 "amplitude-lfo-depth" => Self::AmplitudeLfoDepth,
67 "pan-lfo-depth" => Self::PanLfoDepth,
68 _ => return Err(Error::invalid("Unsupported synth control parameter")),
69 })
70 }
71}
72
73#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
76pub enum ControlTarget {
77 Instrument { track: usize, instrument: usize },
79 Note { track: usize, note: usize },
82}
83
84#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
86pub enum ControlCurve {
87 #[default]
88 Linear,
89 Exponential,
92}
93
94#[derive(Clone, Copy, Debug, PartialEq)]
98pub struct ControlEvent {
99 pub frame: u64,
100 pub target: ControlTarget,
101 pub parameter: ControlParameter,
102 pub value: f64,
103 pub ramp_frames: u64,
104 pub curve: ControlCurve,
105}
106impl ControlEvent {
107 pub fn new(frame: u64, target: ControlTarget, parameter: ControlParameter, value: f64) -> Self {
108 Self {
109 frame,
110 target,
111 parameter,
112 value,
113 ramp_frames: 0,
114 curve: ControlCurve::Linear,
115 }
116 }
117}
118
119#[derive(Clone, Copy, Debug, Eq, PartialEq)]
122pub struct ControlId(pub u32);
123#[derive(Clone, Copy, Debug, Eq, PartialEq)]
124pub enum ControlStatus {
125 Applied,
126 TargetInactive,
127}
128#[derive(Clone, Copy, Debug, Eq, PartialEq)]
130pub struct ControlReport {
131 pub id: ControlId,
132 pub frame: u64,
133 pub status: ControlStatus,
134}
135
136#[derive(Clone, Copy, Debug)]
137pub(super) struct Ramp {
138 start: u64,
139 end: u64,
140 from: f64,
141 to: f64,
142 curve: ControlCurve,
143 log_from: f64,
144 log_to: f64,
145}
146impl Ramp {
147 fn new(from: f64, event: ControlEvent) -> Result<Self> {
148 let (log_from, log_to) = if event.curve == ControlCurve::Exponential {
149 if from <= 0.0 || event.value <= 0.0 {
150 return Err(Error::invalid(
151 "Exponential synth controls require positive endpoints",
152 ));
153 }
154 (crate::math::log2(from), crate::math::log2(event.value))
155 } else {
156 (0.0, 0.0)
157 };
158 Ok(Self {
159 start: event.frame,
160 end: event
161 .frame
162 .checked_add(event.ramp_frames)
163 .ok_or_else(|| Error::limit("Synth ramp frame overflow"))?,
164 from,
165 to: event.value,
166 curve: event.curve,
167 log_from,
168 log_to,
169 })
170 }
171 pub(super) fn value(self, frame: u64) -> f64 {
172 if frame >= self.end {
173 return self.to;
174 }
175 if frame <= self.start {
176 return self.from;
177 }
178 let t = (frame - self.start) as f64 / (self.end - self.start) as f64;
179 let value = match self.curve {
180 ControlCurve::Linear => self.from + (self.to - self.from) * t,
181 ControlCurve::Exponential => {
182 crate::math::exp2(self.log_from + (self.log_to - self.log_from) * t)
183 }
184 };
185 value.clamp(self.from.min(self.to), self.from.max(self.to))
186 }
187}
188pub(super) type Lanes = [Option<Ramp>; PARAMETERS];
189#[derive(Clone, Copy, Debug, Default)]
190pub(super) struct Values {
191 mask: u16,
192 values: [f64; PARAMETERS],
193}
194impl Values {
195 pub(super) fn get(self, p: ControlParameter) -> Option<f64> {
196 (self.mask & (1 << p as usize) != 0).then_some(self.values[p as usize])
197 }
198 fn set(&mut self, p: usize, value: f64) {
199 self.mask |= 1 << p;
200 self.values[p] = value;
201 }
202 pub(super) fn some(self) -> Option<Self> {
203 (self.mask != 0).then_some(self)
204 }
205}
206#[derive(Clone, Copy, Debug)]
207pub(super) struct Resolved {
208 pub group: usize,
209 pub note: Option<(usize, usize)>,
210}
211#[derive(Clone, Copy, Debug)]
212pub(super) struct Scheduled {
213 pub id: ControlId,
214 pub event: ControlEvent,
215 target: Resolved,
216 ramp: Ramp,
217}
218#[derive(Clone, Debug)]
219pub(super) struct VoiceLanes {
220 note: Option<usize>,
221 lanes: Lanes,
222}
223#[derive(Debug)]
224pub(super) struct ControlState {
225 pub queue: Vec<Scheduled>,
226 pub next: usize,
227 pub capacity: usize,
228 pub used: Vec<bool>,
229 groups: Vec<Lanes>,
230 voices: Vec<Vec<VoiceLanes>>,
231 reports: Vec<ControlReport>,
232 poll: usize,
233}
234impl Clone for ControlState {
235 fn clone(&self) -> Self {
236 let mut queue = Vec::with_capacity(self.capacity);
238 queue.extend_from_slice(&self.queue);
239 let mut reports = Vec::with_capacity(self.capacity);
240 reports.extend_from_slice(&self.reports);
241 Self {
242 queue,
243 reports,
244 next: self.next,
245 capacity: self.capacity,
246 used: self.used.clone(),
247 groups: self.groups.clone(),
248 voices: self.voices.clone(),
249 poll: self.poll,
250 }
251 }
252}
253pub(super) fn compile(
254 entries: &[(ControlId, ControlEvent)],
255 capacity: usize,
256 resolve: impl Fn(ControlEvent) -> Result<(Resolved, f64)>,
257) -> Result<Vec<Scheduled>> {
258 if entries.windows(2).any(|p| p[0].1.frame > p[1].1.frame) {
259 return Err(Error::invalid("Synth controls must be frame-ordered"));
260 }
261 let mut queue = Vec::new();
262 queue
263 .try_reserve_exact(capacity)
264 .map_err(|_| Error::limit("Cannot reserve synth controls"))?;
265 let mut history: BTreeMap<(usize, Option<usize>, ControlParameter), Ramp> = BTreeMap::new();
266 for &(id, event) in entries {
267 event.parameter.validate(event.value)?;
268 let (target, base) = resolve(event)?;
269 let key = (
270 target.group,
271 target.note.map(|(_, note)| note),
272 event.parameter,
273 );
274 let from = history
275 .get(&key)
276 .or_else(|| history.get(&(target.group, None, event.parameter)))
277 .map_or(base, |r| r.value(event.frame));
278 let ramp = Ramp::new(from, event)?;
279 history.insert(key, ramp);
280 queue.push(Scheduled {
281 id,
282 event,
283 target,
284 ramp,
285 });
286 }
287 Ok(queue)
288}
289impl ControlState {
290 pub fn new(
291 used: Vec<bool>,
292 voice_counts: impl Iterator<Item = usize>,
293 capacity: usize,
294 ) -> Result<Self> {
295 let groups = vec![[None; PARAMETERS]; used.len()];
296 let voices = voice_counts
297 .map(|n| {
298 vec![
299 VoiceLanes {
300 note: None,
301 lanes: [None; PARAMETERS]
302 };
303 n
304 ]
305 })
306 .collect();
307 let mut reports = Vec::new();
308 reports
309 .try_reserve_exact(capacity)
310 .map_err(|_| Error::limit("Cannot reserve synth control reports"))?;
311 Ok(Self {
312 queue: Vec::new(),
313 next: 0,
314 capacity,
315 used,
316 groups,
317 voices,
318 reports,
319 poll: 0,
320 })
321 }
322 pub fn sync(&mut self, track: usize, slot: usize, note: Option<usize>) {
323 let state = &mut self.voices[track][slot];
324 if state.note != note {
325 state.note = note;
326 state.lanes.fill(None);
327 }
328 }
329 pub fn advance(&mut self, frame: u64) {
330 while let Some(c) = self.queue.get(self.next).copied() {
331 if c.event.frame > frame {
332 break;
333 }
334 let lanes = match c.target.note {
335 None => Some(&mut self.groups[c.target.group]),
336 Some((track, note)) => self.voices[track]
337 .iter_mut()
338 .find(|v| v.note == Some(note))
339 .map(|v| &mut v.lanes),
340 };
341 let status = if let Some(lanes) = lanes {
342 lanes[c.event.parameter as usize] = Some(c.ramp);
343 ControlStatus::Applied
344 } else {
345 ControlStatus::TargetInactive
346 };
347 self.reports.push(ControlReport {
348 id: c.id,
349 frame,
350 status,
351 });
352 self.next += 1;
353 }
354 }
355 pub fn values(&self, group: usize, track: usize, slot: usize, frame: u64) -> Values {
356 let mut out = Values::default();
357 for (p, (note, instrument)) in self.voices[track][slot]
358 .lanes
359 .iter()
360 .zip(self.groups[group].iter())
361 .enumerate()
362 {
363 if let Some(ramp) = note.or(*instrument) {
364 out.set(p, ramp.value(frame));
365 }
366 }
367 out
368 }
369 pub fn group_value(&self, group: usize, p: ControlParameter, frame: u64) -> Option<f64> {
370 self.groups[group][p as usize].map(|r| r.value(frame))
371 }
372 pub fn poll(&mut self) -> Option<ControlReport> {
373 let report = self.reports.get(self.poll).copied()?;
374 self.poll += 1;
375 Some(report)
376 }
377 pub fn reset(&mut self) {
378 self.next = 0;
379 self.poll = 0;
380 self.reports.clear();
381 self.groups.fill([None; PARAMETERS]);
382 for track in &mut self.voices {
383 for voice in track {
384 voice.note = None;
385 voice.lanes.fill(None);
386 }
387 }
388 }
389}