old school music tracker audio backend
at dev 178 lines 5.5 kB view raw
1use std::{ 2 fmt::Debug, 3 iter::repeat_n, 4 num::NonZero, 5 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}, 6}; 7 8use triomphe::ThinArc; 9 10use crate::{audio_processing::Frame, project::note_event::Note}; 11 12// implemented for f32 and Frame 13pub(crate) trait ProcessingFrame: 14 Add<Self, Output = Self> 15 + AddAssign<Self> 16 + Sub<Self, Output = Self> 17 + SubAssign<Self> 18 + Mul<f32, Output = Self> 19 + MulAssign<f32> 20 + Div<f32, Output = Self> 21 + DivAssign<f32> 22 + Copy 23{ 24 fn mul_add(self, mul: f32, add: Self) -> Self; 25} 26 27impl ProcessingFrame for f32 { 28 fn mul_add(self, mul: f32, add: Self) -> Self { 29 f32::mul_add(self, mul, add) 30 } 31} 32 33pub(crate) trait ProcessingFunction<const N: usize, Fr: ProcessingFrame> { 34 fn process(position: f32, data: &[Fr; N]) -> Fr; 35} 36 37#[derive(Clone)] 38pub struct Sample { 39 // also stores the mono state on the heap to make the packing in arrays more efficient 40 data: ThinArc<bool, f32>, 41} 42 43impl Sample { 44 pub const MAX_LENGTH: usize = 16_000_000; 45 pub const MAX_RATE: usize = 192_000; 46 /// this many frames need to be put on the start and the end to ensure that the interpolation algorithms work correctly. 47 pub const PAD_SIZE_EACH: usize = 4; 48 49 pub fn is_mono(&self) -> bool { 50 self.data.header.header 51 } 52 53 /// len in Frames 54 pub fn len_with_pad(&self) -> usize { 55 if self.is_mono() { 56 self.data.header.length 57 } else { 58 self.data.header.length 59 } 60 } 61 62 /// This function also offsets the loaded sample data correctly, depending on the size of required processing data 63 pub(crate) fn compute< 64 const N: usize, 65 // all implementations are generic over the ProcessingFrame type. here both possible ProcessingFrame types 66 // are required, so that it can be decided at runtime which one to call. both are generated by the compiler 67 // from the generic implementation 68 Proc: ProcessingFunction<N, f32> + ProcessingFunction<N, Frame>, 69 >( 70 &self, 71 position: (usize, f32), 72 ) -> Frame { 73 const { assert!(N / 2 <= Self::PAD_SIZE_EACH) }; 74 let half = const { (N - 1) / 2 }; 75 let start_idx = position.0 - half; 76 let end_idx = start_idx + N; 77 if self.is_mono() { 78 let data: &[f32; N] = self.data.slice[start_idx..end_idx].try_into().unwrap(); 79 Frame::from(Proc::process(position.1, data)) 80 } else { 81 let data: &[Frame; N] = 82 Frame::from_interleaved(&self.data.slice[start_idx * 2..end_idx * 2]) 83 .try_into() 84 .unwrap(); 85 Proc::process(position.1, data) 86 } 87 } 88 89 pub fn index(&self, idx: usize) -> Frame { 90 if self.is_mono() { 91 Frame::from(self.data.slice[idx]) 92 } else { 93 Frame::from([self.data.slice[idx * 2], self.data.slice[idx * 2 + 1]]) 94 } 95 } 96 97 pub(crate) fn strongcount(&self) -> usize { 98 ThinArc::strong_count(&self.data) 99 } 100 101 pub fn new_stereo_interpolated<I: IntoIterator<Item = f32>>(data: I) -> Self { 102 Self::new_stereo_interpolated_padded( 103 repeat_n(0f32, 2 * Self::PAD_SIZE_EACH) 104 .chain(data) 105 .chain(repeat_n(0f32, 2 * Self::PAD_SIZE_EACH)), 106 ) 107 } 108 109 /// Should only be called if the Iterator already includes enough padding 110 pub fn new_stereo_interpolated_padded<I: IntoIterator<Item = f32>>(data: I) -> Self { 111 let data = Vec::from_iter(data); 112 Self { 113 data: ThinArc::from_header_and_slice(false, &data), 114 } 115 } 116 117 // replace these with some kind of specialization as soon as it becomes available 118 pub fn new_stereo_interpolated_padded_exact<I: Iterator<Item = f32> + ExactSizeIterator>( 119 data: I, 120 ) -> Self { 121 Self { 122 data: ThinArc::from_header_and_iter(false, data), 123 } 124 } 125 126 pub fn new_mono<I: IntoIterator<Item = f32>>(data: I) -> Self { 127 Self::new_mono_padded( 128 repeat_n(0f32, Self::PAD_SIZE_EACH) 129 .chain(data) 130 .chain(repeat_n(0f32, Self::PAD_SIZE_EACH)), 131 ) 132 } 133 134 pub fn new_mono_padded<I: IntoIterator<Item = f32>>(data: I) -> Self { 135 let data = Vec::from_iter(data); 136 Self { 137 data: ThinArc::from_header_and_slice(true, &data), 138 } 139 } 140 141 // replace these with some kind of specialization as soon as it becomes available 142 pub fn new_mono_padded_exact<I: Iterator<Item = f32> + ExactSizeIterator>(data: I) -> Self { 143 Self { 144 data: ThinArc::from_header_and_iter(true, data), 145 } 146 } 147} 148 149impl Debug for Sample { 150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 151 f.debug_struct("Sample") 152 .field("mono", &self.is_mono()) 153 .field("data_len", &self.len_with_pad()) 154 .finish_non_exhaustive() 155 } 156} 157 158#[derive(Debug, Default, Clone, Copy)] 159pub enum VibratoWave { 160 #[default] 161 Sine = 0, 162 RampDown = 1, 163 Square = 2, 164 Random = 3, 165} 166 167#[derive(Clone, Copy, Debug)] 168pub struct SampleMetaData { 169 pub default_volume: u8, 170 pub global_volume: u8, 171 pub default_pan: Option<u8>, 172 pub vibrato_speed: u8, 173 pub vibrato_depth: u8, 174 pub vibrato_rate: u8, 175 pub vibrato_waveform: VibratoWave, 176 pub sample_rate: NonZero<u32>, 177 pub base_note: Note, 178}