Skip to main content

rust_audio_api/nodes/
file.rs

1use crate::nodes::resampler::{ResamplerState, RingIter};
2use crate::types::{AUDIO_UNIT_SIZE, AudioUnit};
3use dasp::signal::Signal;
4use ringbuf::HeapRb;
5use ringbuf::traits::{Observer, Producer, Split};
6use std::sync::Arc;
7use std::sync::atomic::{AtomicBool, Ordering};
8use std::thread;
9use std::time::Duration;
10use symphonia::core::audio::SampleBuffer;
11use symphonia::core::codecs::DecoderOptions;
12use symphonia::core::formats::FormatOptions;
13use symphonia::core::io::MediaSourceStream;
14use symphonia::core::meta::MetadataOptions;
15use symphonia::core::probe::Hint;
16
17/// A node that reads and decodes audio from a file.
18///
19/// It uses the `symphonia` library to support various audio formats.
20/// Like [`MicrophoneNode`][crate::nodes::MicrophoneNode], it handles
21/// sample rate conversion automatically.
22///
23/// # Example
24/// ```no_run
25/// use rust_audio_api::nodes::{FileNode, NodeType};
26/// use rust_audio_api::AudioContext;
27///
28/// let mut ctx = AudioContext::new().unwrap();
29/// let sample_rate = ctx.sample_rate();
30///
31/// let dest_id = ctx.build_graph(|builder| {
32///     let file = FileNode::new("music.mp3", sample_rate).unwrap();
33///     builder.add_node(NodeType::File(file))
34/// });
35/// ```
36pub struct FileNode {
37    resampler: ResamplerState,
38    gain: f32,
39    _running: Arc<AtomicBool>,
40}
41
42impl FileNode {
43    /// Creates a new `FileNode` for the specified file path.
44    ///
45    /// # Parameters
46    /// - `file_path`: Path to the audio file.
47    /// - `target_sample_rate`: Processing sample rate.
48    pub fn new(file_path: &str, target_sample_rate: u32) -> Result<Self, anyhow::Error> {
49        let file = std::fs::File::open(file_path)?;
50        let mss = MediaSourceStream::new(Box::new(file), Default::default());
51
52        let hint = Hint::new();
53        let format_opts = FormatOptions::default();
54        let metadata_opts = MetadataOptions::default();
55        let decoder_opts = DecoderOptions::default();
56
57        let probed =
58            symphonia::default::get_probe().format(&hint, mss, &format_opts, &metadata_opts)?;
59
60        let mut format = probed.format;
61
62        let track = format
63            .tracks()
64            .iter()
65            .find(|t| t.codec_params.codec != symphonia::core::codecs::CODEC_TYPE_NULL)
66            .cloned()
67            .ok_or_else(|| anyhow::anyhow!("No audio track found"))?;
68
69        let track_id = track.id;
70        let mut decoder =
71            symphonia::default::get_codecs().make(&track.codec_params, &decoder_opts)?;
72
73        let channels = track.codec_params.channels.unwrap_or_default().count();
74        let sample_rate = track.codec_params.sample_rate.unwrap_or(target_sample_rate);
75
76        println!("Audio file sample rate: {:?}", sample_rate);
77
78        // Create 2-second raw f32 buffer
79        let capacity = sample_rate as usize * channels * 2;
80        let ringbuf = HeapRb::<f32>::new(capacity);
81        let (mut producer, consumer) = ringbuf.split();
82
83        let running = Arc::new(AtomicBool::new(true));
84        let running_clone = running.clone();
85
86        thread::spawn(move || {
87            let mut sample_buf = None;
88
89            while running_clone.load(Ordering::Relaxed) {
90                if producer.is_full() {
91                    thread::sleep(Duration::from_millis(10));
92                    continue;
93                }
94
95                let packet = match format.next_packet() {
96                    Ok(p) => p,
97                    Err(_) => break, // EOF or Error
98                };
99
100                if packet.track_id() != track_id {
101                    continue;
102                }
103
104                let decoded = match decoder.decode(&packet) {
105                    Ok(d) => d,
106                    Err(_) => continue,
107                };
108
109                if sample_buf.is_none() {
110                    let spec = *decoded.spec();
111                    let duration = decoded.capacity() as u64;
112                    sample_buf = Some(SampleBuffer::<f32>::new(duration, spec));
113                }
114
115                let buf = sample_buf.as_mut().unwrap();
116                buf.copy_interleaved_ref(decoded);
117
118                let samples = buf.samples();
119
120                for &sample in samples {
121                    if !running_clone.load(Ordering::Relaxed) {
122                        break;
123                    }
124                    while producer.is_full() && running_clone.load(Ordering::Relaxed) {
125                        thread::sleep(Duration::from_millis(1));
126                    }
127                    let _ = producer.try_push(sample);
128                }
129            }
130        });
131
132        let ring_iter = RingIter { consumer, channels };
133
134        let resampler = if sample_rate != target_sample_rate {
135            let ring_buffer = dasp::ring_buffer::Fixed::from([[0.0; 2]; AUDIO_UNIT_SIZE]);
136            let sinc = dasp::interpolate::sinc::Sinc::new(ring_buffer);
137            let converter =
138                ring_iter.from_hz_to_hz(sinc, sample_rate as f64, target_sample_rate as f64);
139            ResamplerState::Resampling(Box::new(converter))
140        } else {
141            ResamplerState::Passthrough(ring_iter)
142        };
143
144        Ok(Self {
145            resampler,
146            gain: 1.0,
147            _running: running,
148        })
149    }
150
151    /// Sets the output gain for the file playback.
152    pub fn set_gain(&mut self, gain: f32) {
153        self.gain = gain;
154    }
155
156    #[inline(always)]
157    pub fn process(&mut self, _input: Option<&AudioUnit>, output: &mut AudioUnit) {
158        match &mut self.resampler {
159            ResamplerState::Passthrough(iter) => {
160                for out in output.iter_mut().take(AUDIO_UNIT_SIZE) {
161                    *out = iter.next();
162                }
163            }
164            ResamplerState::Resampling(converter) => {
165                for out in output.iter_mut().take(AUDIO_UNIT_SIZE) {
166                    *out = converter.next();
167                }
168            }
169        }
170
171        // Apply gain safely using dasp slice operations
172        dasp::slice::map_in_place(&mut output[..], |frame| {
173            [frame[0] * self.gain, frame[1] * self.gain]
174        });
175    }
176}
177
178impl Drop for FileNode {
179    fn drop(&mut self) {
180        self._running.store(false, Ordering::Relaxed);
181    }
182}