use core::array;
use heapless::index_map::FnvIndexMap;
use dasp::sample::{FromSample, Sample};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{core::Frequency, prelude::*};
const PI2: f32 = PI * 2.0;
pub fn sample_sine<S: Sample + FromSample<f32>>(
index: usize,
sample_rate: usize,
frequency: Frequency,
) -> S {
let time = index as f32 / sample_rate as f32;
((2.0 * PI * frequency.0 * time).sin()).to_sample()
}
pub fn sample_saw<S: Sample + FromSample<f32>>(
index: usize,
sample_rate: usize,
frequency: Frequency,
) -> S {
(1.0 - ((index as f32 / sample_rate as f32 * frequency.0) % 1.0) * 2.0).to_sample()
}
pub fn sample_triangle<S: Sample + FromSample<f32>>(
index: usize,
sample_rate: usize,
frequency: Frequency,
) -> S {
let slope = (index as f32 / sample_rate as f32 * frequency.0) % 1.0 * 2.0;
if slope < 1.0 {
(-1.0 + slope * 2.0).to_sample()
} else {
(3.0 - slope * 2.0).to_sample()
}
}
pub fn sample_square<S: Sample + FromSample<f32>>(
index: usize,
sample_rate: usize,
frequency: Frequency,
duty_cycle: DutyCycle,
) -> S {
if (index as f32 / sample_rate as f32 * frequency.0) % 1.0 < duty_cycle.to_fractional() {
(1.0).to_sample()
} else {
(-1.0).to_sample()
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum DutyCycle {
Eight,
Quarter,
Third,
Half,
}
impl DutyCycle {
pub fn to_fractional(self) -> f32 {
match self {
DutyCycle::Eight => 0.125,
DutyCycle::Quarter => 0.25,
DutyCycle::Third => 0.33,
DutyCycle::Half => 0.5,
}
}
}
impl Default for DutyCycle {
fn default() -> Self {
DutyCycle::Half
}
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)]
pub enum OscillatorType {
Sine,
Saw,
Triangle,
Square,
}
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug)]
pub enum TableError {
IncorrectSize { expected: usize, actual: usize },
TableFull,
}
impl OscillatorType {
pub fn sample<S: Sample + FromSample<f32>>(
&self,
index: usize,
sample_rate: usize,
frequency: Frequency,
duty_cycle: DutyCycle,
) -> S {
match self {
OscillatorType::Sine => sample_sine(index, sample_rate, frequency),
OscillatorType::Saw => sample_saw(index, sample_rate, frequency),
OscillatorType::Triangle => sample_triangle(index, sample_rate, frequency),
OscillatorType::Square => sample_square(index, sample_rate, frequency, duty_cycle),
}
}
pub fn build_table<S: Sample + FromSample<f32>>(
&self,
table: &'_ mut [S],
sample_rate: usize,
frequency: Frequency,
duty_cycle: DutyCycle,
) -> Result<(), TableError> {
if table.len() != sample_rate {
return Err(TableError::IncorrectSize {
expected: sample_rate,
actual: table.len(),
});
}
match self {
OscillatorType::Sine => {
let mult: f32 = frequency.0 * PI2 / sample_rate as f32;
for (index, row) in table.iter_mut().enumerate() {
*row = ((index as f32 * mult).sin()).to_sample()
}
}
_ => {
for (index, row) in table.iter_mut().enumerate() {
*row = self.sample(index, sample_rate, frequency, duty_cycle);
}
}
}
Ok(())
}
}
pub trait Oscillator<S: Sample + FromSample<f32>> {
fn sample(&self, index: usize) -> S;
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, PartialEq)]
pub struct RuntimeOscillator {
osc_type: OscillatorType,
sample_rate: usize,
frequency: Frequency,
duty_cycle: DutyCycle,
}
impl RuntimeOscillator {
pub fn new(osc_type: OscillatorType, sample_rate: usize, frequency: Frequency) -> Self {
Self {
osc_type,
sample_rate,
frequency,
duty_cycle: DutyCycle::Half,
}
}
#[inline]
pub const fn get_sample_rate(&self) -> usize {
self.sample_rate
}
pub fn sample_with_frequency<S: Sample + FromSample<f32>>(
&self,
phase: usize,
freq: Frequency,
) -> S {
self.osc_type
.sample(phase, self.sample_rate, freq, self.duty_cycle)
}
}
impl<S: Sample + FromSample<f32>> Oscillator<S> for RuntimeOscillator {
fn sample(&self, index: usize) -> S {
self.osc_type
.sample(index, self.sample_rate, self.frequency, self.duty_cycle)
}
}
pub struct LookupOscillator<'a, LookupSample: Sample + FromSample<f32>> {
table: &'a [LookupSample],
}
impl<'a, LookupSample: Sample + FromSample<f32>> LookupOscillator<'a, LookupSample> {
pub fn new_from_table(table: &'a [LookupSample]) -> Self {
Self { table }
}
}
impl<'a, LookupSample: Sample + FromSample<f32>> Oscillator<LookupSample>
for LookupOscillator<'a, LookupSample>
{
fn sample(&self, index: usize) -> LookupSample {
self.table[index % self.table.len()]
}
}
pub struct OscillatorAllocator<
LookupSample: Sample + FromSample<f32>,
const SAMPLE_RATE: usize,
const MAX_TABLES: usize,
> {
lookup: FnvIndexMap<
(OscillatorType, Frequency, DutyCycle),
RefCell<[LookupSample; SAMPLE_RATE]>,
MAX_TABLES,
>,
}
impl<LookupSample: Sample + FromSample<f32>, const SAMPLE_RATE: usize, const MAX_TABLES: usize>
OscillatorAllocator<LookupSample, SAMPLE_RATE, MAX_TABLES>
{
pub fn lookup_or_allocate(
&mut self,
osc: OscillatorType,
frequency: Frequency,
duty_cycle: DutyCycle,
) -> Result<RefCell<[LookupSample; SAMPLE_RATE]>, TableError> {
let table = match self
.lookup
.iter()
.find(|entry| entry.0.0 == osc && entry.0.1 == frequency && entry.0.2 == duty_cycle)
{
Some(table) => RefCell::clone(table.1),
None => {
let mut table: [LookupSample; SAMPLE_RATE] = array::from_fn(|_| 0.0.to_sample());
osc.build_table(&mut table, SAMPLE_RATE, frequency, duty_cycle)?;
let cell = RefCell::new(table);
let clone = RefCell::clone(&cell);
self.lookup
.insert((osc, frequency, duty_cycle), cell)
.map_err(|_| TableError::TableFull)?;
clone
}
};
Ok(table)
}
}