use std::{ fmt::Debug, iter::repeat_n, num::NonZero, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}, }; use triomphe::ThinArc; use crate::{audio_processing::Frame, project::note_event::Note}; // implemented for f32 and Frame pub(crate) trait ProcessingFrame: Add + AddAssign + Sub + SubAssign + Mul + MulAssign + Div + DivAssign + Copy { fn mul_add(self, mul: f32, add: Self) -> Self; } impl ProcessingFrame for f32 { fn mul_add(self, mul: f32, add: Self) -> Self { f32::mul_add(self, mul, add) } } pub(crate) trait ProcessingFunction { fn process(position: f32, data: &[Fr; N]) -> Fr; } #[derive(Clone)] pub struct Sample { // also stores the mono state on the heap to make the packing in arrays more efficient data: ThinArc, } impl Sample { pub const MAX_LENGTH: usize = 16_000_000; pub const MAX_RATE: usize = 192_000; /// this many frames need to be put on the start and the end to ensure that the interpolation algorithms work correctly. pub const PAD_SIZE_EACH: usize = 4; pub fn is_mono(&self) -> bool { self.data.header.header } /// len in Frames pub fn len_with_pad(&self) -> usize { if self.is_mono() { self.data.header.length } else { self.data.header.length } } /// This function also offsets the loaded sample data correctly, depending on the size of required processing data pub(crate) fn compute< const N: usize, // all implementations are generic over the ProcessingFrame type. here both possible ProcessingFrame types // are required, so that it can be decided at runtime which one to call. both are generated by the compiler // from the generic implementation Proc: ProcessingFunction + ProcessingFunction, >( &self, position: (usize, f32), ) -> Frame { const { assert!(N / 2 <= Self::PAD_SIZE_EACH) }; let half = const { (N - 1) / 2 }; let start_idx = position.0 - half; let end_idx = start_idx + N; if self.is_mono() { let data: &[f32; N] = self.data.slice[start_idx..end_idx].try_into().unwrap(); Frame::from(Proc::process(position.1, data)) } else { let data: &[Frame; N] = Frame::from_interleaved(&self.data.slice[start_idx * 2..end_idx * 2]) .try_into() .unwrap(); Proc::process(position.1, data) } } pub fn index(&self, idx: usize) -> Frame { if self.is_mono() { Frame::from(self.data.slice[idx]) } else { Frame::from([self.data.slice[idx * 2], self.data.slice[idx * 2 + 1]]) } } pub(crate) fn strongcount(&self) -> usize { ThinArc::strong_count(&self.data) } pub fn new_stereo_interpolated>(data: I) -> Self { Self::new_stereo_interpolated_padded( repeat_n(0f32, 2 * Self::PAD_SIZE_EACH) .chain(data) .chain(repeat_n(0f32, 2 * Self::PAD_SIZE_EACH)), ) } /// Should only be called if the Iterator already includes enough padding pub fn new_stereo_interpolated_padded>(data: I) -> Self { let data = Vec::from_iter(data); Self { data: ThinArc::from_header_and_slice(false, &data), } } // replace these with some kind of specialization as soon as it becomes available pub fn new_stereo_interpolated_padded_exact + ExactSizeIterator>( data: I, ) -> Self { Self { data: ThinArc::from_header_and_iter(false, data), } } pub fn new_mono>(data: I) -> Self { Self::new_mono_padded( repeat_n(0f32, Self::PAD_SIZE_EACH) .chain(data) .chain(repeat_n(0f32, Self::PAD_SIZE_EACH)), ) } pub fn new_mono_padded>(data: I) -> Self { let data = Vec::from_iter(data); Self { data: ThinArc::from_header_and_slice(true, &data), } } // replace these with some kind of specialization as soon as it becomes available pub fn new_mono_padded_exact + ExactSizeIterator>(data: I) -> Self { Self { data: ThinArc::from_header_and_iter(true, data), } } } impl Debug for Sample { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Sample") .field("mono", &self.is_mono()) .field("data_len", &self.len_with_pad()) .finish_non_exhaustive() } } #[derive(Debug, Default, Clone, Copy)] pub enum VibratoWave { #[default] Sine = 0, RampDown = 1, Square = 2, Random = 3, } #[derive(Clone, Copy, Debug)] pub struct SampleMetaData { pub default_volume: u8, pub global_volume: u8, pub default_pan: Option, pub vibrato_speed: u8, pub vibrato_depth: u8, pub vibrato_rate: u8, pub vibrato_waveform: VibratoWave, pub sample_rate: NonZero, pub base_note: Note, }