alpha2_audio/
crossfade.rs1use core::f32::consts::FRAC_PI_2;
2
3use crate::{ProcessCtx, Processor, StateBlob, StateError};
4
5#[derive(Copy, Clone, Debug, PartialEq)]
7pub struct EqualPowerCrossfade {
8 progress: f32,
9}
10
11impl EqualPowerCrossfade {
12 #[must_use]
14 pub fn new(progress: f32) -> Self {
15 Self {
16 progress: if progress.is_finite() {
17 progress.clamp(0.0, 1.0)
18 } else {
19 0.0
20 },
21 }
22 }
23
24 pub fn set_progress(&mut self, progress: f32) {
26 *self = Self::new(progress);
27 }
28}
29
30impl Processor for EqualPowerCrossfade {
31 fn inputs_per_output(&self) -> usize {
33 2
34 }
35
36 fn prepare(&mut self, _sample_rate: u32, _max_block: usize) {}
37
38 fn process(&mut self, _ctx: &ProcessCtx, inputs: &[&[f32]], outputs: &mut [&mut [f32]]) {
39 let channels = outputs.len();
40 if inputs.len() < channels.saturating_mul(2) {
41 return;
42 }
43 let (from_gain, to_gain) = match self.progress {
44 0.0 => (1.0, 0.0),
45 1.0 => (0.0, 1.0),
46 progress => (
47 libm::cosf(progress * FRAC_PI_2),
48 libm::sinf(progress * FRAC_PI_2),
49 ),
50 };
51 for channel in 0..channels {
52 let from = inputs[channel];
53 let to = inputs[channel + channels];
54 for ((from, to), output) in from.iter().zip(to).zip(outputs[channel].iter_mut()) {
55 *output = from * from_gain + *to * to_gain;
56 }
57 }
58 }
59
60 fn reset(&mut self) {}
61
62 fn save_state(&self) -> StateBlob {
63 StateBlob::new(Vec::new())
64 }
65
66 fn load_state(&mut self, state: &StateBlob) -> Result<(), StateError> {
67 if state.as_bytes().is_empty() {
68 Ok(())
69 } else {
70 Err(StateError::Mismatch)
71 }
72 }
73}