midi_toolkit/sequence/event/
stats.rs1use std::{ops::Deref, sync::Arc, time::Duration};
2
3use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
4
5use crate::{
6 events::{Event, MIDIDelta, MIDIEventEnum, TempoEvent},
7 num::MIDINum,
8 pipe,
9 sequence::{event::merge_events_array, to_vec, to_vec_result, wrap_ok},
10};
11
12use super::Delta;
13
14struct ElementCountDebug(&'static str, usize);
15
16impl std::fmt::Debug for ElementCountDebug {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 write!(f, "[{}; {}]", self.0, self.1)
19 }
20}
21
22#[derive(Clone)]
24pub struct ChannelStatistics<D: MIDINum> {
25 note_on_count: u64,
26 note_off_count: u64,
27 total_event_count: u64,
28 total_length_ticks: D,
29 tempo_events: Arc<[Delta<D, TempoEvent>]>,
30}
31
32impl<T: MIDINum> ChannelStatistics<T> {
33 pub fn note_on_count(&self) -> u64 {
35 self.note_on_count
36 }
37
38 pub fn note_count(&self) -> u64 {
40 self.note_on_count()
41 }
42
43 pub fn note_off_count(&self) -> u64 {
45 self.note_off_count
46 }
47
48 pub fn other_event_count(&self) -> u64 {
52 self.total_event_count - self.note_on_count - self.note_off_count
53 }
54
55 pub fn total_event_count(&self) -> u64 {
57 self.total_event_count
58 }
59
60 pub fn total_length_ticks(&self) -> T {
62 self.total_length_ticks
63 }
64
65 pub fn calculate_total_duration(&self, ppq: u16) -> Duration {
68 tempo_sequence_get_duration(&self.tempo_events, ppq, self.total_length_ticks)
69 }
70}
71
72impl<T: MIDINum> std::fmt::Debug for ChannelStatistics<T> {
73 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
74 f.debug_struct("ChannelStatistics")
75 .field("note_on_count", &self.note_on_count)
76 .field("note_off_count", &self.note_off_count)
77 .field("total_event_count", &self.total_event_count)
78 .field("other_event_count", &self.other_event_count())
79 .field("total_length_ticks", &self.total_length_ticks)
80 .field(
81 "tempo_events",
82 &ElementCountDebug("TempoEvent", self.tempo_events.len()),
83 )
84 .finish()
85 }
86}
87
88pub struct ChannelGroupStatistics<T: MIDINum> {
90 group: ChannelStatistics<T>,
91 channels: Vec<ChannelStatistics<T>>,
92}
93
94impl<T: MIDINum> ChannelGroupStatistics<T> {
95 pub fn channels(&self) -> &[ChannelStatistics<T>] {
97 &self.channels
98 }
99}
100
101impl<T: MIDINum> Deref for ChannelGroupStatistics<T> {
102 type Target = ChannelStatistics<T>;
103
104 fn deref(&self) -> &Self::Target {
105 &self.group
106 }
107}
108
109impl<T: MIDINum> std::fmt::Debug for ChannelGroupStatistics<T> {
110 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
111 f.debug_struct("ChannelGroupStatistics")
112 .field("group", &self.group)
113 .field(
114 "channels",
115 &ElementCountDebug("ChannelStatistics", self.channels.len()),
116 )
117 .finish()
118 }
119}
120
121pub fn tempo_sequence_get_duration<T: MIDINum>(
122 tempos: &[Delta<T, TempoEvent>],
123 ppq: u16,
124 ticks: T,
125) -> Duration {
126 let mut ticks = ticks;
127 let mut time = 0.0;
128 let mut multiplier = (500000.0 / ppq as f64) / 1000000.0;
129 for t in tempos {
130 let offset = t.delta();
131 if offset > ticks {
132 break;
133 }
134 ticks -= offset;
135
136 let offset: f64 = offset.midi_num_into();
137 time += multiplier * offset;
138 multiplier = (t.tempo as f64 / ppq as f64) / 1000000.0;
139 }
140 let ticks: f64 = ticks.midi_num_into();
141 time += multiplier * ticks;
142 Duration::from_secs_f64(time)
143}
144
145pub fn get_channel_statistics<D: MIDINum, E: MIDIEventEnum + MIDIDelta<D>, Err>(
150 iter: impl Iterator<Item = Result<E, Err>>,
151) -> Result<ChannelStatistics<D>, Err> {
152 let mut note_on_count = 0;
153 let mut note_off_count = 0;
154 let mut total_event_count = 0;
155 let mut total_length_ticks = D::zero();
156 let mut ticks_since_last_tempo = D::zero();
157
158 let mut tempo_events = Vec::new();
159
160 for event in iter {
161 let event = event?;
162 total_event_count += 1;
163 total_length_ticks += event.delta();
164 ticks_since_last_tempo += event.delta();
165 match event.as_event() {
166 Event::NoteOn(_) => note_on_count += 1,
167 Event::NoteOff(_) => note_off_count += 1,
168 Event::Tempo(t) => {
169 let ev = *t.clone();
170 let tempo_delta = Delta::new(ticks_since_last_tempo, ev);
171 tempo_events.push(tempo_delta);
172 ticks_since_last_tempo = D::zero();
173 }
174 _ => (),
175 }
176 }
177
178 Ok(ChannelStatistics {
179 note_on_count,
180 note_off_count,
181 total_event_count,
182 total_length_ticks,
183 tempo_events: tempo_events.into(),
184 })
185}
186
187pub fn get_channels_array_statistics<
193 D: MIDINum,
194 E: MIDIEventEnum + MIDIDelta<D>,
195 Err: Send,
196 I: Iterator<Item = Result<E, Err>> + Sized + Send,
197>(
198 iters: Vec<I>,
199) -> Result<ChannelGroupStatistics<D>, Err> {
200 let pool = iters
201 .into_par_iter()
202 .map(|iter| get_channel_statistics(iter));
203 let mut result = Vec::new();
204 pool.collect_into_vec(&mut result);
205 let mut channels = pipe!(result.into_iter()|>to_vec_result())?;
206
207 let tempo_vecs: Vec<_> = channels.iter().map(|c| c.tempo_events.clone()).collect();
208 let tempo_iterators = tempo_vecs
209 .into_iter()
210 .map(|tempos| pipe!(tempos.iter().cloned()|>wrap_ok()|>to_vec().into_iter()))
211 .collect();
212
213 let merge = pipe!(tempo_iterators|>merge_events_array()|>to_vec_result().unwrap());
214
215 let tempo_events: Arc<[Delta<D, TempoEvent>]> = merge.into();
216
217 for c in channels.iter_mut() {
218 c.tempo_events = tempo_events.clone();
219 }
220
221 let mut max_tick_length = D::zero();
222 for c in channels.iter() {
223 if c.total_length_ticks > max_tick_length {
224 max_tick_length = c.total_length_ticks;
225 }
226 }
227
228 let group = ChannelStatistics {
229 note_on_count: channels.iter().map(|c| c.note_on_count).sum(),
230 note_off_count: channels.iter().map(|c| c.note_off_count).sum(),
231 total_event_count: channels.iter().map(|c| c.total_event_count).sum(),
232 total_length_ticks: max_tick_length,
233 tempo_events,
234 };
235
236 Ok(ChannelGroupStatistics { group, channels })
237}