audio_processor_file/audio_file_processor/
mod.rs

1// Augmented Audio: Audio libraries and applications
2// Copyright (c) 2022 Pedro Tacla Yamada
3//
4// The MIT License (MIT)
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22// THE SOFTWARE.
23use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
24use std::time::Instant;
25
26use symphonia::core::probe::ProbeResult;
27
28use audio_garbage_collector::{Handle, Shared};
29use audio_processor_traits::{AudioBuffer, AudioContext, AudioProcessor, AudioProcessorSettings};
30use file_io::{AudioFileError, FileContentsStream};
31
32pub mod file_io;
33
34pub struct InMemoryAudioFile {
35    audio_file: ProbeResult,
36}
37
38impl InMemoryAudioFile {
39    pub fn new(audio_file: ProbeResult) -> Self {
40        InMemoryAudioFile { audio_file }
41    }
42
43    pub fn from_path(path: &str) -> Result<Self, AudioFileError> {
44        Ok(Self::new(file_io::default_read_audio_file(path)?))
45    }
46
47    /// Eagerly read the file onto memory, do sample rate conversion into the target
48    /// AudioProcessorSettings and return a VecAudioBuffer containing the file's contents.
49    #[cfg(feature = "samplerate")]
50    pub fn read_into_vec_audio_buffer(
51        &mut self,
52        settings: &AudioProcessorSettings,
53    ) -> Result<AudioBuffer<f32>, AudioFileError> {
54        use rayon::prelude::*;
55
56        let output_rate = settings.sample_rate();
57        let contents = file_io::read_file_contents(&mut self.audio_file)?;
58        let converted_channels: Vec<Vec<f32>> = (0..contents.spec().channels.count())
59            .into_par_iter()
60            .map(|channel_number| {
61                file_io::convert_audio_file_sample_rate(&contents, output_rate, channel_number)
62            })
63            .collect();
64
65        let mut output_buffer = AudioBuffer::empty();
66        output_buffer.resize(settings.output_channels(), converted_channels[0].len());
67        for (channel_index, channel) in converted_channels.iter().enumerate() {
68            for (sample_index, sample) in channel.iter().enumerate() {
69                output_buffer.set(channel_index, sample_index, *sample);
70            }
71        }
72
73        Ok(output_buffer)
74    }
75}
76
77pub struct AudioFileProcessorHandle {
78    audio_file_cursor: AtomicUsize,
79    is_playing: AtomicBool,
80    should_loop: AtomicBool,
81}
82
83impl AudioFileProcessorHandle {
84    /// Resume playback
85    pub fn play(&self) {
86        self.is_playing.store(true, Ordering::Relaxed);
87    }
88
89    /// Pause playback
90    pub fn pause(&self) {
91        self.is_playing.store(false, Ordering::Relaxed);
92    }
93
94    /// Stop playback and go back to the start of the file
95    pub fn stop(&self) {
96        self.is_playing.store(false, Ordering::Relaxed);
97        self.audio_file_cursor.store(0, Ordering::Relaxed);
98    }
99
100    /// Whether the file is being played back
101    pub fn is_playing(&self) -> bool {
102        self.is_playing.load(Ordering::Relaxed)
103    }
104
105    pub fn set_should_loop(&self, should_loop: bool) {
106        self.should_loop.store(should_loop, Ordering::Relaxed);
107    }
108
109    pub fn should_loop(&self) -> bool {
110        self.should_loop.load(Ordering::Relaxed)
111    }
112}
113
114/// An audio processor which plays a file in loop
115pub struct AudioFileProcessor {
116    audio_file_settings: InMemoryAudioFile,
117    audio_settings: AudioProcessorSettings,
118    buffer: Vec<Vec<f32>>,
119    handle: Shared<AudioFileProcessorHandle>,
120}
121
122impl AudioFileProcessor {
123    pub fn from_path(
124        handle: &Handle,
125        audio_settings: AudioProcessorSettings,
126        path: &str,
127    ) -> Result<Self, AudioFileError> {
128        let audio_file_settings = InMemoryAudioFile::new(file_io::default_read_audio_file(path)?);
129        Ok(Self::new(handle, audio_file_settings, audio_settings))
130    }
131
132    pub fn new(
133        gc_handle: &Handle,
134        audio_file_settings: InMemoryAudioFile,
135        audio_settings: AudioProcessorSettings,
136    ) -> Self {
137        let handle = Shared::new(
138            gc_handle,
139            AudioFileProcessorHandle {
140                audio_file_cursor: AtomicUsize::new(0),
141                is_playing: AtomicBool::new(true),
142                should_loop: AtomicBool::new(true),
143            },
144        );
145
146        AudioFileProcessor {
147            audio_file_settings,
148            audio_settings,
149            buffer: Vec::new(),
150            handle,
151        }
152    }
153
154    /// Unsafe get buffer for offline rendering
155    pub fn num_samples(&self) -> usize {
156        if self.buffer.is_empty() {
157            0
158        } else {
159            self.buffer[0].len()
160        }
161    }
162
163    /// Unsafe get buffer for offline rendering
164    pub fn buffer(&self) -> &Vec<Vec<f32>> {
165        &self.buffer
166    }
167
168    pub fn handle(&self) -> &Shared<AudioFileProcessorHandle> {
169        &self.handle
170    }
171
172    /// Resume playback
173    pub fn play(&self) {
174        self.handle.play()
175    }
176
177    /// Pause playback
178    pub fn pause(&self) {
179        self.handle.pause()
180    }
181
182    /// Stop playback and go back to the start of the file
183    pub fn stop(&self) {
184        self.handle.stop()
185    }
186
187    /// Whether the file is being played back
188    pub fn is_playing(&self) -> bool {
189        self.handle.is_playing()
190    }
191
192    pub fn process_single(&self) -> impl Iterator<Item = f32> + '_ {
193        let handle = &self.handle;
194        let audio_file_cursor = handle.audio_file_cursor.load(Ordering::Relaxed);
195        let iterator = self
196            .buffer
197            .iter()
198            .map(move |channel| channel[audio_file_cursor]);
199
200        let mut audio_file_cursor: usize = audio_file_cursor;
201        audio_file_cursor += 1;
202        if audio_file_cursor >= self.buffer[0].len() {
203            audio_file_cursor = 0;
204        }
205        handle
206            .audio_file_cursor
207            .store(audio_file_cursor, Ordering::Relaxed);
208
209        iterator
210    }
211}
212
213impl AudioProcessor for AudioFileProcessor {
214    type SampleType = f32;
215
216    /// Prepares for playback
217    ///
218    /// Note: Currently this will load the audio file on the audio-thread.
219    /// It'd be an interesting exercise to perform this on a background thread.
220    fn prepare(&mut self, context: &mut AudioContext) {
221        let audio_settings = context.settings;
222        log::info!("Preparing for audio file playback");
223        self.audio_settings = audio_settings;
224
225        self.buffer.clear();
226        self.buffer.reserve(self.audio_settings.output_channels());
227
228        let start = Instant::now();
229        log::info!("Reading audio file onto memory");
230
231        let mut run = || -> Result<(), file_io::AudioFileError> {
232            let input_stream = FileContentsStream::new(&mut self.audio_file_settings.audio_file)?;
233            let converted_stream = file_io::convert_audio_file_stream_sample_rate(
234                input_stream,
235                audio_settings.sample_rate(),
236            );
237
238            for buffer in converted_stream {
239                self.buffer.resize(buffer.num_channels(), vec![]);
240
241                for (channel, source) in self.buffer.iter_mut().zip(buffer.channels()) {
242                    for sample in source {
243                        channel.push(*sample)
244                    }
245                }
246            }
247
248            // With block size 1024, rubato will introduce 256 samples of latency that need to be
249            // skipped.
250            #[cfg(feature = "rubato")]
251            for channel in self.buffer.iter_mut() {
252                *channel = channel.iter().skip(256).cloned().collect();
253            }
254
255            Ok(())
256        };
257
258        match run() {
259            Ok(_) => {
260                log::info!("Read input file duration={}ms", start.elapsed().as_millis());
261            }
262            Err(err) => {
263                log::error!("Failed to read input file {}", err);
264            }
265        }
266    }
267
268    fn process(&mut self, _context: &mut AudioContext, data: &mut AudioBuffer<Self::SampleType>) {
269        let is_playing = self.handle.is_playing.load(Ordering::Relaxed);
270
271        if !is_playing {
272            return;
273        }
274
275        let should_loop = self.handle.should_loop();
276        let start_cursor = self.handle.audio_file_cursor.load(Ordering::Relaxed);
277        let mut audio_file_cursor = start_cursor;
278
279        for sample_num in 0..data.num_samples() {
280            for channel_index in 0..data.num_channels() {
281                let audio_input = self.buffer[channel_index][audio_file_cursor];
282                let value = audio_input;
283                data.channel_mut(channel_index)[sample_num] += value;
284            }
285
286            audio_file_cursor += 1;
287            if audio_file_cursor >= self.buffer[0].len() {
288                audio_file_cursor = 0;
289
290                if !should_loop {
291                    self.handle.stop();
292                    break;
293                }
294            }
295        }
296
297        let _ = self.handle.audio_file_cursor.compare_exchange(
298            start_cursor,
299            audio_file_cursor,
300            Ordering::Relaxed,
301            Ordering::Relaxed,
302        );
303    }
304}
305
306#[cfg(test)]
307mod test {
308    use audio_garbage_collector::GarbageCollector;
309
310    use super::*;
311
312    fn setup() -> (GarbageCollector, InMemoryAudioFile) {
313        wisual_logger::init_from_env();
314
315        let garbage_collector = GarbageCollector::default();
316        let path = format!(
317            "{}{}",
318            env!("CARGO_MANIFEST_DIR"),
319            "/../../../../input-files/1sec-sine.mp3"
320        );
321        let audio_file_settings = InMemoryAudioFile::from_path(&path).unwrap();
322        (garbage_collector, audio_file_settings)
323    }
324
325    #[test]
326    fn test_in_memory_audio_file_can_be_created_from_probe() {
327        let path = format!(
328            "{}{}",
329            env!("CARGO_MANIFEST_DIR"),
330            "/../../../../input-files/1sec-sine.mp3"
331        );
332        let probe_result = file_io::default_read_audio_file(&path).unwrap();
333        let _audio_file = InMemoryAudioFile::new(probe_result);
334    }
335
336    /**
337     * Test a stopped audio processor is silent
338     */
339    #[test]
340    fn test_audio_file_processor_stopped_is_silent() {
341        let (garbage_collector, audio_file_settings) = setup();
342
343        let mut audio_file_processor = AudioFileProcessor::new(
344            garbage_collector.handle(),
345            audio_file_settings,
346            Default::default(),
347        );
348        let mut context = AudioContext::default();
349        audio_file_processor.prepare(&mut context);
350
351        audio_file_processor.stop();
352        let mut sample_buffer = AudioBuffer::empty();
353        sample_buffer.resize(2, 44100);
354        audio_file_processor.process(&mut context, &mut sample_buffer);
355
356        assert!(
357            audio_processor_testing_helpers::rms_level(sample_buffer.channel(0)) < f32::EPSILON
358        );
359    }
360
361    /**
362     * Test a running audio processor is not silent
363     */
364    #[test]
365    fn test_audio_file_processor_playing_is_not_silent() {
366        let (garbage_collector, audio_file_settings) = setup();
367
368        let mut audio_file_processor = AudioFileProcessor::new(
369            garbage_collector.handle(),
370            audio_file_settings,
371            Default::default(),
372        );
373        let mut context = AudioContext::default();
374        audio_file_processor.prepare(&mut context);
375
376        let mut sample_buffer = AudioBuffer::empty();
377        sample_buffer.resize(2, 44100);
378        audio_file_processor.play();
379        audio_file_processor.process(&mut context, &mut sample_buffer);
380
381        assert!(
382            audio_processor_testing_helpers::rms_level(sample_buffer.channel(0)) > f32::EPSILON
383        );
384    }
385}