firewheel_core/dsp/pan_law.rs
1#[cfg(not(feature = "std"))]
2use num_traits::Float;
3
4use core::f32::consts::FRAC_PI_2;
5
6use crate::diff::{Diff, Patch};
7
8/// The algorithm to use to map a normalized panning value in the range `[-1.0, 1.0]`
9/// to the corresponding gain values for the left and right channels.
10#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Diff, Patch)]
11#[cfg_attr(feature = "bevy_reflect", derive(bevy_reflect::Reflect))]
12#[repr(u32)]
13pub enum PanLaw {
14 /// This pan law makes the signal appear to play at a constant volume across
15 /// the entire panning range.
16 ///
17 /// More specifically this a circular pan law with each channel at -3dB when
18 /// panned center.
19 #[default]
20 EqualPower3dB = 0,
21 /// Same as [`PanLaw::EqualPower3dB`], but each channel will be at -6dB when
22 /// panned center which may be better for some signals.
23 EqualPower6dB,
24 /// This is cheaper to compute than `EqualPower3dB`, but is less accurate in
25 /// its perception of constant volume.
26 SquareRoot,
27 /// The cheapest to compute, but is the least accurate in its perception of
28 /// constant volume.
29 Linear,
30}
31
32impl PanLaw {
33 /// Compute the raw gain values for the `(left, right)` channels.
34 ///
35 /// * `pan` - The pan amount, where `0.0` is center, `-1.0` is fully left, and
36 /// `1.0` is fully right.
37 pub fn compute_gains(&self, pan: f32) -> (f32, f32) {
38 if pan <= -1.0 {
39 (1.0, 0.0)
40 } else if pan >= 1.0 {
41 (0.0, 1.0)
42 } else {
43 let pan = (pan + 1.0) * 0.5;
44
45 match self {
46 Self::EqualPower3dB => {
47 let pan = FRAC_PI_2 * pan;
48 let pan_cos = pan.cos();
49 let pan_sin = pan.sin();
50
51 (pan_cos, pan_sin)
52 }
53 Self::EqualPower6dB => {
54 let pan = FRAC_PI_2 * pan;
55 let pan_cos = pan.cos();
56 let pan_sin = pan.sin();
57
58 (pan_cos * pan_cos, pan_sin * pan_sin)
59 }
60 Self::SquareRoot => ((1.0 - pan).sqrt(), pan.sqrt()),
61 Self::Linear => ((1.0 - pan), pan),
62 }
63 }
64 }
65
66 pub fn from_u32(val: u32) -> Self {
67 match val {
68 1 => Self::EqualPower6dB,
69 2 => Self::SquareRoot,
70 3 => Self::Linear,
71 _ => Self::EqualPower3dB,
72 }
73 }
74}