audio_processor_analysis/
envelope_follower_processor.rs1use audio_garbage_collector::{make_shared, Shared};
45use audio_processor_traits::atomic_float::{AtomicFloatRepresentable, AtomicValue};
46use audio_processor_traits::simple_processor::MonoAudioProcessor;
47use audio_processor_traits::{AudioContext, AudioProcessorSettings, Float};
48use std::time::Duration;
49
50fn calculate_multiplier<F: Float>(sample_rate: F, duration_ms: F) -> F {
51 let attack_secs = duration_ms * F::from(0.001).unwrap();
52 let attack_samples = sample_rate * attack_secs;
53 (F::from(-1.0).unwrap() / attack_samples).exp2()
54}
55
56pub type EnvelopeFollowerProcessor = EnvelopeFollowerProcessorImpl<f32>;
58
59pub type EnvelopeFollowerHandle = EnvelopeFollowerHandleImpl<f32>;
61
62pub struct EnvelopeFollowerHandleImpl<ST: AtomicFloatRepresentable> {
65 envelope_state: ST::AtomicType,
66 attack_multiplier: ST::AtomicType,
67 release_multiplier: ST::AtomicType,
68 attack_duration_ms: ST::AtomicType,
69 release_duration_ms: ST::AtomicType,
70 sample_rate: ST::AtomicType,
71 _marker: std::marker::PhantomData<ST>,
72}
73
74impl<ST: Float + AtomicFloatRepresentable> EnvelopeFollowerHandleImpl<ST> {
75 pub fn state(&self) -> ST {
77 ST::from(self.envelope_state.get()).unwrap()
78 }
79
80 pub fn set_state(&self, state: ST) {
82 self.envelope_state.set(state);
83 }
84
85 pub fn set_attack(&self, duration: Duration) {
87 let duration_ms = ST::from(duration.as_millis()).unwrap();
88 self.attack_duration_ms.set(duration_ms);
89 self.attack_multiplier
90 .set(calculate_multiplier(self.sample_rate.get(), duration_ms));
91 }
92
93 pub fn set_release(&self, duration: Duration) {
95 let duration_ms = ST::from(duration.as_millis()).unwrap();
96 self.release_duration_ms.set(duration_ms);
97 self.release_multiplier
98 .set(calculate_multiplier(self.sample_rate.get(), duration_ms));
99 }
100}
101
102pub struct EnvelopeFollowerProcessorImpl<ST: AtomicFloatRepresentable> {
121 handle: Shared<EnvelopeFollowerHandleImpl<ST>>,
122}
123
124impl<ST> Default for EnvelopeFollowerProcessorImpl<ST>
125where
126 ST: AtomicFloatRepresentable + Float + Send + 'static,
127 ST::AtomicType: Send + 'static,
128{
129 fn default() -> Self {
130 Self::new(Duration::from_millis(10), Duration::from_millis(10))
131 }
132}
133
134impl<ST> EnvelopeFollowerProcessorImpl<ST>
135where
136 ST: AtomicFloatRepresentable + Float + Send + 'static,
137 ST::AtomicType: Send + 'static,
138{
139 pub fn new(attack_duration: Duration, release_duration: Duration) -> Self {
141 let sample_rate = AudioProcessorSettings::default().sample_rate as f64;
142 EnvelopeFollowerProcessorImpl {
143 handle: make_shared(EnvelopeFollowerHandleImpl {
144 envelope_state: ST::AtomicType::from(ST::zero()),
145 attack_multiplier: ST::AtomicType::from(
146 ST::from(calculate_multiplier(
147 sample_rate,
148 attack_duration.as_millis() as f64,
149 ))
150 .unwrap(),
151 ),
152 release_multiplier: ST::AtomicType::from(
153 ST::from(calculate_multiplier(
154 sample_rate,
155 release_duration.as_millis() as f64,
156 ))
157 .unwrap(),
158 ),
159 attack_duration_ms: (ST::AtomicType::from(
160 ST::from(attack_duration.as_millis() as f64).unwrap(),
161 )),
162 release_duration_ms: ST::AtomicType::from(
163 ST::from(release_duration.as_millis() as f64).unwrap(),
164 ),
165 sample_rate: ST::AtomicType::from(ST::from(sample_rate).unwrap()),
166 _marker: Default::default(),
167 }),
168 }
169 }
170
171 pub fn handle(&self) -> &Shared<EnvelopeFollowerHandleImpl<ST>> {
173 &self.handle
174 }
175}
176
177impl<ST: AtomicFloatRepresentable + Copy + Float> MonoAudioProcessor
178 for EnvelopeFollowerProcessorImpl<ST>
179{
180 type SampleType = ST;
181
182 fn m_prepare(&mut self, context: &mut AudioContext) {
183 let sample_rate = ST::from(context.settings.sample_rate as f64).unwrap();
184 self.handle.sample_rate.set(sample_rate);
185 self.handle.attack_multiplier.set(calculate_multiplier(
186 sample_rate,
187 self.handle.attack_duration_ms.get(),
188 ));
189 self.handle.release_multiplier.set(calculate_multiplier(
190 sample_rate,
191 self.handle.release_duration_ms.get(),
192 ));
193 }
194
195 fn m_process(
196 &mut self,
197 _context: &mut AudioContext,
198 sample: Self::SampleType,
199 ) -> Self::SampleType {
200 let value = sample.abs();
201
202 let handle = &self.handle;
203 let envelope = ST::from(handle.envelope_state.get()).unwrap();
204 let attack = ST::from(handle.attack_multiplier.get()).unwrap();
205 let release = ST::from(handle.release_multiplier.get()).unwrap();
206
207 let one = ST::from(1.0).unwrap();
208 if value > envelope {
209 handle
210 .envelope_state
211 .set((one - attack) * value + attack * envelope);
212 } else {
213 handle
214 .envelope_state
215 .set((one - release) * value + release * envelope);
216 }
217
218 sample
219 }
220}
221
222#[cfg(test)]
223mod test {
224 use audio_processor_file::AudioFileProcessor;
225 use audio_processor_testing_helpers::charts::draw_vec_chart;
226 use audio_processor_testing_helpers::relative_path;
227 use audio_processor_traits::{AudioBuffer, AudioProcessor, AudioProcessorSettings};
228
229 use super::*;
230
231 #[test]
232 fn test_draw_envelope() {
233 let output_path = relative_path!("src/envelope_follower_processor");
234 let input_file_path = relative_path!("../../../../input-files/C3-loop.mp3");
235
236 let settings = AudioProcessorSettings::default();
237 let mut context = AudioContext::from(settings);
238 let mut input = AudioFileProcessor::from_path(
239 audio_garbage_collector::handle(),
240 settings,
241 &input_file_path,
242 )
243 .unwrap();
244 input.prepare(&mut context);
245
246 let mut envelope_follower = EnvelopeFollowerProcessor::default();
247 envelope_follower.m_prepare(&mut context);
248
249 let mut buffer = AudioBuffer::empty();
250 buffer.resize(1, settings.block_size());
251 let num_chunks = (input.num_samples() / 8) / settings.block_size();
252
253 let mut envelope_readings = vec![];
254 for _chunk in 0..num_chunks {
255 for sample in buffer.slice_mut() {
256 *sample = 0.0;
257 }
258
259 input.process(&mut context, &mut buffer);
260 for sample_num in 0..buffer.num_samples() {
261 let sample = *buffer.get(0, sample_num);
262 envelope_follower.m_process(&mut context, sample);
263 envelope_readings.push(envelope_follower.handle.envelope_state.get());
264 }
265 }
266
267 draw_vec_chart(&output_path, "Envelope", envelope_readings);
268 }
269}