use std::{num::NonZero, ops::ControlFlow};
use crate::{
project::note_event::Note,
sample::{ProcessingFrame, ProcessingFunction, Sample, SampleMetaData},
};
use super::Frame;
#[repr(u8)]
pub enum Interpolation {
Nearest = 0,
Linear = 1,
}
impl From<u8> for Interpolation {
fn from(value: u8) -> Self {
Self::from_u8(value)
}
}
impl Interpolation {
pub const fn pad_needed(&self) -> usize {
match self {
Interpolation::Nearest => 1,
Interpolation::Linear => 1,
}
}
pub const fn from_u8(value: u8) -> Self {
match value {
0 => Self::Nearest,
1 => Self::Linear,
_ => panic!(),
}
}
}
#[derive(Debug)]
pub struct SamplePlayer {
sample: Sample,
meta: SampleMetaData,
note: Note,
position: (usize, f32),
out_rate: NonZero<u32>,
step_size: f32,
}
impl SamplePlayer {
pub fn new(sample: Sample, meta: SampleMetaData, out_rate: NonZero<u32>, note: Note) -> Self {
let step_size = Self::compute_step_size(meta.sample_rate, out_rate, meta.base_note, note);
Self {
sample,
meta,
position: (Sample::PAD_SIZE_EACH, 0.),
out_rate,
step_size,
note,
}
}
pub fn check_position(&self) -> ControlFlow<()> {
if self.position.0 > self.sample.len_with_pad() - Sample::PAD_SIZE_EACH {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
#[inline]
fn compute_step_size(
in_rate: NonZero<u32>,
out_rate: NonZero<u32>,
sample_base_note: Note,
playing_note: Note,
) -> f32 {
(f32::from(i16::from(playing_note.get()) - i16::from(sample_base_note.get())) / 12.).exp2()
* (out_rate.get() as f32 / in_rate.get() as f32)
}
fn set_step_size(&mut self) {
self.step_size = Self::compute_step_size(
self.meta.sample_rate,
self.out_rate,
self.meta.base_note,
self.note,
);
}
pub fn set_out_samplerate(&mut self, samplerate: NonZero<u32>) {
self.out_rate = samplerate;
self.set_step_size();
}
fn step(&mut self) {
self.position.1 += self.step_size;
let floor = self.position.1.trunc();
self.position.1 -= floor;
self.position.0 += floor as usize;
}
pub fn iter<const INTERPOLATION: u8>(&mut self) -> SampleIter<'_, INTERPOLATION> {
SampleIter { inner: self }
}
pub fn next<const INTERPOLATION: u8>(&mut self) -> Option<Frame> {
let interpolation = const { Interpolation::from_u8(INTERPOLATION) };
if self.check_position().is_break() {
return None;
}
let out = match interpolation {
Interpolation::Nearest => self.compute_nearest(),
Interpolation::Linear => self.compute_linear(),
};
self.step();
Some(out)
}
fn compute_linear(&mut self) -> Frame {
struct Linear(f32);
impl<S: ProcessingFrame> ProcessingFunction<2, S> for Linear {
fn process(self, data: &[S; 2]) -> S {
let diff = data[1] - data[0];
(diff * self.0) + data[0]
}
}
self.sample
.compute(self.position.0, Linear(self.position.1))
}
fn compute_nearest(&mut self) -> Frame {
let load_idx = if self.position.1 < 0.5 {
self.position.0
} else {
self.position.0 + 1
};
self.sample.index(load_idx)
}
}
pub struct SampleIter<'player, const INTERPOLATION: u8> {
inner: &'player mut SamplePlayer,
}
impl<const INTERPOLATION: u8> Iterator for SampleIter<'_, INTERPOLATION> {
type Item = Frame;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next::<INTERPOLATION>()
}
}