ruffbox_synth 0.13.0

A beatbox-oriented synthesizer library.
Documentation
use super::spherical_harmonics::*;
use crate::building_blocks::{
    Modulator, Panner, SampleBuffer, SynthParameterLabel, SynthParameterValue,
};

/**
 * a plain ambisonics encoder (no spread or distance)
 */
pub struct Encoder<const BUFSIZE: usize, const NCHAN: usize> {
    pub order: usize,
    azimuth: f32,
    elevation: f32,
    azimuth_mod: Option<Modulator<BUFSIZE>>,
    elevation_mod: Option<Modulator<BUFSIZE>>,
    coefs: [[f32; BUFSIZE]; NCHAN],
}

impl<const BUFSIZE: usize, const NCHAN: usize> Default for Encoder<BUFSIZE, NCHAN> {
    fn default() -> Self {
        Self::new()
    }
}

impl<const BUFSIZE: usize, const NCHAN: usize> Encoder<BUFSIZE, NCHAN> {
    pub fn new() -> Self {
        // to be extended
        Encoder {
            order: NCHAN.isqrt() - 1,
            azimuth: 0.0,
            elevation: 0.0,
            azimuth_mod: None,
            elevation_mod: None,
            coefs: [[0.0; BUFSIZE]; NCHAN],
        }
    }
}

impl<const BUFSIZE: usize, const NCHAN: usize> Panner<BUFSIZE, NCHAN> for Encoder<BUFSIZE, NCHAN> {
    fn set_modulator(
        &mut self,
        par: SynthParameterLabel,
        init: f32,
        modulator: Modulator<BUFSIZE>,
    ) {
        match par {
            SynthParameterLabel::AmbisonicAzimuth => {
                self.azimuth = init; // keep for later
                self.azimuth_mod = Some(modulator);
            }
            SynthParameterLabel::AmbisonicElevation => {
                self.elevation = init; // keep for later
                self.elevation_mod = Some(modulator);
            }
            _ => {}
        }
    }

    // some parameter limits might be nice ...
    fn set_parameter(&mut self, par: SynthParameterLabel, value: &SynthParameterValue) {
        if let SynthParameterValue::ScalarF32(val) = value {
            match par {
                SynthParameterLabel::AmbisonicAzimuth => self.azimuth = *val,
                SynthParameterLabel::AmbisonicElevation => {
                    self.elevation = *val;
                }
                _ => {}
            };

            // always calc first order
            let sin_a = self.azimuth.sin();
            let cos_a = self.azimuth.cos();
            let sin_e = self.elevation.sin();
            let cos_e = self.elevation.cos();

            // order 0
            self.coefs[0] = [1.0; BUFSIZE];

            // order 1
            self.coefs[1] = [SH_ACN_1 * sin_a * cos_e; BUFSIZE];
            self.coefs[2] = [SH_ACN_2 * sin_e; BUFSIZE];
            self.coefs[3] = [SH_ACN_3 * cos_a * cos_e; BUFSIZE];

            // order 2
            if self.order >= 2 {
                let sin_a2 = (self.azimuth * 2.0).sin();
                let cos_a2 = (self.azimuth * 2.0).cos();
                let sin_e2 = (self.elevation * 2.0).sin();

                let sin2_e = sin_e * sin_e;
                let cos2_e = cos_e * cos_e;

                self.coefs[4] = [SH_ACN_4 * sin_a2 * cos2_e; BUFSIZE];
                self.coefs[5] = [SH_ACN_5 * sin_a * sin_e2; BUFSIZE];
                self.coefs[6] = [SH_ACN_6 * (3.0 * sin2_e - 1.0); BUFSIZE];
                self.coefs[7] = [SH_ACN_7 * cos_a * sin_e2; BUFSIZE];
                self.coefs[8] = [SH_ACN_8 * cos_a2 * cos2_e; BUFSIZE];

                if self.order >= 3 {
                    let sin_a3 = (self.azimuth * 3.0).sin();
                    let cos3_e = cos2_e * cos_e;
                    let cos_a3 = (self.azimuth * 3.0).cos();

                    self.coefs[9] = [SH_ACN_9 * sin_a3 * cos3_e; BUFSIZE];
                    self.coefs[10] = [SH_ACN_10 * sin_a2 * sin_e * cos2_e; BUFSIZE];
                    self.coefs[11] = [SH_ACN_11 * sin_a * cos_e * (5.0 * sin2_e - 1.0); BUFSIZE];
                    self.coefs[12] = [SH_ACN_12 * sin_e * (5.0 * sin2_e - 3.0); BUFSIZE];
                    self.coefs[13] = [SH_ACN_13 * cos_a * cos_e * (5.0 * sin2_e - 1.0); BUFSIZE];
                    self.coefs[14] = [SH_ACN_14 * cos_a2 * sin_e * cos2_e; BUFSIZE];
                    self.coefs[15] = [SH_ACN_15 * cos_a3 * cos3_e; BUFSIZE];

                    if self.order >= 4 {
                        let sin_a4 = (self.azimuth * 4.0).sin();
                        let cos_a4 = (self.azimuth * 4.0).cos();
                        let cos4_e = cos3_e * cos_e;
                        let sin4_e = sin2_e * sin2_e;

                        self.coefs[16] = [SH_ACN_16 * sin_a4 * cos4_e; BUFSIZE];
                        self.coefs[17] = [SH_ACN_17 * sin_a3 * sin_e * cos3_e; BUFSIZE];
                        self.coefs[18] =
                            [SH_ACN_18 * sin_a2 * cos2_e * (7.0 * sin2_e - 1.0); BUFSIZE];
                        self.coefs[19] =
                            [SH_ACN_19 * sin_a * sin_e2 * (7.0 * sin2_e - 3.0); BUFSIZE];
                        self.coefs[20] =
                            [SH_ACN_20 * (35.0 * sin4_e - 30.0 * sin2_e + 3.0); BUFSIZE];
                        self.coefs[21] =
                            [SH_ACN_21 * cos_a * sin_e2 * (7.0 * sin2_e - 3.0); BUFSIZE];
                        self.coefs[22] =
                            [SH_ACN_22 * cos_a2 * cos2_e * (7.0 * sin2_e - 1.0); BUFSIZE];
                        self.coefs[23] = [SH_ACN_23 * cos_a3 * sin_e * cos3_e; BUFSIZE];
                        self.coefs[24] = [SH_ACN_24 * cos_a4 * cos4_e; BUFSIZE];
                    }
                }
            }
        }
    }

    fn process_block(
        &mut self,
        input: [f32; BUFSIZE],
        start_sample: usize,
        in_buffers: &[SampleBuffer],
    ) -> [[f32; BUFSIZE]; NCHAN] {
        let mut enc_block = [[0.0; BUFSIZE]; NCHAN];

        if self.azimuth_mod.is_some() || self.elevation_mod.is_some() {
            let azi_buf = if let Some(azi_mod) = self.azimuth_mod.as_mut() {
                azi_mod.process(self.azimuth, start_sample, in_buffers)
            } else {
                [self.azimuth; BUFSIZE]
            };
            let ele_buf = if let Some(ele_mod) = self.elevation_mod.as_mut() {
                ele_mod.process(self.elevation, start_sample, in_buffers)
            } else {
                [self.elevation; BUFSIZE]
            };

            // order 0
            self.coefs[0] = [1.0; BUFSIZE];

            if self.order == 1 {
                for s in 0..BUFSIZE {
                    // always calc first order
                    let sin_a = azi_buf[s].sin();
                    let cos_a = azi_buf[s].cos();
                    let sin_e = ele_buf[s].sin();
                    let cos_e = ele_buf[s].cos();

                    self.coefs[1][s] = SH_ACN_1 * sin_a * cos_e;
                    self.coefs[2][s] = SH_ACN_2 * sin_e;
                    self.coefs[3][s] = SH_ACN_3 * cos_a * cos_e;
                }
            }

            if self.order == 2 {
                for s in 0..BUFSIZE {
                    // always calc first order
                    let sin_a = azi_buf[s].sin();
                    let cos_a = azi_buf[s].cos();
                    let sin_e = ele_buf[s].sin();
                    let cos_e = ele_buf[s].cos();
                    let sin_a2 = (azi_buf[s] * 2.0).sin();
                    let cos_a2 = (azi_buf[s] * 2.0).cos();
                    let sin2_e = sin_e * sin_e;
                    let sin_e2 = (ele_buf[s] * 2.0).sin();
                    let cos2_e = cos_e * cos_e;

                    self.coefs[1][s] = SH_ACN_1 * sin_a * cos_e;
                    self.coefs[2][s] = SH_ACN_2 * sin_e;
                    self.coefs[3][s] = SH_ACN_3 * cos_a * cos_e;

                    self.coefs[4][s] = SH_ACN_4 * sin_a2 * cos2_e;
                    self.coefs[5][s] = SH_ACN_5 * sin_a * sin_e2;
                    self.coefs[6][s] = SH_ACN_6 * (3.0 * sin2_e - 1.0);
                    self.coefs[7][s] = SH_ACN_7 * cos_a * sin_e2;
                    self.coefs[8][s] = SH_ACN_8 * cos_a2 * cos2_e;
                }
            }

            if self.order == 3 {
                for s in 0..BUFSIZE {
                    // always calc first order
                    let sin_a = azi_buf[s].sin();
                    let cos_a = azi_buf[s].cos();
                    let sin_e = ele_buf[s].sin();
                    let cos_e = ele_buf[s].cos();
                    let sin_a2 = (azi_buf[s] * 2.0).sin();
                    let cos_a2 = (azi_buf[s] * 2.0).cos();
                    let sin2_e = sin_e * sin_e;

                    let cos2_e = cos_e * cos_e;
                    let sin_a3 = (azi_buf[s] * 3.0).sin();
                    let cos3_e = cos2_e * cos_e;
                    let cos_a3 = (azi_buf[s] * 3.0).cos();
                    let sin_e2 = (ele_buf[s] * 2.0).sin();

                    self.coefs[1][s] = SH_ACN_1 * sin_a * cos_e;
                    self.coefs[2][s] = SH_ACN_2 * sin_e;
                    self.coefs[3][s] = SH_ACN_3 * cos_a * cos_e;

                    self.coefs[4][s] = SH_ACN_4 * sin_a2 * cos2_e;
                    self.coefs[5][s] = SH_ACN_5 * sin_a * sin_e2;
                    self.coefs[6][s] = SH_ACN_6 * (3.0 * sin2_e - 1.0);
                    self.coefs[7][s] = SH_ACN_7 * cos_a * sin_e2;
                    self.coefs[8][s] = SH_ACN_8 * cos_a2 * cos2_e;

                    self.coefs[9][s] = SH_ACN_9 * sin_a3 * cos3_e;
                    self.coefs[10][s] = SH_ACN_10 * sin_a2 * sin_e * cos2_e;
                    self.coefs[11][s] = SH_ACN_11 * sin_a * cos_e * (5.0 * sin2_e - 1.0);
                    self.coefs[12][s] = SH_ACN_12 * sin_e * (5.0 * sin2_e - 3.0);
                    self.coefs[13][s] = SH_ACN_13 * cos_a * cos_e * (5.0 * sin2_e - 1.0);
                    self.coefs[14][s] = SH_ACN_14 * cos_a2 * sin_e * cos2_e;
                    self.coefs[15][s] = SH_ACN_15 * cos_a3 * cos3_e;
                }
            }

            if self.order == 4 {
                for s in 0..BUFSIZE {
                    // always calc first order
                    let sin_a = azi_buf[s].sin();
                    let cos_a = azi_buf[s].cos();
                    let sin_e = ele_buf[s].sin();
                    let cos_e = ele_buf[s].cos();

                    let sin_a2 = (azi_buf[s] * 2.0).sin();
                    let cos_a2 = (azi_buf[s] * 2.0).cos();
                    let sin2_e = sin_e * sin_e;

                    let cos2_e = cos_e * cos_e;
                    let sin_a3 = (azi_buf[s] * 3.0).sin();
                    let cos3_e = cos2_e * cos_e;
                    let cos_a3 = (azi_buf[s] * 3.0).cos();
                    let sin_e2 = (ele_buf[s] * 2.0).sin();

                    let sin_a4 = (azi_buf[s] * 4.0).sin();
                    let cos_a4 = (azi_buf[s] * 4.0).cos();
                    let cos4_e = cos3_e * cos_e;
                    let sin4_e = sin2_e * sin2_e;

                    self.coefs[1][s] = SH_ACN_1 * sin_a * cos_e;
                    self.coefs[2][s] = SH_ACN_2 * sin_e;
                    self.coefs[3][s] = SH_ACN_3 * cos_a * cos_e;

                    self.coefs[4][s] = SH_ACN_4 * sin_a2 * cos2_e;
                    self.coefs[5][s] = SH_ACN_5 * sin_a * sin_e2;
                    self.coefs[6][s] = SH_ACN_6 * (3.0 * sin2_e - 1.0);
                    self.coefs[7][s] = SH_ACN_7 * cos_a * sin_e2;
                    self.coefs[8][s] = SH_ACN_8 * cos_a2 * cos2_e;

                    self.coefs[9][s] = SH_ACN_9 * sin_a3 * cos3_e;
                    self.coefs[10][s] = SH_ACN_10 * sin_a2 * sin_e * cos2_e;
                    self.coefs[11][s] = SH_ACN_11 * sin_a * cos_e * (5.0 * sin2_e - 1.0);
                    self.coefs[12][s] = SH_ACN_12 * sin_e * (5.0 * sin2_e - 3.0);
                    self.coefs[13][s] = SH_ACN_13 * cos_a * cos_e * (5.0 * sin2_e - 1.0);
                    self.coefs[14][s] = SH_ACN_14 * cos_a2 * sin_e * cos2_e;
                    self.coefs[15][s] = SH_ACN_15 * cos_a3 * cos3_e;

                    self.coefs[16][s] = SH_ACN_16 * sin_a4 * cos4_e;
                    self.coefs[17][s] = SH_ACN_17 * sin_a3 * sin_e * cos3_e;
                    self.coefs[18][s] = SH_ACN_18 * sin_a2 * cos2_e * (7.0 * sin2_e - 1.0);
                    self.coefs[19][s] = SH_ACN_19 * sin_a * sin_e2 * (7.0 * sin2_e - 3.0);
                    self.coefs[20][s] = SH_ACN_20 * (35.0 * sin4_e - 30.0 * sin2_e + 3.0);
                    self.coefs[21][s] = SH_ACN_21 * cos_a * sin_e2 * (7.0 * sin2_e - 3.0);
                    self.coefs[22][s] = SH_ACN_22 * cos_a2 * cos2_e * (7.0 * sin2_e - 1.0);
                    self.coefs[23][s] = SH_ACN_23 * cos_a3 * sin_e * cos3_e;
                    self.coefs[24][s] = SH_ACN_24 * cos_a4 * cos4_e;
                }
            }
        }

        if self.order == 1 {
            // some naive unrolling by hand, again, as above, avoid branching
            for (i, input_sample) in input.iter().enumerate().take(BUFSIZE) {
                enc_block[0][i] = input_sample * self.coefs[0][i];
                enc_block[1][i] = input_sample * self.coefs[1][i];
                enc_block[2][i] = input_sample * self.coefs[2][i];
                enc_block[3][i] = input_sample * self.coefs[3][i];
            }
        }

        if self.order == 2 {
            // some naive unrolling by hand, again, as above, avoid branching
            for (i, input_sample) in input.iter().enumerate().take(BUFSIZE) {
                enc_block[0][i] = input_sample * self.coefs[0][i];
                enc_block[1][i] = input_sample * self.coefs[1][i];
                enc_block[2][i] = input_sample * self.coefs[2][i];
                enc_block[3][i] = input_sample * self.coefs[3][i];
                enc_block[4][i] = input_sample * self.coefs[4][i];
                enc_block[5][i] = input_sample * self.coefs[5][i];
                enc_block[6][i] = input_sample * self.coefs[6][i];
                enc_block[7][i] = input_sample * self.coefs[7][i];
                enc_block[8][i] = input_sample * self.coefs[8][i];
            }
        }

        if self.order == 3 {
            // some naive unrolling by hand, again, as above, avoid branching
            for (i, input_sample) in input.iter().enumerate().take(BUFSIZE) {
                enc_block[0][i] = input_sample * self.coefs[0][i];
                enc_block[1][i] = input_sample * self.coefs[1][i];
                enc_block[2][i] = input_sample * self.coefs[2][i];
                enc_block[3][i] = input_sample * self.coefs[3][i];
                enc_block[4][i] = input_sample * self.coefs[4][i];
                enc_block[5][i] = input_sample * self.coefs[5][i];
                enc_block[6][i] = input_sample * self.coefs[6][i];
                enc_block[7][i] = input_sample * self.coefs[7][i];
                enc_block[8][i] = input_sample * self.coefs[8][i];
                enc_block[9][i] = input_sample * self.coefs[9][i];
                enc_block[10][i] = input_sample * self.coefs[10][i];
                enc_block[11][i] = input_sample * self.coefs[11][i];
                enc_block[12][i] = input_sample * self.coefs[12][i];
                enc_block[13][i] = input_sample * self.coefs[13][i];
                enc_block[14][i] = input_sample * self.coefs[14][i];
                enc_block[15][i] = input_sample * self.coefs[15][i];
            }
        }

        if self.order == 4 {
            // some naive unrolling by hand, again, as above, avoid branching
            for (i, input_sample) in input.iter().enumerate().take(BUFSIZE) {
                enc_block[0][i] = input_sample * self.coefs[0][i];
                enc_block[1][i] = input_sample * self.coefs[1][i];
                enc_block[2][i] = input_sample * self.coefs[2][i];
                enc_block[3][i] = input_sample * self.coefs[3][i];
                enc_block[4][i] = input_sample * self.coefs[4][i];
                enc_block[5][i] = input_sample * self.coefs[5][i];
                enc_block[6][i] = input_sample * self.coefs[6][i];
                enc_block[7][i] = input_sample * self.coefs[7][i];
                enc_block[8][i] = input_sample * self.coefs[8][i];
                enc_block[9][i] = input_sample * self.coefs[9][i];
                enc_block[10][i] = input_sample * self.coefs[10][i];
                enc_block[11][i] = input_sample * self.coefs[11][i];
                enc_block[12][i] = input_sample * self.coefs[12][i];
                enc_block[13][i] = input_sample * self.coefs[13][i];
                enc_block[14][i] = input_sample * self.coefs[14][i];
                enc_block[15][i] = input_sample * self.coefs[15][i];
                enc_block[16][i] = input_sample * self.coefs[16][i];
                enc_block[17][i] = input_sample * self.coefs[17][i];
                enc_block[18][i] = input_sample * self.coefs[18][i];
                enc_block[19][i] = input_sample * self.coefs[19][i];
                enc_block[20][i] = input_sample * self.coefs[20][i];
                enc_block[21][i] = input_sample * self.coefs[21][i];
                enc_block[22][i] = input_sample * self.coefs[22][i];
                enc_block[23][i] = input_sample * self.coefs[23][i];
                enc_block[24][i] = input_sample * self.coefs[24][i];
            }
        }

        enc_block
    }
}