audio_processor_bitcrusher/
lib.rs1use audio_garbage_collector::{make_shared, Shared};
32use audio_processor_traits::atomic_float::{AtomicFloatRepresentable, AtomicValue};
33use audio_processor_traits::parameters::{
34 make_handle_ref, AudioProcessorHandleProvider, AudioProcessorHandleRef,
35};
36use audio_processor_traits::{AudioBuffer, AudioContext, AudioProcessor, Float};
37pub use generic_handle::BitCrusherHandleRef;
38
39mod generic_handle;
40
41pub type BitCrusherHandle = BitCrusherHandleImpl<f32>;
42
43pub struct BitCrusherHandleImpl<ST>
44where
45 ST: AtomicFloatRepresentable + Float,
46{
47 sample_rate: ST::AtomicType,
48 bit_rate: ST::AtomicType,
49}
50
51impl<ST> BitCrusherHandleImpl<ST>
52where
53 ST: Float + AtomicFloatRepresentable,
54 ST: From<f32>,
55 f32: From<ST>,
56{
57 pub fn sample_rate(&self) -> f32 {
58 self.sample_rate.get().into()
59 }
60
61 pub fn bit_rate(&self) -> f32 {
62 self.bit_rate.get().into()
63 }
64
65 pub fn set_sample_rate(&self, sample_rate: f32) {
66 self.sample_rate.set(sample_rate.into());
67 }
68
69 pub fn set_bit_rate(&self, bit_rate: f32) {
70 self.bit_rate.set(bit_rate.into());
71 }
72}
73
74impl<ST> Default for BitCrusherHandleImpl<ST>
75where
76 ST: Float + AtomicFloatRepresentable,
77 ST: From<f32>,
78{
79 fn default() -> Self {
80 Self {
81 sample_rate: ST::AtomicType::from(44100.0.into()),
82 bit_rate: ST::AtomicType::from(44100.0.into()),
83 }
84 }
85}
86
87pub type BitCrusherProcessor = BitCrusherProcessorImpl<f32>;
88
89pub struct BitCrusherProcessorImpl<ST = f32>
90where
91 ST: AtomicFloatRepresentable + Float,
92{
93 handle: Shared<BitCrusherHandleImpl<ST>>,
94}
95
96impl<ST> AudioProcessorHandleProvider for BitCrusherProcessorImpl<ST>
97where
98 ST: AtomicFloatRepresentable + Float + 'static,
99 ST::AtomicType: Send + Sync,
100 ST: From<f32>,
101 f32: From<ST>,
102{
103 fn generic_handle(&self) -> AudioProcessorHandleRef {
104 make_handle_ref(BitCrusherHandleRef::<ST>::new(self.handle.clone()))
105 }
106}
107
108impl<ST> BitCrusherProcessorImpl<ST>
109where
110 ST: AtomicFloatRepresentable + Float + 'static,
111 ST::AtomicType: Send + Sync,
112 ST: From<f32>,
113 f32: From<ST>,
114{
115 pub fn new(handle: Shared<BitCrusherHandleImpl<ST>>) -> Self {
116 BitCrusherProcessorImpl { handle }
117 }
118
119 pub fn handle(&self) -> &Shared<BitCrusherHandleImpl<ST>> {
120 &self.handle
121 }
122
123 fn step_size(&self) -> usize {
124 (self.handle.sample_rate() / self.handle.bit_rate()) as usize
125 }
126}
127
128impl<ST> Default for BitCrusherProcessorImpl<ST>
129where
130 ST: AtomicFloatRepresentable + Float + 'static,
131 ST::AtomicType: Send + Sync,
132 ST: From<f32>,
133 f32: From<ST>,
134{
135 fn default() -> Self {
136 Self::new(make_shared(BitCrusherHandleImpl::default()))
137 }
138}
139
140impl<ST> AudioProcessor for BitCrusherProcessorImpl<ST>
141where
142 ST: AtomicFloatRepresentable + Float + 'static,
143 ST::AtomicType: Send + Sync,
144 ST: From<f32>,
145 f32: From<ST>,
146{
147 type SampleType = ST;
148
149 fn prepare(&mut self, context: &mut AudioContext) {
150 let settings = context.settings;
151 self.handle.set_sample_rate(settings.sample_rate());
152 if (self.handle.sample_rate() - self.handle.bit_rate()).abs() < f32::EPSILON {
153 self.handle.set_bit_rate(settings.sample_rate());
154 }
155 }
156
157 fn process(&mut self, _context: &mut AudioContext, data: &mut AudioBuffer<Self::SampleType>) {
158 let step_size = self.step_size();
159
160 let mut sample_index = 0;
161 let buffer_size = data.num_samples();
162
163 while sample_index < buffer_size {
164 let first_index = sample_index;
165 let limit_index = (sample_index + step_size).min(buffer_size);
166
167 while sample_index < limit_index {
168 for channel_index in 0..data.num_channels() {
169 let value = *data.get(channel_index, first_index);
170 data.set(channel_index, sample_index, value);
171 }
172 sample_index += 1;
173 }
174 }
175 }
176}
177
178#[cfg(test)]
179mod test {
180 use std::time::Duration;
181
182 use audio_processor_testing_helpers::sine_buffer;
183
184 use audio_processor_traits::AudioProcessorSettings;
185
186 use super::*;
187
188 #[test]
189 fn test_construct_bitcrusher() {
190 let _processor = BitCrusherProcessor::default();
191 }
192
193 #[test]
194 fn test_step_size_is_1_on_passthrough() {
195 let settings = AudioProcessorSettings::default();
196 let mut context = AudioContext::from(settings);
197 let mut processor = BitCrusherProcessor::default();
198 processor.prepare(&mut context);
199 assert_eq!(processor.step_size(), 1);
200 }
201
202 #[test]
203 fn test_step_size_is_2_on_lower_bitrate() {
204 let settings = AudioProcessorSettings::default();
205 let mut context = AudioContext::from(settings);
206 let mut processor = BitCrusherProcessor::default();
207 processor.prepare(&mut context);
208 processor
209 .handle()
210 .set_bit_rate(settings.sample_rate() / 2.0);
211 assert_eq!(processor.step_size(), 2);
212 }
213
214 #[test]
215 fn test_passthrough_bitcrusher() {
216 let settings = AudioProcessorSettings::default();
217 let mut context = AudioContext::from(settings);
218 let mut processor = BitCrusherProcessor::default();
219 processor.prepare(&mut context);
220
221 let input_buffer = AudioBuffer::from_interleaved(
222 1,
223 &sine_buffer(settings.sample_rate(), 440.0, Duration::from_millis(10)),
224 );
225 let mut output_buffer = input_buffer.clone();
226 processor.process(&mut context, &mut output_buffer);
227
228 assert_eq!(input_buffer.channel(0), output_buffer.channel(0));
229 }
230}