1use fundsp::prelude::*;
2
3use crate::abnormal::AbnormalSample;
4use crate::chart::generate_svg;
5use crate::config::{Processing, SnapshotConfig, SnapshotOutputMode};
6use crate::input::InputSource;
7use crate::wav::generate_wav;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum SnapshotAbnormalSample {
17 Nan,
19 NegInf,
21 PosInf,
23}
24
25impl std::fmt::Display for SnapshotAbnormalSample {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 match self {
28 SnapshotAbnormalSample::Nan => write!(f, "NaN"),
29 SnapshotAbnormalSample::NegInf => write!(f, "-∞"),
30 SnapshotAbnormalSample::PosInf => write!(f, "∞"),
31 }
32 }
33}
34
35impl From<AbnormalSample> for SnapshotAbnormalSample {
36 fn from(value: AbnormalSample) -> Self {
37 match value {
38 AbnormalSample::Nan => Self::Nan,
39 AbnormalSample::NegInf => Self::NegInf,
40 AbnormalSample::PosInf => Self::PosInf,
41 }
42 }
43}
44
45impl From<SnapshotAbnormalSample> for AbnormalSample {
46 fn from(value: SnapshotAbnormalSample) -> Self {
47 match value {
48 SnapshotAbnormalSample::Nan => Self::Nan,
49 SnapshotAbnormalSample::NegInf => Self::NegInf,
50 SnapshotAbnormalSample::PosInf => Self::PosInf,
51 }
52 }
53}
54
55#[derive(Debug, Clone)]
62pub struct AudioUnitSnapshotData {
63 pub input_data: Vec<Vec<f32>>,
65 pub output_data: Vec<Vec<f32>>,
67 pub abnormalities: Vec<Vec<(usize, SnapshotAbnormalSample)>>,
73 pub sample_rate: f64,
75 pub num_samples: usize,
77 pub start_sample: usize,
80}
81
82pub fn snapshot_audio_unit<N>(unit: N) -> Vec<u8>
94where
95 N: AudioUnit,
96{
97 snapshot_audio_unit_with_input_and_options(unit, InputSource::None, SnapshotConfig::default())
98}
99
100pub fn snapshot_audio_unit_data<N>(unit: N) -> AudioUnitSnapshotData
116where
117 N: AudioUnit,
118{
119 snapshot_audio_unit_data_with_input_and_options(unit, InputSource::None, SnapshotConfig::default())
120}
121
122pub fn snapshot_audio_unit_with_options<N>(unit: N, options: SnapshotConfig) -> Vec<u8>
135where
136 N: AudioUnit,
137{
138 snapshot_audio_unit_with_input_and_options(unit, InputSource::None, options)
139}
140
141pub fn snapshot_audio_unit_data_with_options<N>(unit: N, options: SnapshotConfig) -> AudioUnitSnapshotData
157where
158 N: AudioUnit,
159{
160 snapshot_audio_unit_data_with_input_and_options(unit, InputSource::None, options)
161}
162
163pub fn snapshot_audio_unit_with_input<N>(unit: N, input_source: InputSource) -> Vec<u8>
176where
177 N: AudioUnit,
178{
179 snapshot_audio_unit_with_input_and_options(
180 unit,
181 input_source,
182 SnapshotConfig {
183 ..SnapshotConfig::default()
184 },
185 )
186}
187
188pub fn snapshot_audio_unit_data_with_input<N>(unit: N, input_source: InputSource) -> AudioUnitSnapshotData
204where
205 N: AudioUnit,
206{
207 snapshot_audio_unit_data_with_input_and_options(
208 unit,
209 input_source,
210 SnapshotConfig {
211 ..SnapshotConfig::default()
212 },
213 )
214}
215
216pub fn snapshot_audio_unit_with_input_and_options<N>(
230 unit: N,
231 input_source: InputSource,
232 config: SnapshotConfig,
233) -> Vec<u8>
234where
235 N: AudioUnit,
236{
237 let snapshot_data = capture_audio_unit_data(unit, input_source, &config);
238 render_snapshot_output(&snapshot_data, &config.output_mode)
239}
240
241pub fn snapshot_audio_unit_data_with_input_and_options<N>(
262 unit: N,
263 input_source: InputSource,
264 config: SnapshotConfig,
265) -> AudioUnitSnapshotData
266where
267 N: AudioUnit,
268{
269 capture_audio_unit_data(unit, input_source, &config)
270}
271
272fn capture_audio_unit_data<N>(
273 mut unit: N,
274 mut input_source: InputSource,
275 config: &SnapshotConfig,
276) -> AudioUnitSnapshotData
277where
278 N: AudioUnit,
279{
280 let num_inputs = N::inputs(&unit);
281 let num_outputs = N::outputs(&unit);
282
283 unit.set_sample_rate(config.sample_rate);
284 unit.reset();
285 unit.allocate();
286
287 let input_data = input_source.make_data(num_inputs, config.num_samples);
288
289 let mut output_data: Vec<Vec<f32>> = vec![vec![]; num_outputs];
290
291 let warmup_samples = config
292 .warm_up
293 .warm_up_samples(config.sample_rate, num_inputs);
294
295 let num_warmup_samples = warmup_samples
296 .iter()
297 .map(|ch| ch.len())
298 .next()
299 .unwrap_or_default();
300
301 let mut abnormalities: Vec<Vec<(usize, SnapshotAbnormalSample)>> = vec![vec![]; num_outputs];
302
303 let mut checked_sample = |mut sample: f32, ch: usize, i: usize| {
304 if sample.is_nan() || sample.is_infinite() {
305 let abnormality = SnapshotAbnormalSample::from(AbnormalSample::from(sample));
306
307 if config.allow_abnormal_samples {
308 abnormalities[ch].push((i, abnormality));
309 sample = 0.0;
310 } else {
311 panic!("Output channel #[{ch}] at sample [{i}] produced [{abnormality}] sample");
312 }
313 }
314 sample
315 };
316
317 (0..num_warmup_samples).for_each(|i| {
318 let mut input_frame = vec![0.0; num_inputs];
319 for ch in 0..num_inputs {
320 input_frame[ch] = warmup_samples[ch][i];
321 }
322 let mut output_frame = vec![0.0; num_outputs];
323 unit.tick(&input_frame, &mut output_frame);
324 });
326
327 match config.processing_mode {
328 Processing::Tick => {
329 (0..config.num_samples).for_each(|i| {
330 let mut input_frame = vec![0.0; num_inputs];
331 for ch in 0..num_inputs {
332 input_frame[ch] = input_data[ch][i];
333 }
334 let mut output_frame = vec![0.0; num_outputs];
335 unit.tick(&input_frame, &mut output_frame);
336 for ch in 0..num_outputs {
337 let sample = checked_sample(output_frame[ch], ch, i);
338 output_data[ch].push(sample);
339 }
340 });
341 }
342 Processing::Batch(batch_size) => {
343 assert!(
344 batch_size <= MAX_BUFFER_SIZE as u8,
345 "Batch size must be less than or equal to [{MAX_BUFFER_SIZE}]"
346 );
347
348 let samples_index = (0..config.num_samples).collect::<Vec<_>>();
349 for chunk in samples_index.chunks(batch_size as usize) {
350 let mut input_buff = BufferVec::new(num_inputs);
351 for (frame_index, input_index) in chunk.iter().enumerate() {
352 for (ch, input) in input_data.iter().enumerate() {
353 let value: f32 = input[*input_index];
354 input_buff.set_f32(ch, frame_index, value);
355 }
356 }
357 let input_ref = input_buff.buffer_ref();
358 let mut output_buf = BufferVec::new(num_outputs);
359 let mut output_ref = output_buf.buffer_mut();
360
361 unit.process(chunk.len(), &input_ref, &mut output_ref);
362
363 for (ch, data) in output_data.iter_mut().enumerate() {
364 data.extend(
365 output_buf
366 .channel_f32(ch)
367 .iter()
368 .enumerate()
369 .map(|(i, &value)| checked_sample(value, ch, i + chunk[0])),
370 );
371 }
372 }
373 }
374 }
375
376 AudioUnitSnapshotData {
377 input_data,
378 output_data,
379 abnormalities,
380 sample_rate: config.sample_rate,
381 num_samples: config.num_samples,
382 start_sample: config.warm_up.num_samples(config.sample_rate),
383 }
384}
385
386fn render_snapshot_output(data: &AudioUnitSnapshotData, output_mode: &SnapshotOutputMode) -> Vec<u8> {
387 match output_mode {
388 SnapshotOutputMode::SvgChart(svg_chart_config) => {
389 let abnormalities = data
390 .abnormalities
391 .iter()
392 .map(|channel| {
393 channel
394 .iter()
395 .map(|(i, abnormality)| (*i, AbnormalSample::from(*abnormality)))
396 .collect::<Vec<_>>()
397 })
398 .collect::<Vec<_>>();
399
400 generate_svg(
401 &data.input_data,
402 &data.output_data,
403 &abnormalities,
404 svg_chart_config,
405 data.sample_rate,
406 data.num_samples,
407 data.start_sample,
408 )
409 .as_bytes()
410 .to_vec()
411 }
412 SnapshotOutputMode::Wav(wav_output) => {
413 generate_wav(&data.output_data, wav_output, data.sample_rate, data.num_samples)
414 }
415 }
416}