Skip to main content

audiobox/
decoder.rs

1//! Incremental decoding of WAV, FLAC, MP3, Vorbis and Opus held in memory.
2use crate::flac::{FlacFrame, FlacInfo, Md5};
3use crate::formats::WavLayout;
4use crate::mp3::{self, Header, Mp3State, Vbr};
5use crate::pcm::{decode_ima_block, read_sample};
6use crate::{Audio, AudioFormat, DecodeInfo, Endian, Error, ErrorCode, Limits, Result};
7
8#[cfg(all(feature = "file-stream", not(target_family = "wasm")))]
9pub(crate) mod file;
10
11/// Options for [`Decoder`].
12#[derive(Clone, Debug)]
13pub struct DecoderOptions {
14    /// The input's format, or `None` to detect it from the bytes.
15    pub format: Option<AudioFormat>,
16    /// The limits of whole decoding, checked against the declared stream when it
17    /// opens. `max_decoded_bytes` also bounds the decoder's own buffers.
18    pub limits: Limits,
19    /// Most frames that [`Decoder::next_chunk`] returns at once. Default 4096.
20    pub chunk_frames: usize,
21}
22impl Default for DecoderOptions {
23    fn default() -> Self {
24        Self {
25            format: None,
26            limits: Limits::default(),
27            chunk_frames: 4096,
28        }
29    }
30}
31
32/// Decodes WAV, FLAC, MP3, Ogg Vorbis or Ogg Opus in memory a chunk at a time.
33///
34/// The bytes can be anything that holds them: `&'static [u8]` from
35/// `include_bytes!`, a `Vec<u8>` read from a file, or an `Arc<[u8]>` shared with
36/// other decoders. Apart from them, the decoder's memory is bounded: it holds at
37/// most one block of the input format (a FLAC block, an MP3 frame, an IMA ADPCM block
38/// or `chunk_frames` of PCM), the MP3 bit reservoir, or bounded Ogg packet, table
39/// and DSP scratch storage, all allocated when it opens.
40/// [`Decoder::read`] allocates nothing, so it can run on an audio thread.
41///
42/// The concatenated output equals what [`decode`](crate::decode) gives for the same
43/// bytes, sample for sample.
44///
45/// [`Decoder::seek`] moves to any frame. WAV seeks directly. FLAC starts from the
46/// nearest SEEKTABLE point at or before the frame, or decodes forward from the start
47/// or the current position when there is no table. MP3 restarts a few frames earlier,
48/// enough to refill the bit reservoir and the filter state, so the samples after a
49/// seek equal those of a whole decode. [`Decoder::rewind`] returns to the start at
50/// once, for looping music.
51/// Ogg validates page framing/granules at open and prepares bounded packet
52/// DSP storage. Seeking replays from the first audio packet with existing storage;
53/// it is exact but O(distance from the start). Chained/multiplexed links are rejected.
54///
55/// ```
56/// use audiobox::{Audio, AudioFormat, encode, stream::Decoder};
57/// let tone: Vec<f32> = (0..10_000).map(|i| (i as f32 * 0.05).sin() * 0.5).collect();
58/// let bytes = encode(&Audio::new(vec![tone], 44_100)?, AudioFormat::Flac)?;
59///
60/// let mut decoder = Decoder::new(bytes)?;
61/// assert_eq!((decoder.sample_rate(), decoder.channels()), (44_100, 1));
62/// let mut buffer = [0.0f32; 1024];
63/// let mut frames = 0;
64/// loop {
65///     let n = decoder.read(&mut buffer)?;
66///     if n == 0 {
67///         break;
68///     }
69///     frames += n;
70/// }
71/// assert_eq!(frames, 10_000);
72/// decoder.rewind()?;
73/// assert_eq!(decoder.position(), 0);
74/// # Ok::<(), audiobox::Error>(())
75/// ```
76pub struct Decoder<B> {
77    bytes: B,
78    format: AudioFormat,
79    channels: usize,
80    rate: u32,
81    frames: Option<u64>,
82    chunk_frames: usize,
83    position: u64,
84    block: Block,
85    inner: Inner,
86    failed: Option<(ErrorCode, String)>,
87}
88
89/// Decoded samples waiting to be read: `data[channel][start..]`.
90struct Block {
91    data: Vec<Vec<f32>>,
92    start: usize,
93}
94impl Block {
95    fn new(channels: usize, capacity: usize) -> Self {
96        Self {
97            data: (0..channels)
98                .map(|_| Vec::with_capacity(capacity))
99                .collect(),
100            start: 0,
101        }
102    }
103    fn clear(&mut self) {
104        for c in &mut self.data {
105            c.clear();
106        }
107        self.start = 0;
108    }
109    fn available(&self) -> usize {
110        self.data[0].len() - self.start
111    }
112}
113
114enum Inner {
115    Ogg(Box<crate::ogg::Stream>),
116    Wav(WavStream),
117    Flac(Box<FlacStream>),
118    Mp3(Box<Mp3Stream>),
119}
120
121impl<B: AsRef<[u8]>> Decoder<B> {
122    pub fn new(bytes: B) -> Result<Self> {
123        Self::with_options(bytes, &DecoderOptions::default())
124    }
125    pub fn with_options(bytes: B, options: &DecoderOptions) -> Result<Self> {
126        if options.chunk_frames == 0 {
127            return Err(Error::invalid("chunk_frames must be at least 1"));
128        }
129        let input = bytes.as_ref();
130        let format = options
131            .format
132            .or_else(|| crate::sniff(input))
133            .ok_or_else(|| {
134                Error::new(
135                    ErrorCode::UnsupportedFormat,
136                    "Unknown or unsupported audio format",
137                )
138            })?;
139        let limits = &options.limits;
140        let (inner, channels, rate, frames, capacity) = match format {
141            AudioFormat::Vorbis | AudioFormat::Opus => {
142                let s = crate::ogg::Stream::open(input, format, limits)?;
143                let (channels, rate, frames, capacity) = (s.channels, s.rate, s.frames, s.capacity);
144                (
145                    Inner::Ogg(Box::new(s)),
146                    channels,
147                    rate,
148                    Some(frames),
149                    capacity,
150                )
151            }
152            AudioFormat::Wav => {
153                let layout = crate::formats::parse_wav(input, limits)?;
154                let (channels, rate, frames) = (layout.channels, layout.rate, layout.frames);
155                let stream = WavStream::new(layout, options.chunk_frames);
156                let capacity = stream.block_frames();
157                (
158                    Inner::Wav(stream),
159                    channels,
160                    rate,
161                    Some(frames as u64),
162                    capacity,
163                )
164            }
165            AudioFormat::Flac => {
166                let info = crate::flac::parse_info(input, limits)?;
167                let (channels, rate) = (info.channels, info.rate);
168                let frames = (info.total != 0).then_some(info.total as u64);
169                let capacity = info.max;
170                // The frame buffers hold two i64 per sample besides the f32 output.
171                limits.check(capacity, channels * 5, rate)?;
172                let stream = FlacStream::new(info);
173                (
174                    Inner::Flac(Box::new(stream)),
175                    channels,
176                    rate,
177                    frames,
178                    capacity,
179                )
180            }
181            AudioFormat::Mp3 => {
182                let stream = Mp3Stream::open(input, limits)?;
183                let (channels, rate) = (stream.channels, stream.rate);
184                let frames = (stream.end_raw - stream.start_raw) as u64;
185                (
186                    Inner::Mp3(Box::new(stream)),
187                    channels,
188                    rate,
189                    Some(frames),
190                    1152,
191                )
192            }
193            _ => {
194                return Err(Error::new(
195                    ErrorCode::UnsupportedFormat,
196                    "Incremental decoding supports WAV, FLAC and MP3",
197                ));
198            }
199        };
200        limits.check(capacity, channels, rate)?;
201        Ok(Self {
202            bytes,
203            format,
204            channels,
205            rate,
206            frames,
207            chunk_frames: options.chunk_frames,
208            position: 0,
209            block: Block::new(channels, capacity),
210            inner,
211            failed: None,
212        })
213    }
214    pub fn format(&self) -> AudioFormat {
215        self.format
216    }
217    pub fn sample_rate(&self) -> u32 {
218        self.rate
219    }
220    pub fn channels(&self) -> usize {
221        self.channels
222    }
223    /// The stream's length in frames, when the container declares it or the decoder
224    /// can count it on opening (always for WAV and MP3, and for FLAC with a sample count).
225    pub fn frames(&self) -> Option<u64> {
226        self.frames
227    }
228    /// The index of the next frame that [`Decoder::read`] returns.
229    pub fn position(&self) -> u64 {
230        self.position
231    }
232    /// Returns the bytes the decoder was created with.
233    pub fn into_inner(self) -> B {
234        self.bytes
235    }
236    /// Fills `out` with interleaved frames and returns how many frames were written:
237    /// `out.len() / channels` unless the stream ends first, and 0 at its end. A
238    /// remainder of `out` shorter than a frame is left untouched.
239    ///
240    /// After an error, every later call returns the same error until a seek or rewind.
241    pub fn read(&mut self, out: &mut [f32]) -> Result<usize> {
242        if let Some((code, message)) = &self.failed {
243            return Err(Error::new(*code, message.clone()));
244        }
245        let wanted = out.len() / self.channels;
246        let mut written = 0;
247        while written < wanted {
248            if self.block.available() == 0 {
249                match self.refill() {
250                    Ok(true) => continue,
251                    Ok(false) => break,
252                    Err(e) => {
253                        self.position += written as u64;
254                        self.failed = Some((e.code, e.message.clone()));
255                        return Err(e);
256                    }
257                }
258            }
259            let n = self.block.available().min(wanted - written);
260            let start = self.block.start;
261            for (c, data) in self.block.data.iter().enumerate() {
262                for (i, &s) in data[start..start + n].iter().enumerate() {
263                    out[(written + i) * self.channels + c] = s;
264                }
265            }
266            self.block.start += n;
267            written += n;
268        }
269        self.position += written as u64;
270        Ok(written)
271    }
272    /// Returns up to `chunk_frames` frames, or `None` at the end of the stream.
273    pub fn next_chunk(&mut self) -> Result<Option<Audio>> {
274        let mut data: Vec<Vec<f32>> = vec![Vec::with_capacity(self.chunk_frames); self.channels];
275        let mut buffer = vec![0.0; self.chunk_frames.min(4096) * self.channels];
276        while data[0].len() < self.chunk_frames {
277            let want = (self.chunk_frames - data[0].len()).min(4096);
278            let n = self.read(&mut buffer[..want * self.channels])?;
279            if n == 0 {
280                break;
281            }
282            for (c, channel) in data.iter_mut().enumerate() {
283                channel.extend((0..n).map(|i| buffer[i * self.channels + c]));
284            }
285        }
286        if data[0].is_empty() {
287            return Ok(None);
288        }
289        let info = DecodeInfo {
290            format: Some(self.format),
291            ..Default::default()
292        };
293        Audio::decoded(data, self.rate, info).map(Some)
294    }
295    /// Moves to `frame`, so that the next read starts there. A frame past the end
296    /// moves to the end.
297    pub fn seek(&mut self, frame: u64) -> Result<()> {
298        self.failed = None;
299        let frame = self.frames.map_or(frame, |n| frame.min(n));
300        let bytes = self.bytes.as_ref();
301        let result = match &mut self.inner {
302            Inner::Ogg(s) => s.seek(bytes, frame, &mut self.block.data).map(|offset| {
303                self.block.start = offset;
304                frame
305            }),
306            Inner::Wav(s) => s.seek(bytes, frame, &mut self.block),
307            Inner::Flac(s) => s.seek(bytes, frame, self.position, &mut self.block),
308            Inner::Mp3(s) => s.seek(bytes, frame, &mut self.block),
309        };
310        match result {
311            Ok(position) => {
312                self.position = position;
313                Ok(())
314            }
315            Err(e) => {
316                self.failed = Some((e.code, e.message.clone()));
317                Err(e)
318            }
319        }
320    }
321    /// Returns to the first frame without decoding anything.
322    pub fn rewind(&mut self) -> Result<()> {
323        self.failed = None;
324        self.block.clear();
325        match &mut self.inner {
326            Inner::Ogg(s) => s.rewind(),
327            Inner::Wav(s) => s.rewind(),
328            Inner::Flac(s) => s.rewind(),
329            Inner::Mp3(s) => s.rewind(),
330        }
331        self.position = 0;
332        Ok(())
333    }
334    fn refill(&mut self) -> Result<bool> {
335        let bytes = self.bytes.as_ref();
336        self.block.clear();
337        match &mut self.inner {
338            Inner::Ogg(s) => s.refill(bytes, &mut self.block.data),
339            Inner::Wav(s) => s.refill(bytes, &mut self.block),
340            Inner::Flac(s) => s.refill(bytes, &mut self.block),
341            Inner::Mp3(s) => s.refill(bytes, &mut self.block),
342        }
343    }
344}
345
346impl<B> std::fmt::Debug for Decoder<B> {
347    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
348        f.debug_struct("Decoder")
349            .field("format", &self.format)
350            .field("channels", &self.channels)
351            .field("sample_rate", &self.rate)
352            .field("frames", &self.frames)
353            .field("position", &self.position)
354            .finish_non_exhaustive()
355    }
356}
357
358struct WavStream {
359    layout: WavLayout,
360    chunk_frames: usize,
361    /// Offset of the next undecoded byte in the concatenated payload.
362    next: usize,
363    /// Frames decoded before `next`.
364    produced: usize,
365    scratch: Vec<u8>,
366}
367impl WavStream {
368    fn new(layout: WavLayout, chunk_frames: usize) -> Self {
369        let bytes = if layout.code == 17 {
370            layout.align
371        } else {
372            chunk_frames * layout.align
373        };
374        Self {
375            layout,
376            chunk_frames,
377            next: 0,
378            produced: 0,
379            scratch: Vec::with_capacity(bytes),
380        }
381    }
382    fn block_frames(&self) -> usize {
383        if self.layout.code == 17 {
384            self.layout.samples_per_block
385        } else {
386            self.chunk_frames
387        }
388    }
389    fn rewind(&mut self) {
390        self.next = 0;
391        self.produced = 0;
392    }
393    /// Copies `len` payload bytes from offset `self.next` into `scratch`, across data chunks.
394    fn gather(&mut self, bytes: &[u8], len: usize) {
395        self.scratch.clear();
396        let mut offset = self.next;
397        for range in &self.layout.payloads {
398            let size = range.end - range.start;
399            if offset >= size {
400                offset -= size;
401                continue;
402            }
403            let take = (size - offset).min(len - self.scratch.len());
404            let from = range.start + offset;
405            self.scratch.extend_from_slice(&bytes[from..from + take]);
406            offset = 0;
407            if self.scratch.len() == len {
408                break;
409            }
410        }
411    }
412    fn refill(&mut self, bytes: &[u8], block: &mut Block) -> Result<bool> {
413        let remaining = self.layout.frames - self.produced;
414        if remaining == 0 {
415            return Ok(false);
416        }
417        let layout = &self.layout;
418        if layout.code == 17 {
419            let align = layout.align;
420            self.gather(bytes, align);
421            decode_ima_block(
422                &self.scratch,
423                self.layout.samples_per_block,
424                &mut block.data,
425            )?;
426            let n = self.layout.samples_per_block.min(remaining);
427            for c in &mut block.data {
428                c.truncate(n);
429            }
430            self.next += align;
431            self.produced += n;
432        } else {
433            let n = self.chunk_frames.min(remaining);
434            let (align, format) = (layout.align, layout.format);
435            self.gather(bytes, n * align);
436            let width = format.bytes();
437            for frame in self.scratch.chunks_exact(align) {
438                for (c, sample) in frame.chunks_exact(width).enumerate() {
439                    block.data[c].push(read_sample(sample, format, Endian::Little)?);
440                }
441            }
442            self.next += n * align;
443            self.produced += n;
444        }
445        Ok(true)
446    }
447    fn seek(&mut self, bytes: &[u8], frame: u64, block: &mut Block) -> Result<u64> {
448        block.clear();
449        let frame = frame as usize;
450        if self.layout.code == 17 {
451            let spb = self.layout.samples_per_block;
452            let index = frame / spb;
453            self.next = index * self.layout.align;
454            self.produced = index * spb;
455            if frame > self.produced {
456                self.refill(bytes, block)?;
457                block.start = frame - index * spb;
458            }
459        } else {
460            self.next = frame * self.layout.align;
461            self.produced = frame;
462        }
463        Ok(frame as u64)
464    }
465}
466
467struct FlacStream {
468    info: FlacInfo,
469    frame: FlacFrame,
470    /// Offset of the next undecoded frame.
471    pos: usize,
472    /// Frames decoded before `pos`.
473    written: usize,
474    md5: Md5,
475    /// Whether every frame since the start went through `md5`.
476    from_start: bool,
477}
478impl FlacStream {
479    fn new(info: FlacInfo) -> Self {
480        Self {
481            frame: FlacFrame::new(&info),
482            pos: info.first_frame,
483            info,
484            written: 0,
485            md5: Md5::new(),
486            from_start: true,
487        }
488    }
489    fn rewind(&mut self) {
490        self.pos = self.info.first_frame;
491        self.written = 0;
492        self.md5 = Md5::new();
493        self.from_start = true;
494    }
495    fn refill(&mut self, bytes: &[u8], block: &mut Block) -> Result<bool> {
496        let total = self.info.total;
497        if self.pos >= bytes.len() {
498            if self.from_start {
499                if total != 0 && self.written != total {
500                    return Err(Error::decode("FLAC sample count mismatch"));
501                }
502                let md5 = std::mem::replace(&mut self.md5, Md5::new());
503                if self.info.md5 != [0; 16] && md5.digest() != self.info.md5 {
504                    return Err(Error::decode("FLAC PCM MD5 mismatch"));
505                }
506                self.from_start = false;
507            }
508            return Ok(false);
509        }
510        let (n, next_pos) = self.frame.decode(bytes, self.pos, &self.info)?;
511        let next = self
512            .written
513            .checked_add(n)
514            .ok_or_else(|| Error::limit("FLAC frame count overflow"))?;
515        if total != 0 && next > total {
516            return Err(Error::decode("FLAC exceeds declared sample count"));
517        }
518        let bits = self.info.bits;
519        for (c, samples) in block.data.iter_mut().zip(&self.frame.samples) {
520            c.extend(samples[..n].iter().map(|&s| crate::flac::to_float(s, bits)));
521        }
522        if self.from_start {
523            self.md5.update(&self.frame.raw);
524        }
525        self.written = next;
526        self.pos = next_pos;
527        Ok(true)
528    }
529    /// The SEEKTABLE point with the largest sample at or before `frame`.
530    fn seek_point(&self, bytes: &[u8], frame: u64) -> Option<(usize, usize)> {
531        let table = &bytes[self.info.seek_table.clone()?];
532        let mut best = None;
533        for point in table.chunks_exact(18) {
534            let sample = u64::from_be_bytes(point[..8].try_into().unwrap());
535            let offset = u64::from_be_bytes(point[8..16].try_into().unwrap());
536            if sample == u64::MAX || sample > frame {
537                continue;
538            }
539            let pos = usize::try_from(offset)
540                .ok()
541                .and_then(|o| o.checked_add(self.info.first_frame))
542                .filter(|&p| p < bytes.len())?;
543            if best.is_none_or(|(s, _)| sample as usize > s) {
544                best = Some((sample as usize, pos));
545            }
546        }
547        best
548    }
549    fn seek(&mut self, bytes: &[u8], frame: u64, position: u64, block: &mut Block) -> Result<u64> {
550        let block_start = position - block.start as u64;
551        if frame >= block_start && frame < block_start + block.data[0].len() as u64 {
552            block.start = (frame - block_start) as usize;
553            return Ok(frame);
554        }
555        block.clear();
556        if frame < self.written as u64 || frame == 0 {
557            self.rewind();
558        }
559        if let Some((sample, pos)) = self.seek_point(bytes, frame) {
560            if sample > self.written {
561                self.pos = pos;
562                self.written = sample;
563                self.from_start = false;
564            }
565        }
566        while (self.written as u64) <= frame && frame != 0 {
567            let frame_start = self.written;
568            block.clear();
569            if !self.refill(bytes, block)? {
570                return Ok(self.written as u64);
571            }
572            if self.written as u64 > frame {
573                block.start = (frame - frame_start as u64) as usize;
574            }
575        }
576        Ok(frame)
577    }
578}
579
580/// Bytes of earlier main data that an MP3 frame can refer to (main_data_begin is 9 bits).
581const RESERVOIR: usize = 511;
582/// Recent frames remembered while looking for where to restart before a seek.
583const RECENT: usize = 64;
584
585#[derive(Clone, Copy, Default)]
586struct FrameMark {
587    pos: usize,
588    payload: usize,
589    /// Raw index of the frame's first sample in a whole decode.
590    raw: usize,
591    /// Samples the frame contributes in a whole decode.
592    counted: usize,
593    /// Reservoir length of a whole decode before the frame.
594    history: usize,
595}
596
597struct Mp3Stream {
598    channels: usize,
599    rate: u32,
600    first: usize,
601    tag: Option<Vbr>,
602    /// Raw indices of the first output sample and one past the last.
603    start_raw: usize,
604    end_raw: usize,
605    state: Mp3State,
606    buffer: Box<[[f32; 1152]; 2]>,
607    pos: usize,
608    /// Reservoir length that a whole decode would have here, to count samples as it does.
609    history: usize,
610    /// Raw index of the next decoded sample.
611    raw: usize,
612    /// Raw samples before this index are decoded only to restore state after a seek.
613    discard: usize,
614    flushed: bool,
615}
616impl Mp3Stream {
617    fn open(bytes: &[u8], limits: &Limits) -> Result<Self> {
618        let (first, h) = mp3::find(bytes, mp3::tag_size(bytes))
619            .ok_or_else(|| Error::decode("No readable MPEG Layer III frame"))?;
620        let tag = mp3::vbr(bytes, first, h)?;
621        let (channels, rate) = (h.channels, h.rate);
622        limits.check(0, channels, rate)?;
623        if let Some(tag) = tag {
624            let frames = tag
625                .frames
626                .checked_mul(if h.version == 1 { 1152 } else { 576 })
627                .ok_or_else(|| Error::limit("MP3 sample count overflow"))?;
628            limits.check(frames, channels, rate)?;
629        }
630        let mut stream = Self {
631            channels,
632            rate,
633            first,
634            tag,
635            start_raw: 0,
636            end_raw: 0,
637            state: Mp3State::new(channels, rate),
638            buffer: Box::new([[0.0; 1152]; 2]),
639            pos: first,
640            history: 0,
641            raw: 0,
642            discard: 0,
643            flushed: false,
644        };
645        let mut total = 0;
646        stream.walk_until(bytes, |mark| {
647            total = mark.raw + mark.counted;
648            true
649        })?;
650        if total == 0 {
651            return Err(Error::decode("No decodable MP3 frames"));
652        }
653        let raw_total = total + mp3::FLUSH;
654        limits.check(raw_total, channels, rate)?;
655        let tag = tag.unwrap_or_default();
656        stream.start_raw = (tag.delay + mp3::DECODER_DELAY + mp3::FLUSH).min(raw_total);
657        stream.end_raw = raw_total
658            .saturating_sub(tag.padding.saturating_sub(mp3::DECODER_DELAY))
659            .max(stream.start_raw);
660        stream.discard = stream.start_raw;
661        Ok(stream)
662    }
663    /// Calls `visit` for each frame that a whole decode reads, without decoding, and
664    /// stops at the first frame `visit` rejects. Returns that frame, if any.
665    fn walk_until(
666        &self,
667        bytes: &[u8],
668        mut visit: impl FnMut(FrameMark) -> bool,
669    ) -> Result<Option<FrameMark>> {
670        let (mut pos, mut history, mut raw) = (self.first, 0, 0);
671        while let Some(h) = mp3::header(bytes, pos) {
672            if pos + h.length > bytes.len() || h.rate != self.rate || h.channels != self.channels {
673                break;
674            }
675            let mark = self.mark(bytes, pos, h, history, raw)?;
676            if !visit(mark) {
677                return Ok(Some(mark));
678            }
679            history = (history + mark.payload).min(4096);
680            raw += mark.counted;
681            pos += h.length;
682        }
683        Ok(None)
684    }
685    fn mark(
686        &self,
687        bytes: &[u8],
688        pos: usize,
689        h: Header,
690        history: usize,
691        raw: usize,
692    ) -> Result<FrameMark> {
693        let output = !(pos == self.first && self.tag.is_some());
694        let back = mp3::main_data_begin(bytes, pos, h)?;
695        let granules = if h.version == 1 { 2 } else { 1 };
696        Ok(FrameMark {
697            pos,
698            payload: mp3::payload_len(h),
699            raw,
700            counted: if output && back <= history {
701                granules * 576
702            } else {
703                0
704            },
705            history,
706        })
707    }
708    fn rewind(&mut self) {
709        self.state.reset();
710        self.pos = self.first;
711        self.history = 0;
712        self.raw = 0;
713        self.discard = self.start_raw;
714        self.flushed = false;
715    }
716    /// Moves the samples of raw indices `raw..raw + n` that belong to the output into `block`.
717    fn emit(&self, raw: usize, n: usize, block: &mut Block) {
718        let from = raw.max(self.discard).max(self.start_raw);
719        let to = (raw + n).min(self.end_raw);
720        if from < to {
721            for (c, samples) in block.data.iter_mut().zip(self.buffer.iter()) {
722                c.extend_from_slice(&samples[from - raw..to - raw]);
723            }
724        }
725    }
726    fn refill(&mut self, bytes: &[u8], block: &mut Block) -> Result<bool> {
727        loop {
728            if self.flushed {
729                return Ok(false);
730            }
731            let Some(h) = mp3::header(bytes, self.pos) else {
732                self.state.flush(&mut self.buffer);
733                self.emit(self.raw, mp3::FLUSH, block);
734                self.raw += mp3::FLUSH;
735                self.flushed = true;
736                return Ok(block.available() > 0);
737            };
738            if self.pos + h.length > bytes.len() {
739                return Err(Error::decode("Truncated MP3 frame"));
740            }
741            if h.rate != self.rate || h.channels != self.channels {
742                return Err(Error::decode("MP3 stream shape changed"));
743            }
744            let mark = self.mark(bytes, self.pos, h, self.history, self.raw)?;
745            let output = !(self.pos == self.first && self.tag.is_some());
746            let n = self
747                .state
748                .frame(bytes, self.pos, h, output, &mut self.buffer)?;
749            self.pos += h.length;
750            self.history = (self.history + mark.payload).min(4096);
751            self.raw += mark.counted;
752            if n > 0 && mark.counted > 0 {
753                self.emit(mark.raw, n, block);
754                if block.available() > 0 {
755                    return Ok(true);
756                }
757            }
758        }
759    }
760    fn seek(&mut self, bytes: &[u8], frame: u64, block: &mut Block) -> Result<u64> {
761        block.clear();
762        let target = (self.start_raw + frame as usize).min(self.end_raw);
763        // Find the frame holding `target` while remembering the frames before it.
764        let mut recent = [FrameMark::default(); RECENT];
765        let mut seen = 0usize;
766        self.walk_until(bytes, |mark| {
767            if mark.counted > 0 && mark.raw + mark.counted > target {
768                return false;
769            }
770            recent[seen % RECENT] = mark;
771            seen += 1;
772            true
773        })?;
774        // Restart at the last decoded frame before the target, and early enough before
775        // it that the reservoir holds everything that frame and later ones refer to.
776        let mut restart = None;
777        let mut previous_decoded = None;
778        let mut payload = 0;
779        for back in 0..seen.min(RECENT) {
780            let mark = recent[(seen - 1 - back) % RECENT];
781            if previous_decoded.is_none() {
782                if mark.counted > 0 {
783                    previous_decoded = Some(back);
784                }
785                continue;
786            }
787            payload += mark.payload;
788            if payload >= RESERVOIR {
789                restart = Some(mark);
790                break;
791            }
792        }
793        // Without a decoded frame before the target, or without enough remembered
794        // frames to refill the reservoir, decoding restarts from the first frame.
795        self.rewind();
796        if let (Some(_), Some(mark)) = (previous_decoded, restart) {
797            self.pos = mark.pos;
798            self.history = mark.history;
799            self.raw = mark.raw;
800        }
801        self.discard = target.max(self.start_raw);
802        Ok((target - self.start_raw) as u64)
803    }
804}
805
806#[cfg(test)]
807mod tests {
808    use super::*;
809
810    #[test]
811    fn mp3_seek_restarts_shortly_before_the_target() {
812        // Keep library unit tests runnable from the published crate: external
813        // reservoir/interop controls live in the repository integration suite.
814        let audio = Audio::new(
815            (0..2)
816                .map(|ch| {
817                    (0..30_000)
818                        .map(|i| ((i * (ch + 1)) as f32 * 0.02).sin() * 0.3)
819                        .collect()
820                })
821                .collect(),
822            44_100,
823        )
824        .unwrap();
825        let bytes = crate::encode(&audio, AudioFormat::Mp3).unwrap();
826        let mut decoder = Decoder::new(bytes.as_slice()).unwrap();
827        let total = decoder.frames().unwrap();
828        decoder.seek(total * 3 / 4).unwrap();
829        let Inner::Mp3(stream) = &decoder.inner else {
830            unreachable!()
831        };
832        let target = stream.start_raw + (total * 3 / 4) as usize;
833        // It restarts after the first frame, and early enough to cover the reservoir.
834        assert!(stream.pos > stream.first, "restarted from the first frame");
835        assert!(
836            stream.raw < target && target - stream.raw < 20 * 1152,
837            "{} vs {target}",
838            stream.raw
839        );
840        assert!(stream.history > 0);
841    }
842
843    #[test]
844    fn flac_seek_without_table_continues_forward() {
845        let tone: Vec<f32> = (0..40_000).map(|i| (i as f32 * 0.02).sin() * 0.3).collect();
846        let bytes =
847            crate::encode(&Audio::new(vec![tone], 8000).unwrap(), AudioFormat::Flac).unwrap();
848        let mut decoder = Decoder::new(bytes.as_slice()).unwrap();
849        decoder.seek(20_000).unwrap();
850        let Inner::Flac(stream) = &decoder.inner else {
851            unreachable!()
852        };
853        assert!(stream.from_start, "decoding forward keeps the MD5 check");
854        let written = stream.written;
855        decoder.seek(30_000).unwrap();
856        let Inner::Flac(stream) = &decoder.inner else {
857            unreachable!()
858        };
859        assert!(stream.written > written && stream.written <= 30_000 + 4096);
860    }
861}