old school music tracker audio backend
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}