use super::osc::PhaseFxP;
use super::*;
use crate::fixedmath::apply_scalar_i;
use core::mem::transmute;
use core::option::Option;
use rand::{rngs::SmallRng, RngCore, SeedableRng};
const RANDOM_SEED: u64 = 0xce607a9d25ec3d88u64;
#[repr(transparent)]
#[derive(Clone, Copy)]
pub struct LfoOptions {
bits: u16,
}
impl LfoOptions {
const BIPOLAR: u16 = 1 << 8;
const RETRIGGER: u16 = 1 << 9;
pub fn wave(&self) -> Option<LfoWave> {
let value = (self.bits & 0xFF) as u8;
LfoWave::try_from(value).ok()
}
pub fn bipolar(&self) -> bool {
self.bits & Self::BIPOLAR != 0
}
pub fn retrigger(&self) -> bool {
self.bits & Self::RETRIGGER != 0
}
pub fn new(wave: LfoWave, bipolar: bool, retrigger: bool) -> Self {
LfoOptions {
bits: (wave as u16)
| if bipolar { Self::BIPOLAR } else { 0 }
| if retrigger { Self::RETRIGGER } else { 0 },
}
}
}
impl Default for LfoOptions {
fn default() -> Self {
Self::new(LfoWave::Sine, true, true)
}
}
#[derive(Default, Clone, Copy)]
#[repr(u8)]
pub enum LfoWave {
#[default]
Sine,
Square,
Triangle,
Saw,
SampleHold,
SampleGlide,
}
impl LfoWave {
const ELEM: [LfoWave; 6] = [
Self::Sine,
Self::Square,
Self::Triangle,
Self::Saw,
Self::SampleHold,
Self::SampleGlide,
];
pub const fn waves() -> &'static [LfoWave] {
&Self::ELEM
}
pub const fn to_str(&self) -> &'static str {
[
"Sine",
"Square",
"Triangle",
"Saw",
"Sample & Hold",
"Sample & Glide",
][*self as usize]
}
pub const fn to_str_short(&self) -> &'static str {
[
crate::util::SIN_CHARSTR,
crate::util::SQ_CHARSTR,
crate::util::TRI_CHARSTR,
crate::util::SAW_CHARSTR,
"S+H",
"S+G",
][*self as usize]
}
}
impl From<LfoWave> for &'static str {
fn from(value: LfoWave) -> Self {
value.to_str()
}
}
impl TryFrom<u8> for LfoWave {
type Error = &'static str;
fn try_from(value: u8) -> Result<Self, &'static str> {
if value >= LfoWave::Sine as u8 && value <= LfoWave::SampleGlide as u8 {
unsafe { Ok(transmute::<u8, LfoWave>(value)) }
} else {
Err("Conversion of u8 to LfoWave Overflowed")
}
}
}
pub struct LfoParams<'a, Smp: Float> {
pub freq: &'a [Smp],
pub depth: &'a [Smp],
pub opts: &'a [LfoOptions],
}
impl<'a, Smp: Float> LfoParams<'a, Smp> {
pub fn len(&self) -> usize {
min_size(&[self.freq.len(), self.depth.len(), self.opts.len()])
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub struct MutLfoParams<'a, Smp: Float> {
pub freq: &'a mut [Smp],
pub depth: &'a mut [Smp],
pub opts: &'a mut [LfoOptions],
}
impl<'a, Smp: Float> MutLfoParams<'a, Smp> {
pub fn len(&self) -> usize {
min_size(&[self.freq.len(), self.depth.len(), self.opts.len()])
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<'a, Smp: Float> From<MutLfoParams<'a, Smp>> for LfoParams<'a, Smp> {
fn from(value: MutLfoParams<'a, Smp>) -> Self {
Self {
freq: value.freq,
depth: value.depth,
opts: value.opts,
}
}
}
#[derive(Clone)]
pub struct Lfo<Smp> {
outbuf: BufferT<Smp>,
rng: SmallRng,
phase: Smp,
rand_smps: [Smp; 2],
last_gate: bool,
}
impl<Smp: Float> Lfo<Smp> {
pub fn new(seed: u64) -> Self {
let mut retval = Self {
outbuf: [Smp::ZERO; STATIC_BUFFER_SIZE],
rng: SmallRng::seed_from_u64(seed),
phase: Smp::ZERO,
rand_smps: [Smp::ZERO; 2],
last_gate: false,
};
retval.update_rands();
retval.update_rands();
retval
}
fn update_rands(&mut self) {
self.rand_smps[1] = self.rand_smps[0];
let rand_num = self.rng.next_u32() & (u16::MAX as u32);
self.rand_smps[0] = Smp::from_u16(rand_num as u16) / Smp::from_u16(u16::MAX);
}
pub fn process(&mut self, ctx: &Context<Smp>, gate: &[Smp], params: LfoParams<Smp>) -> &[Smp] {
let freq = params.freq;
let depth = params.depth;
let opts = params.opts;
let numsamples = min_size(&[
freq.len(),
gate.len(),
opts.len(),
depth.len(),
STATIC_BUFFER_SIZE,
]);
for i in 0..numsamples {
let this_gate = gate[i] > Smp::ONE_HALF;
if opts[i].retrigger() && this_gate && !self.last_gate {
self.phase = Smp::ZERO;
}
self.last_gate = this_gate;
let frac_2phase_pi = (self.phase + self.phase) / Smp::PI();
let mut value = match opts[i].wave().unwrap_or_default() {
LfoWave::Saw => frac_2phase_pi / Smp::TWO,
LfoWave::Square => {
if self.phase < Smp::ZERO {
Smp::ONE.neg()
} else {
Smp::ONE
}
}
LfoWave::Triangle => {
if self.phase < Smp::FRAC_PI_2().neg() {
frac_2phase_pi.neg() - Smp::TWO
} else if self.phase > Smp::FRAC_PI_2() {
Smp::TWO - frac_2phase_pi
} else {
frac_2phase_pi
}
}
LfoWave::Sine => {
if self.phase < Smp::FRAC_PI_2().neg() {
Smp::fcos(self.phase + Smp::FRAC_PI_2()).neg()
} else if self.phase < Smp::FRAC_PI_2() {
Smp::fcos(self.phase - Smp::FRAC_PI_2())
} else {
Smp::fsin(self.phase)
}
}
LfoWave::SampleHold => self.rand_smps[0],
LfoWave::SampleGlide => {
self.rand_smps[0] + (frac_2phase_pi * (self.rand_smps[1] - self.rand_smps[0]))
}
};
if !opts[i].bipolar() {
value = (value + Smp::ONE) / Smp::TWO;
}
self.outbuf[i] = value * depth[i];
let phase_per_sample = (freq[i] * Smp::TAU()) / ctx.sample_rate;
self.phase = self.phase + phase_per_sample;
if self.phase >= Smp::PI() {
self.phase = self.phase - Smp::TAU();
self.update_rands();
}
}
&self.outbuf[0..numsamples]
}
}
impl<Smp: Float> Default for Lfo<Smp> {
fn default() -> Self {
Self::new(RANDOM_SEED)
}
}
pub struct LfoParamsFxP<'a> {
pub freq: &'a [LfoFreqFxP],
pub depth: &'a [ScalarFxP],
pub opts: &'a [LfoOptions],
}
impl<'a> LfoParamsFxP<'a> {
pub fn len(&self) -> usize {
min_size(&[self.freq.len(), self.depth.len(), self.opts.len()])
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub struct MutLfoParamsFxP<'a> {
pub freq: &'a mut [LfoFreqFxP],
pub depth: &'a mut [ScalarFxP],
pub opts: &'a mut [LfoOptions],
}
impl<'a> MutLfoParamsFxP<'a> {
pub fn len(&self) -> usize {
min_size(&[self.freq.len(), self.depth.len(), self.opts.len()])
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
}
impl<'a> From<MutLfoParamsFxP<'a>> for LfoParamsFxP<'a> {
fn from(value: MutLfoParamsFxP<'a>) -> Self {
Self {
freq: value.freq,
depth: value.depth,
opts: value.opts,
}
}
}
#[derive(Clone)]
pub struct LfoFxP {
outbuf: BufferT<SampleFxP>,
rng: SmallRng,
rand_smps: [SampleFxP; 2],
phase: PhaseFxP,
last_gate: bool,
}
impl LfoFxP {
pub fn new(seed: u64) -> LfoFxP {
let mut retval = LfoFxP {
outbuf: [SampleFxP::ZERO; STATIC_BUFFER_SIZE],
rng: SmallRng::seed_from_u64(seed),
rand_smps: [SampleFxP::ZERO; 2],
phase: PhaseFxP::ZERO,
last_gate: false,
};
retval.update_rands();
retval.update_rands();
retval
}
fn update_rands(&mut self) {
self.rand_smps[1] = self.rand_smps[0];
let rand_num = self.rng.next_u32() & (u16::MAX as u32);
let rand_scalar = ScalarFxP::from_bits(rand_num as u16);
self.rand_smps[0] = SampleFxP::from_num(rand_scalar);
}
pub fn process(
&mut self,
ctx: &ContextFxP,
gate: &[SampleFxP],
params: LfoParamsFxP,
) -> &[SampleFxP] {
let freq = params.freq;
let depth = params.depth;
let opts = params.opts;
let numsamples = min_size(&[
freq.len(),
gate.len(),
opts.len(),
depth.len(),
STATIC_BUFFER_SIZE,
]);
const FRAC_2_PI: ScalarFxP = ScalarFxP::lit("0x0.a2fa");
const TWO: SampleFxP = SampleFxP::lit("2");
const ONE_HALF: SampleFxP = SampleFxP::lit("0.5");
for i in 0..numsamples {
let this_gate = gate[i] > ONE_HALF;
if opts[i].retrigger() && this_gate && !self.last_gate {
self.phase = PhaseFxP::ZERO;
}
self.last_gate = this_gate;
let frac_2phase_pi = apply_scalar_i(SampleFxP::from_num(self.phase), FRAC_2_PI);
let mut value = match opts[i].wave().unwrap_or_default() {
LfoWave::Saw => frac_2phase_pi.unwrapped_shr(1),
LfoWave::Square => {
if self.phase < 0 {
SampleFxP::NEG_ONE
} else {
SampleFxP::ONE
}
}
LfoWave::Triangle => {
if self.phase < PhaseFxP::FRAC_PI_2.unwrapped_neg() {
frac_2phase_pi.unwrapped_neg() - TWO
} else if self.phase > PhaseFxP::FRAC_PI_2 {
TWO - frac_2phase_pi
} else {
frac_2phase_pi
}
}
LfoWave::Sine => {
if self.phase < PhaseFxP::FRAC_PI_2.unwrapped_neg() {
fixedmath::cos_fixed(SampleFxP::from_num(self.phase + PhaseFxP::FRAC_PI_2))
.unwrapped_neg()
} else if self.phase < PhaseFxP::FRAC_PI_2 {
fixedmath::cos_fixed(SampleFxP::from_num(self.phase - PhaseFxP::FRAC_PI_2))
} else {
fixedmath::sin_fixed(SampleFxP::from_num(self.phase))
}
}
LfoWave::SampleHold => self.rand_smps[0],
LfoWave::SampleGlide => {
self.rand_smps[0]
+ SampleFxP::from_num(
SampleFxP::from_num(frac_2phase_pi)
.wide_mul(self.rand_smps[1] - self.rand_smps[0]),
)
}
};
if !opts[i].bipolar() {
value = (value + SampleFxP::ONE).unwrapped_shr(1);
}
self.outbuf[i] = SampleFxP::from_num(value.wide_mul_unsigned(depth[i]));
let phase_per_sample = PhaseFxP::from_num(
freq[i]
.wide_mul(ctx.sample_rate.frac_2pi4096_sr())
.unwrapped_shr(12),
);
self.phase += phase_per_sample;
if self.phase >= PhaseFxP::PI {
self.phase -= PhaseFxP::TAU;
self.update_rands();
}
}
&self.outbuf[0..numsamples]
}
}
impl Default for LfoFxP {
fn default() -> Self {
Self::new(RANDOM_SEED)
}
}