alpha2_model/audio_load.rs
1//! How much audio effect state a project holds at once.
2//!
3//! [`crate::MAX_AUDIO_EFFECTS`] and [`crate::MAX_AUDIO_DELAY_MS`] bound the state a mixer
4//! holds at one time, not across the whole timeline. The TypeScript validator measures the
5//! same way.
6
7use crate::audio::TICKS_PER_MS;
8use crate::{AUDIO_EFFECT_STATE_MARGIN_MS, AudioEffect, Project};
9
10/// The most audio effect state a project holds at once.
11///
12/// The master's and every track's effects hold state throughout. A clip's effects hold state
13/// from [`crate::AUDIO_EFFECT_STATE_MARGIN_MS`] plus its track's and its own lookahead before
14/// its start (clamped at zero) to its end, both ends inclusive, so a clip ending where
15/// another starts overlaps it. A clip with pitch-preserving stretched audio
16/// ([`crate::Clip::stretches_audio`]) counts one effect more.
17#[derive(Clone, Copy, Debug, PartialEq, Eq)]
18pub struct AudioEffectLoad {
19 /// The most effects holding state at once.
20 pub effects: usize,
21 /// The earliest timeline position, in ticks, at which `effects` hold state.
22 pub effects_at_ticks: i64,
23 /// The most delay held at once, in ticks (120 per millisecond), summed from each effect's
24 /// [`AudioEffect::delay_ticks`].
25 pub delay_ticks: u64,
26 /// The earliest timeline position, in ticks, at which `delay_ticks` is held.
27 pub delay_at_ticks: i64,
28}
29
30impl AudioEffectLoad {
31 /// [`Self::delay_ticks`] in milliseconds, rounded up.
32 #[must_use]
33 pub fn delay_ms(&self) -> u64 {
34 self.delay_ticks.div_ceil(u64::from(TICKS_PER_MS))
35 }
36}
37
38/// One end of the interval in which a clip's effects hold state.
39struct Edge {
40 at: i64,
41 /// Whether the interval ends here; at one position, starts sort first.
42 ends: bool,
43 effects: usize,
44 delay: u64,
45}
46
47impl Project {
48 /// Measures the most audio effect state this project holds at once, which validation bounds
49 /// by [`crate::MAX_AUDIO_EFFECTS`] and [`crate::MAX_AUDIO_DELAY_MS`]. O(n log n) in the clips
50 /// with effects.
51 #[must_use]
52 pub fn audio_effect_load(&self) -> AudioEffectLoad {
53 let chain = |effects: &[AudioEffect]| {
54 (
55 effects.len(),
56 effects.iter().map(AudioEffect::delay_ticks).sum::<u64>(),
57 )
58 };
59 let lookahead = |effects: &[AudioEffect]| {
60 effects
61 .iter()
62 .map(AudioEffect::lookahead_ticks)
63 .sum::<u64>()
64 };
65 let margin = u64::from(AUDIO_EFFECT_STATE_MARGIN_MS) * u64::from(TICKS_PER_MS);
66
67 let (mut effects, mut delay) = chain(&self.audio.effects);
68 let mut edges = Vec::new();
69 for track in &self.timeline.tracks {
70 let (count, held) = chain(&track.audio.effects);
71 effects += count;
72 delay += held;
73 let track_lookahead = lookahead(&track.audio.effects);
74 for clip in &track.clips {
75 // A stretched read holds state like one effect, without delay.
76 let stretch = usize::from(clip.stretches_audio());
77 if clip.audio.effects.is_empty() && stretch == 0 {
78 continue;
79 }
80 let (count, held) = chain(&clip.audio.effects);
81 let count = count + stretch;
82 let lead = track_lookahead + lookahead(&clip.audio.effects) + margin;
83 let lead = i64::try_from(lead).unwrap_or(i64::MAX);
84 let range = clip.timeline_range;
85 edges.push(Edge {
86 at: range.start().ticks().saturating_sub(lead).max(0),
87 ends: false,
88 effects: count,
89 delay: held,
90 });
91 edges.push(Edge {
92 at: range.end().ticks(),
93 ends: true,
94 effects: count,
95 delay: held,
96 });
97 }
98 }
99 edges.sort_unstable_by_key(|edge| (edge.at, edge.ends));
100
101 let mut load = AudioEffectLoad {
102 effects,
103 effects_at_ticks: 0,
104 delay_ticks: delay,
105 delay_at_ticks: 0,
106 };
107 for edge in edges {
108 if edge.ends {
109 effects -= edge.effects;
110 delay -= edge.delay;
111 continue;
112 }
113 effects += edge.effects;
114 delay += edge.delay;
115 if effects > load.effects {
116 load.effects = effects;
117 load.effects_at_ticks = edge.at;
118 }
119 if delay > load.delay_ticks {
120 load.delay_ticks = delay;
121 load.delay_at_ticks = edge.at;
122 }
123 }
124 load
125 }
126}