Skip to main content

audiobox/
stream.rs

1//! Incremental decoding and encoding.
2//!
3//! [`Decoder`] decodes WAV, FLAC, MP3 or Ogg Vorbis bytes held in memory a chunk at a time, with
4//! seeking. [`PcmDecoder`] and [`PcmEncoder`] convert raw PCM whose input chunks may
5//! end in the middle of a frame, and [`Resampler`] resamples across chunks.
6//!
7//! With `file-stream` on native targets, `FileStream` reads WAV/FLAC/MP3 through a
8//! dedicated reader thread and bounded encoded/PCM buffers. Its `read` and `seek`
9//! never wait or allocate. `ErrorCode::Underrun` means retry; only `Ok(0)` means EOF
10//! for a destination holding at least one frame. Keep `FileStreamHandle` off the
11//! callback: it owns cancellation and joins the worker on destruction. Mixer
12//! integration is enabled by `mixer`; underruns produce silence while source
13//! position holds and output-time fades continue. Disk availability can change
14//! the timing of these silent gaps; delivered PCM itself matches memory decoding.
15pub use crate::decoder::{Decoder, DecoderOptions};
16#[cfg(all(feature = "file-stream", not(target_family = "wasm")))]
17mod file;
18use crate::{Audio, Endian, Error, Limits, Result, SampleFormat};
19#[cfg(all(feature = "file-stream", not(target_family = "wasm")))]
20pub use file::{FileStream, FileStreamHandle, FileStreamOptions, FileStreamStatus};
21
22#[derive(Debug)]
23pub struct PcmDecoder {
24    format: SampleFormat,
25    endian: Endian,
26    channels: usize,
27    rate: u32,
28    limits: Limits,
29    pending: Vec<u8>,
30    frames: usize,
31}
32impl PcmDecoder {
33    pub fn new(
34        format: SampleFormat,
35        endian: Endian,
36        channels: usize,
37        rate: u32,
38        limits: Limits,
39    ) -> Result<Self> {
40        limits.check(0, channels, rate)?;
41        Ok(Self {
42            format,
43            endian,
44            channels,
45            rate,
46            limits,
47            pending: Vec::new(),
48            frames: 0,
49        })
50    }
51    pub fn push(&mut self, bytes: &[u8]) -> Result<Audio> {
52        let stride = self
53            .channels
54            .checked_mul(self.format.bytes())
55            .ok_or_else(|| Error::invalid("PCM stride overflow"))?;
56        let n = self
57            .pending
58            .len()
59            .checked_add(bytes.len())
60            .ok_or_else(|| Error::limit("PCM input size overflow"))?;
61        let frames = n / stride;
62        let total = self
63            .frames
64            .checked_add(frames)
65            .ok_or_else(|| Error::limit("PCM frame overflow"))?;
66        self.limits.check(total, self.channels, self.rate)?;
67        let mut combined = std::mem::take(&mut self.pending);
68        combined.extend_from_slice(bytes);
69        let complete = frames * stride;
70        let result = crate::pcm::decode_pcm(
71            &combined[..complete],
72            self.format,
73            self.endian,
74            self.channels,
75            self.rate,
76            &self.limits,
77        )?;
78        self.pending = combined[complete..].to_vec();
79        self.frames = total;
80        Ok(result)
81    }
82    pub fn finish(self) -> Result<()> {
83        if self.pending.is_empty() {
84            Ok(())
85        } else {
86            Err(Error::decode("Incomplete final PCM frame"))
87        }
88    }
89}
90#[derive(Debug)]
91pub struct PcmEncoder {
92    format: SampleFormat,
93    endian: Endian,
94    shape: Option<(usize, u32)>,
95    dither: crate::Dither,
96    quantizers: Vec<crate::pcm::Quantizer>,
97}
98impl PcmEncoder {
99    pub fn new(format: SampleFormat, endian: Endian) -> Self {
100        Self {
101            format,
102            endian,
103            shape: None,
104            dither: crate::Dither::None,
105            quantizers: Vec::new(),
106        }
107    }
108    /// Encode chunks with a continuous per-channel dither stream. The same input,
109    /// format and seed give the bytes of [`crate::pcm::encode_pcm_with_dither`],
110    /// regardless of chunk sizes. Empty chunks consume no noise; shape errors
111    /// preserve the stream state. Float and G.711 reject enabled dither.
112    pub fn with_dither(
113        format: SampleFormat,
114        endian: Endian,
115        dither: crate::Dither,
116    ) -> Result<Self> {
117        crate::pcm::validate_dither(format, dither)?;
118        Ok(Self {
119            dither,
120            ..Self::new(format, endian)
121        })
122    }
123    pub fn push(&mut self, audio: &Audio) -> Result<Vec<u8>> {
124        let shape = (audio.channels(), audio.sample_rate());
125        if self.shape.is_some_and(|s| s != shape) {
126            return Err(Error::invalid("PCM stream shape changed"));
127        }
128        if self.shape.is_none() {
129            self.quantizers = crate::pcm::quantizers(self.format, self.dither, audio.channels());
130        }
131        let bytes =
132            crate::pcm::encode_quantized(audio, self.format, self.endian, &mut self.quantizers)?;
133        self.shape = Some(shape);
134        Ok(bytes)
135    }
136}
137
138/// Windowed-sinc resampling across chunk boundaries. Retains only filter history.
139/// Call `finish` to drain the final filter tail using the last sample as the edge value.
140pub struct Resampler {
141    from_rate: u32,
142    to_rate: u32,
143    channels: usize,
144    limits: Limits,
145    pending: Vec<std::collections::VecDeque<f32>>,
146    first: Vec<f32>,
147    start: usize,
148    inputs: usize,
149    outputs: usize,
150}
151impl Resampler {
152    pub fn new(from_rate: u32, to_rate: u32, channels: usize, limits: Limits) -> Result<Self> {
153        limits.check(0, channels, from_rate)?;
154        limits.check(0, channels, to_rate)?;
155        Ok(Self {
156            from_rate,
157            to_rate,
158            channels,
159            limits,
160            pending: vec![std::collections::VecDeque::new(); channels],
161            first: vec![0.0; channels],
162            start: 0,
163            inputs: 0,
164            outputs: 0,
165        })
166    }
167    pub fn push(&mut self, audio: &Audio) -> Result<Audio> {
168        if audio.sample_rate() != self.from_rate || audio.channels() != self.channels {
169            return Err(Error::invalid("Resampler input shape changed"));
170        }
171        let total = self
172            .inputs
173            .checked_add(audio.frames())
174            .ok_or_else(|| Error::limit("Resampler frame count overflow"))?;
175        self.limits.check(total, self.channels, self.from_rate)?;
176        self.limits.check(
177            (total as f64 * self.to_rate as f64 / self.from_rate as f64).round() as usize,
178            self.channels,
179            self.to_rate,
180        )?;
181        for (c, data) in self.pending.iter_mut().enumerate() {
182            if self.inputs == 0 && audio.frames() > 0 {
183                self.first[c] = audio.data[c][0];
184            }
185            data.extend(audio.data[c].iter().copied());
186        }
187        self.inputs = total;
188        self.drain(false)
189    }
190    fn drain(&mut self, finish: bool) -> Result<Audio> {
191        let ratio = self.to_rate as f64 / self.from_rate as f64;
192        let half = 32.0 / (ratio.min(1.0) * 0.97);
193        let target = (self.inputs as f64 * ratio).round() as usize;
194        let mut data = vec![Vec::new(); self.channels];
195        while self.outputs < target {
196            let center = self.outputs as f64 / ratio;
197            if !finish && center + half >= self.inputs as f64 {
198                break;
199            }
200            for (c, dst) in data.iter_mut().enumerate() {
201                let value = if self.from_rate == self.to_rate {
202                    self.pending[c][self.outputs - self.start]
203                } else {
204                    crate::dsp::sinc_sample(center, ratio, |index| {
205                        if index < 0 {
206                            return self.first[c];
207                        }
208                        if index as usize >= self.inputs {
209                            return self.pending[c].back().copied().unwrap_or(0.0);
210                        }
211                        self.pending[c][index as usize - self.start]
212                    })
213                };
214                dst.push(value);
215            }
216            self.outputs += 1;
217        }
218        let keep = ((self.outputs as f64 / ratio - half).ceil().max(0.0) as usize)
219            .min(self.inputs.saturating_sub(1));
220        if keep > self.start {
221            for c in &mut self.pending {
222                c.drain(..keep - self.start);
223            }
224            self.start = keep;
225        }
226        Audio::decoded(data, self.to_rate, crate::DecodeInfo::default())
227    }
228    pub fn finish(mut self) -> Result<Audio> {
229        self.drain(true)
230    }
231}