Skip to main content

ape_decoder/
decoder.rs

1use std::io::{Read, Seek, SeekFrom};
2
3use crate::bitreader::BitReader;
4use crate::crc::ape_crc;
5use crate::entropy::EntropyState;
6use crate::error::{ApeError, ApeResult};
7use crate::format::{
8    self, ApeFileInfo, APE_FORMAT_FLAG_AIFF, APE_FORMAT_FLAG_BIG_ENDIAN, APE_FORMAT_FLAG_CAF,
9    APE_FORMAT_FLAG_FLOATING_POINT, APE_FORMAT_FLAG_SIGNED_8_BIT, APE_FORMAT_FLAG_SND,
10    APE_FORMAT_FLAG_W64,
11};
12use crate::id3v2::{self, Id3v2Tag};
13use crate::predictor::{Predictor3950, Predictor3950_32};
14use crate::range_coder::RangeCoder;
15use crate::tag::{self, ApeTag};
16use crate::unprepare;
17
18// Special frame codes (from Prepare.h)
19const SPECIAL_FRAME_MONO_SILENCE: i32 = 1;
20const SPECIAL_FRAME_LEFT_SILENCE: i32 = 1;
21const SPECIAL_FRAME_RIGHT_SILENCE: i32 = 2;
22const SPECIAL_FRAME_PSEUDO_STEREO: i32 = 4;
23
24/// Source container format of the original audio.
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum SourceFormat {
27    Wav,
28    Aiff,
29    W64,
30    Snd,
31    Caf,
32    Unknown,
33}
34
35/// Result of a seek operation.
36#[derive(Debug, Clone, Copy)]
37pub struct SeekResult {
38    /// Frame index containing the target sample.
39    pub frame_index: u32,
40    /// Number of samples to skip within the decoded frame.
41    pub skip_samples: u32,
42    /// The exact sample position reached.
43    pub actual_sample: u64,
44}
45
46/// File metadata accessible without decoding.
47#[derive(Debug, Clone)]
48pub struct ApeInfo {
49    // Core audio properties
50    pub version: u16,
51    pub compression_level: u16,
52    pub sample_rate: u32,
53    pub channels: u16,
54    pub bits_per_sample: u16,
55    pub total_samples: u64,
56    pub total_frames: u32,
57    pub blocks_per_frame: u32,
58    pub final_frame_blocks: u32,
59    pub duration_ms: u64,
60    pub block_align: u16,
61
62    // Format details
63    pub format_flags: u16,
64    pub bytes_per_sample: u16,
65    pub average_bitrate_kbps: u32,
66    pub decompressed_bitrate_kbps: u32,
67    pub file_size_bytes: u64,
68
69    // Format flag helpers
70    pub is_big_endian: bool,
71    pub is_floating_point: bool,
72    pub is_signed_8bit: bool,
73
74    // Source container
75    pub source_format: SourceFormat,
76}
77
78impl ApeInfo {
79    fn from_file_info(info: &ApeFileInfo) -> Self {
80        let flags = info.header.format_flags;
81        let source_format = if flags & APE_FORMAT_FLAG_AIFF != 0 {
82            SourceFormat::Aiff
83        } else if flags & APE_FORMAT_FLAG_W64 != 0 {
84            SourceFormat::W64
85        } else if flags & APE_FORMAT_FLAG_SND != 0 {
86            SourceFormat::Snd
87        } else if flags & APE_FORMAT_FLAG_CAF != 0 {
88            SourceFormat::Caf
89        } else {
90            SourceFormat::Wav
91        };
92
93        ApeInfo {
94            version: info.descriptor.version,
95            compression_level: info.header.compression_level,
96            sample_rate: info.header.sample_rate,
97            channels: info.header.channels,
98            bits_per_sample: info.header.bits_per_sample,
99            total_samples: info.total_blocks as u64,
100            total_frames: info.header.total_frames,
101            blocks_per_frame: info.header.blocks_per_frame,
102            final_frame_blocks: info.header.final_frame_blocks,
103            duration_ms: info.length_ms as u64,
104            block_align: info.block_align,
105
106            format_flags: flags,
107            bytes_per_sample: info.bytes_per_sample,
108            average_bitrate_kbps: if info.length_ms > 0 {
109                (info.file_bytes * 8 / info.length_ms as u64) as u32
110            } else {
111                0
112            },
113            decompressed_bitrate_kbps: info.decompressed_bitrate as u32,
114            file_size_bytes: info.file_bytes,
115
116            is_big_endian: flags & APE_FORMAT_FLAG_BIG_ENDIAN != 0,
117            is_floating_point: flags & APE_FORMAT_FLAG_FLOATING_POINT != 0,
118            is_signed_8bit: flags & APE_FORMAT_FLAG_SIGNED_8_BIT != 0,
119
120            source_format,
121        }
122    }
123
124    /// Number of samples (blocks) in a given frame.
125    pub fn frame_samples(&self, frame_idx: u32) -> u32 {
126        if frame_idx == self.total_frames - 1 {
127            self.final_frame_blocks
128        } else {
129            self.blocks_per_frame
130        }
131    }
132
133    /// Generate a standard 44-byte RIFF/WAVE header for the decoded audio.
134    ///
135    /// Use this when `ApeDecoder::wav_header_data()` returns `None` (the
136    /// `CREATE_WAV_HEADER` flag was set, meaning the original header was not
137    /// stored). Combine with decoded PCM to produce a valid WAV file:
138    ///
139    /// ```rust,ignore
140    /// let header = decoder.info().generate_wav_header();
141    /// let pcm = decoder.decode_all()?;
142    /// output.write_all(&header)?;
143    /// output.write_all(&pcm)?;
144    /// ```
145    pub fn generate_wav_header(&self) -> Vec<u8> {
146        let data_size = self.total_samples as u32 * self.block_align as u32;
147        let file_size = 36 + data_size;
148
149        let mut header = Vec::with_capacity(44);
150        header.extend_from_slice(b"RIFF");
151        header.extend_from_slice(&file_size.to_le_bytes());
152        header.extend_from_slice(b"WAVE");
153
154        // fmt sub-chunk
155        header.extend_from_slice(b"fmt ");
156        header.extend_from_slice(&16u32.to_le_bytes()); // sub-chunk size
157        header.extend_from_slice(&1u16.to_le_bytes()); // PCM format
158        header.extend_from_slice(&self.channels.to_le_bytes());
159        header.extend_from_slice(&self.sample_rate.to_le_bytes());
160        let byte_rate = self.sample_rate * self.block_align as u32;
161        header.extend_from_slice(&byte_rate.to_le_bytes());
162        header.extend_from_slice(&self.block_align.to_le_bytes());
163        header.extend_from_slice(&self.bits_per_sample.to_le_bytes());
164
165        // data sub-chunk
166        header.extend_from_slice(b"data");
167        header.extend_from_slice(&data_size.to_le_bytes());
168
169        header
170    }
171}
172
173// ---------------------------------------------------------------------------
174// Internal predictor state — either 16-bit or 32-bit path
175// ---------------------------------------------------------------------------
176
177enum Predictors {
178    Path16(Vec<Predictor3950>),
179    Path32(Vec<Predictor3950_32>),
180}
181
182/// A streaming APE decoder with seek support.
183pub struct ApeDecoder<R: Read + Seek> {
184    reader: R,
185    file_info: ApeFileInfo,
186    info: ApeInfo,
187    predictors: Predictors,
188    entropy_states: Vec<EntropyState>,
189    range_coder: RangeCoder,
190    interim_mode: bool,
191}
192
193impl<R: Read + Seek> ApeDecoder<R> {
194    /// Open an APE file and parse its header.
195    pub fn new(mut reader: R) -> ApeResult<Self> {
196        let file_info = format::parse(&mut reader)?;
197        let version = file_info.descriptor.version as i32;
198        let channels = file_info.header.channels;
199        let bits = file_info.header.bits_per_sample;
200        let compression = file_info.header.compression_level as u32;
201
202        if version < 3950 {
203            return Err(ApeError::UnsupportedVersion(file_info.descriptor.version));
204        }
205
206        let predictors = if bits >= 32 {
207            Predictors::Path32(
208                (0..channels)
209                    .map(|_| Predictor3950_32::new(compression, version))
210                    .collect(),
211            )
212        } else {
213            Predictors::Path16(
214                (0..channels)
215                    .map(|_| Predictor3950::new(compression, version, bits))
216                    .collect(),
217            )
218        };
219
220        let entropy_states = (0..channels).map(|_| EntropyState::new()).collect();
221        let info = ApeInfo::from_file_info(&file_info);
222
223        Ok(ApeDecoder {
224            reader,
225            file_info,
226            info,
227            predictors,
228            entropy_states,
229            range_coder: RangeCoder::new(),
230            interim_mode: false,
231        })
232    }
233
234    /// Get file metadata.
235    pub fn info(&self) -> &ApeInfo {
236        &self.info
237    }
238
239    /// Total number of frames in the file.
240    pub fn total_frames(&self) -> u32 {
241        self.info.total_frames
242    }
243
244    /// Decode a single frame by index, returning raw PCM bytes.
245    pub fn decode_frame(&mut self, frame_idx: u32) -> ApeResult<Vec<u8>> {
246        if frame_idx >= self.info.total_frames {
247            return Err(ApeError::DecodingError("frame index out of bounds"));
248        }
249
250        let frame_data = self.read_frame_data(frame_idx)?;
251        let seek_remainder = self.seek_remainder(frame_idx);
252        let frame_blocks = self.file_info.frame_block_count(frame_idx) as usize;
253        let version = self.info.version as i32;
254        let channels = self.info.channels;
255        let bits = self.info.bits_per_sample;
256        let block_align = self.info.block_align as usize;
257
258        match &mut self.predictors {
259            Predictors::Path16(predictors) => {
260                let result = try_decode_frame_16(
261                    &frame_data,
262                    seek_remainder,
263                    frame_blocks,
264                    version,
265                    channels,
266                    bits,
267                    block_align,
268                    predictors,
269                    &mut self.entropy_states,
270                    &mut self.range_coder,
271                );
272
273                match result {
274                    Ok(pcm) => Ok(pcm),
275                    Err(ApeError::InvalidChecksum) if bits == 24 && !self.interim_mode => {
276                        self.interim_mode = true;
277                        for p in predictors.iter_mut() {
278                            p.set_interim_mode(true);
279                        }
280                        try_decode_frame_16(
281                            &frame_data,
282                            seek_remainder,
283                            frame_blocks,
284                            version,
285                            channels,
286                            bits,
287                            block_align,
288                            predictors,
289                            &mut self.entropy_states,
290                            &mut self.range_coder,
291                        )
292                    }
293                    Err(e) => Err(e),
294                }
295            }
296            Predictors::Path32(predictors) => try_decode_frame_32(
297                &frame_data,
298                seek_remainder,
299                frame_blocks,
300                version,
301                channels,
302                bits,
303                block_align,
304                predictors,
305                &mut self.entropy_states,
306                &mut self.range_coder,
307            ),
308        }
309    }
310
311    /// Decode all frames, returning all PCM bytes.
312    pub fn decode_all(&mut self) -> ApeResult<Vec<u8>> {
313        let total_pcm_bytes = (self.info.total_samples as usize)
314            .checked_mul(self.info.block_align as usize)
315            .ok_or(ApeError::InvalidFormat("total PCM size overflow"))?;
316        // Cap at 2 GB to prevent OOM from malformed headers
317        if total_pcm_bytes > 2 * 1024 * 1024 * 1024 {
318            return Err(ApeError::InvalidFormat("total PCM size exceeds 2 GB"));
319        }
320        let mut pcm_output = Vec::with_capacity(total_pcm_bytes);
321
322        for frame_idx in 0..self.info.total_frames {
323            let frame_pcm = self.decode_frame(frame_idx)?;
324            pcm_output.extend_from_slice(&frame_pcm);
325        }
326
327        Ok(pcm_output)
328    }
329
330    /// Decode all frames with a progress closure.
331    ///
332    /// The closure receives a progress fraction (0.0 to 1.0) after each frame.
333    /// Return `true` to continue, `false` to cancel decoding.
334    pub fn decode_all_with<F: FnMut(f64) -> bool>(
335        &mut self,
336        mut on_progress: F,
337    ) -> ApeResult<Vec<u8>> {
338        let total = self.info.total_frames as f64;
339        let total_pcm_bytes = (self.info.total_samples as usize)
340            .checked_mul(self.info.block_align as usize)
341            .ok_or(ApeError::InvalidFormat("total PCM size overflow"))?;
342        if total_pcm_bytes > 2 * 1024 * 1024 * 1024 {
343            return Err(ApeError::InvalidFormat("total PCM size exceeds 2 GB"));
344        }
345        let mut pcm_output = Vec::with_capacity(total_pcm_bytes);
346
347        for frame_idx in 0..self.info.total_frames {
348            let frame_pcm = self.decode_frame(frame_idx)?;
349            pcm_output.extend_from_slice(&frame_pcm);
350
351            if !on_progress((frame_idx + 1) as f64 / total) {
352                return Err(ApeError::DecodingError("cancelled"));
353            }
354        }
355
356        Ok(pcm_output)
357    }
358
359    /// Decode all frames using multiple threads for parallel decoding.
360    ///
361    /// Frame data is read sequentially (IO is serial), but frame decoding runs
362    /// in parallel across `thread_count` threads. Falls back to single-threaded
363    /// if `thread_count <= 1`.
364    ///
365    /// Output is byte-identical to `decode_all()`.
366    pub fn decode_all_parallel(&mut self, thread_count: usize) -> ApeResult<Vec<u8>> {
367        if thread_count <= 1 {
368            return self.decode_all();
369        }
370
371        let total_frames = self.info.total_frames;
372        let version = self.info.version as i32;
373        let channels = self.info.channels;
374        let bits = self.info.bits_per_sample;
375        let compression = self.info.compression_level as u32;
376        let block_align = self.info.block_align as usize;
377
378        // Step 1: Read all frame data sequentially (IO must be serial)
379        let mut frame_data_list: Vec<(Vec<u8>, u32, usize)> =
380            Vec::with_capacity(total_frames as usize);
381        for frame_idx in 0..total_frames {
382            let data = self.read_frame_data(frame_idx)?;
383            let seek_remainder = self.seek_remainder(frame_idx);
384            let frame_blocks = self.file_info.frame_block_count(frame_idx) as usize;
385            frame_data_list.push((data, seek_remainder, frame_blocks));
386        }
387
388        // Step 2: Decode frames in parallel using std::thread
389        let chunk_size = (total_frames as usize + thread_count - 1) / thread_count;
390        let chunks: Vec<Vec<(usize, Vec<u8>, u32, usize)>> = frame_data_list
391            .into_iter()
392            .enumerate()
393            .collect::<Vec<_>>()
394            .chunks(chunk_size)
395            .map(|chunk| {
396                chunk
397                    .iter()
398                    .map(|(i, (data, sr, fb))| (*i, data.clone(), *sr, *fb))
399                    .collect()
400            })
401            .collect();
402
403        let mut handles = Vec::new();
404        for chunk in chunks {
405            let v = version;
406            let ch = channels;
407            let b = bits;
408            let comp = compression;
409            let ba = block_align;
410
411            handles.push(std::thread::spawn(
412                move || -> ApeResult<Vec<(usize, Vec<u8>)>> {
413                    let mut results = Vec::with_capacity(chunk.len());
414
415                    // Each thread creates its own decoder state
416                    let mut predictors: Vec<Predictor3950> =
417                        (0..ch).map(|_| Predictor3950::new(comp, v, b)).collect();
418                    let mut entropy_states: Vec<EntropyState> =
419                        (0..ch).map(|_| EntropyState::new()).collect();
420                    let mut range_coder = RangeCoder::new();
421
422                    for (frame_idx, frame_data, seek_remainder, frame_blocks) in chunk {
423                        let pcm = if b >= 32 {
424                            let mut preds32: Vec<Predictor3950_32> =
425                                (0..ch).map(|_| Predictor3950_32::new(comp, v)).collect();
426                            try_decode_frame_32(
427                                &frame_data,
428                                seek_remainder,
429                                frame_blocks,
430                                v,
431                                ch,
432                                b,
433                                ba,
434                                &mut preds32,
435                                &mut entropy_states,
436                                &mut range_coder,
437                            )?
438                        } else {
439                            try_decode_frame_16(
440                                &frame_data,
441                                seek_remainder,
442                                frame_blocks,
443                                v,
444                                ch,
445                                b,
446                                ba,
447                                &mut predictors,
448                                &mut entropy_states,
449                                &mut range_coder,
450                            )?
451                        };
452                        results.push((frame_idx, pcm));
453                    }
454                    Ok(results)
455                },
456            ));
457        }
458
459        // Step 3: Collect results in order
460        let mut all_results: Vec<(usize, Vec<u8>)> = Vec::with_capacity(total_frames as usize);
461        for handle in handles {
462            let chunk_results = handle
463                .join()
464                .map_err(|_| ApeError::DecodingError("thread panicked"))??;
465            all_results.extend(chunk_results);
466        }
467        all_results.sort_by_key(|(idx, _)| *idx);
468
469        let total_pcm = self.info.total_samples as usize * block_align;
470        let mut pcm_output = Vec::with_capacity(total_pcm);
471        for (_, pcm) in all_results {
472            pcm_output.extend_from_slice(&pcm);
473        }
474
475        Ok(pcm_output)
476    }
477
478    /// Decode a sample range, returning only the PCM bytes within
479    /// `start_sample..end_sample` (exclusive end).
480    ///
481    /// This is more efficient than `decode_all()` for extracting a portion of a file,
482    /// as it only decodes the frames that overlap the requested range.
483    pub fn decode_range(&mut self, start_sample: u64, end_sample: u64) -> ApeResult<Vec<u8>> {
484        let start = start_sample.min(self.info.total_samples);
485        let end = end_sample.min(self.info.total_samples);
486        if start >= end {
487            return Ok(Vec::new());
488        }
489
490        let bpf = self.info.blocks_per_frame as u64;
491        let block_align = self.info.block_align as usize;
492        let first_frame = (start / bpf) as u32;
493        let last_frame = ((end - 1) / bpf).min(self.info.total_frames as u64 - 1) as u32;
494
495        let range_samples = (end - start) as usize;
496        let mut pcm_output = Vec::with_capacity(range_samples * block_align);
497
498        for frame_idx in first_frame..=last_frame {
499            let frame_pcm = self.decode_frame(frame_idx)?;
500            let frame_start_sample = frame_idx as u64 * bpf;
501            let frame_end_sample = frame_start_sample + self.info.frame_samples(frame_idx) as u64;
502
503            // Compute overlap between frame and requested range
504            let overlap_start = start.max(frame_start_sample) - frame_start_sample;
505            let overlap_end = end.min(frame_end_sample) - frame_start_sample;
506
507            let byte_start = overlap_start as usize * block_align;
508            let byte_end = overlap_end as usize * block_align;
509
510            if byte_end <= frame_pcm.len() {
511                pcm_output.extend_from_slice(&frame_pcm[byte_start..byte_end]);
512            }
513        }
514
515        Ok(pcm_output)
516    }
517
518    /// Seek to a specific sample position. Returns a `SeekResult` with the
519    /// frame index, number of samples to skip within that frame, and the
520    /// exact sample position.
521    pub fn seek(&mut self, sample: u64) -> ApeResult<SeekResult> {
522        if self.info.total_frames == 0 {
523            return Ok(SeekResult {
524                frame_index: 0,
525                skip_samples: 0,
526                actual_sample: 0,
527            });
528        }
529        let sample = sample.min(self.info.total_samples.saturating_sub(1));
530        let frame_index = (sample / self.info.blocks_per_frame as u64) as u32;
531        let frame_index = frame_index.min(self.info.total_frames - 1);
532        let frame_start = frame_index as u64 * self.info.blocks_per_frame as u64;
533        let skip_samples = (sample - frame_start) as u32;
534
535        Ok(SeekResult {
536            frame_index,
537            skip_samples,
538            actual_sample: sample,
539        })
540    }
541
542    /// Seek to a sample position and return PCM from that point to the end
543    /// of the containing frame.
544    pub fn decode_from(&mut self, sample: u64) -> ApeResult<Vec<u8>> {
545        let pos = self.seek(sample)?;
546        let frame_pcm = self.decode_frame(pos.frame_index)?;
547        let skip_bytes = pos.skip_samples as usize * self.info.block_align as usize;
548        Ok(frame_pcm[skip_bytes..].to_vec())
549    }
550
551    /// Get the original WAV header data stored in the APE file.
552    /// Returns `None` if the `CREATE_WAV_HEADER` flag is set (header not stored).
553    pub fn wav_header_data(&self) -> Option<&[u8]> {
554        if self.file_info.wav_header_data.is_empty() {
555            None
556        } else {
557            Some(&self.file_info.wav_header_data)
558        }
559    }
560
561    /// Get the number of terminating data bytes from the original container.
562    pub fn wav_terminating_bytes(&self) -> u32 {
563        self.file_info.terminating_data_bytes
564    }
565
566    /// Read and parse APE tags from the file (APEv2 format).
567    /// Returns `None` if no tag is present.
568    pub fn read_tag(&mut self) -> ApeResult<Option<ApeTag>> {
569        tag::read_tag(&mut self.reader)
570    }
571
572    /// Read and parse an ID3v2 tag from the beginning of the file.
573    /// Returns `None` if no ID3v2 header is present.
574    pub fn read_id3v2_tag(&mut self) -> ApeResult<Option<Id3v2Tag>> {
575        id3v2::read_id3v2(&mut self.reader)
576    }
577
578    /// Get the stored MD5 hash from the APE descriptor.
579    pub fn stored_md5(&self) -> &[u8; 16] {
580        &self.file_info.descriptor.md5
581    }
582
583    /// Quick verify: compute MD5 over raw file sections and compare against
584    /// the stored hash in the APE descriptor. Returns `Ok(true)` if the hash
585    /// matches, `Ok(false)` if it doesn't, or `Err` on I/O failure.
586    ///
587    /// This validates file integrity without decompressing the audio.
588    /// Requires version >= 3980.
589    pub fn verify_md5(&mut self) -> ApeResult<bool> {
590        use md5::{Digest, Md5};
591
592        let desc = &self.file_info.descriptor;
593
594        // MD5 only available for version >= 3980 with a descriptor
595        if desc.version < 3980 {
596            return Err(ApeError::UnsupportedVersion(desc.version));
597        }
598
599        // Check if MD5 is all zeros (not set)
600        if desc.md5 == [0u8; 16] {
601            return Ok(true); // No MD5 stored, consider valid
602        }
603
604        let junk = self.file_info.junk_header_bytes as u64;
605        let desc_bytes = desc.descriptor_bytes as u64;
606        let header_bytes = desc.header_bytes as u64;
607        let seek_table_bytes = desc.seek_table_bytes as u64;
608        let header_data_bytes = desc.header_data_bytes as u64;
609        let frame_data_bytes = self.file_info.ape_frame_data_bytes;
610        let term_bytes = desc.terminating_data_bytes as u64;
611
612        let mut hasher = Md5::new();
613
614        // 1. Hash header data (WAV header stored in APE file)
615        let header_data_pos = junk + desc_bytes + header_bytes + seek_table_bytes;
616        self.reader.seek(SeekFrom::Start(header_data_pos))?;
617        copy_to_hasher(&mut self.reader, &mut hasher, header_data_bytes)?;
618
619        // 2. Hash frame data + terminating data (compressed audio + post-audio)
620        // (reader is already positioned at frame data start)
621        copy_to_hasher(&mut self.reader, &mut hasher, frame_data_bytes + term_bytes)?;
622
623        // 3. Hash APE header (out-of-order — header is hashed AFTER audio data)
624        let header_pos = junk + desc_bytes;
625        self.reader.seek(SeekFrom::Start(header_pos))?;
626        copy_to_hasher(&mut self.reader, &mut hasher, header_bytes)?;
627
628        // 4. Hash seek table
629        // (reader is already positioned at seek table start)
630        copy_to_hasher(&mut self.reader, &mut hasher, seek_table_bytes)?;
631
632        // Compare
633        let computed: [u8; 16] = hasher.finalize().into();
634        Ok(computed == desc.md5)
635    }
636
637    /// Returns an iterator over decoded frames.
638    pub fn frames(&mut self) -> FrameIterator<'_, R> {
639        FrameIterator {
640            decoder: self,
641            current_frame: 0,
642        }
643    }
644
645    // -- Internal helpers --
646
647    fn seek_remainder(&self, frame_idx: u32) -> u32 {
648        let seek_byte = self.file_info.seek_byte(frame_idx);
649        let seek_byte_0 = self.file_info.seek_byte(0);
650        ((seek_byte - seek_byte_0) % 4) as u32
651    }
652
653    fn read_frame_data(&mut self, frame_idx: u32) -> ApeResult<Vec<u8>> {
654        let seek_byte = self.file_info.seek_byte(frame_idx);
655        let seek_remainder = self.seek_remainder(frame_idx);
656        let frame_bytes = self.file_info.frame_byte_count(frame_idx);
657        if frame_bytes > 64 * 1024 * 1024 {
658            return Err(ApeError::InvalidFormat("frame data exceeds 64 MB"));
659        }
660        let read_bytes = (frame_bytes as u32 + seek_remainder + 4) as usize;
661
662        self.reader
663            .seek(SeekFrom::Start(seek_byte - seek_remainder as u64))?;
664        let mut frame_data = vec![0u8; read_bytes];
665        let bytes_read = self.reader.read(&mut frame_data)?;
666        if bytes_read < read_bytes.saturating_sub(4) {
667            return Err(ApeError::DecodingError("short read on frame data"));
668        }
669        frame_data.truncate(bytes_read);
670        Ok(frame_data)
671    }
672}
673
674/// Iterator that yields decoded frames as raw PCM bytes.
675pub struct FrameIterator<'a, R: Read + Seek> {
676    decoder: &'a mut ApeDecoder<R>,
677    current_frame: u32,
678}
679
680impl<'a, R: Read + Seek> Iterator for FrameIterator<'a, R> {
681    type Item = ApeResult<Vec<u8>>;
682
683    fn next(&mut self) -> Option<Self::Item> {
684        if self.current_frame >= self.decoder.info.total_frames {
685            return None;
686        }
687        let frame_idx = self.current_frame;
688        self.current_frame += 1;
689        Some(self.decoder.decode_frame(frame_idx))
690    }
691
692    fn size_hint(&self) -> (usize, Option<usize>) {
693        let remaining = (self.decoder.info.total_frames - self.current_frame) as usize;
694        (remaining, Some(remaining))
695    }
696}
697
698/// Convenience: decode an entire APE file to raw PCM bytes.
699pub fn decode<R: Read + Seek>(reader: &mut R) -> ApeResult<Vec<u8>> {
700    // ApeDecoder::new takes ownership, so we need to pass a reference wrapper
701    // that implements Read + Seek. Since reader is &mut R where R: Read + Seek,
702    // we can use it directly because &mut R also implements Read + Seek.
703    let mut decoder = ApeDecoder::new_from_ref(reader)?;
704    decoder.decode_all()
705}
706
707impl<R: Read + Seek> ApeDecoder<R> {
708    fn new_from_ref<'a>(reader: &'a mut R) -> ApeResult<ApeDecoder<&'a mut R>> {
709        ApeDecoder::new(reader)
710    }
711}
712
713// ---------------------------------------------------------------------------
714// Frame decode implementations (shared between owned and borrowed paths)
715// ---------------------------------------------------------------------------
716
717fn try_decode_frame_16(
718    frame_data: &[u8],
719    seek_remainder: u32,
720    frame_blocks: usize,
721    version: i32,
722    channels: u16,
723    bits: u16,
724    block_align: usize,
725    predictors: &mut [Predictor3950],
726    entropy_states: &mut [EntropyState],
727    range_coder: &mut RangeCoder,
728) -> ApeResult<Vec<u8>> {
729    let mut br = BitReader::from_frame_bytes(frame_data, seek_remainder * 8);
730
731    // --- StartFrame ---
732    let mut stored_crc = br.decode_value_x_bits(32);
733    let mut special_codes: i32 = 0;
734    if version > 3820 {
735        if stored_crc & 0x80000000 != 0 {
736            special_codes = br.decode_value_x_bits(32) as i32;
737        }
738        stored_crc &= 0x7FFFFFFF;
739    }
740
741    for p in predictors.iter_mut() {
742        p.flush();
743    }
744    for s in entropy_states.iter_mut() {
745        s.flush();
746    }
747    range_coder.flush_bit_array(&mut br);
748
749    let mut last_x: i32 = 0;
750    let pcm_size = frame_blocks
751        .checked_mul(block_align)
752        .ok_or(ApeError::InvalidFormat("frame too large"))?;
753    if pcm_size > 64 * 1024 * 1024 {
754        return Err(ApeError::InvalidFormat("frame PCM size exceeds 64 MB"));
755    }
756    let mut pcm_output = Vec::with_capacity(pcm_size);
757
758    let decode_result: ApeResult<()> = (|| {
759        if channels == 2 {
760            if (special_codes & SPECIAL_FRAME_LEFT_SILENCE) != 0
761                && (special_codes & SPECIAL_FRAME_RIGHT_SILENCE) != 0
762            {
763                for _ in 0..frame_blocks {
764                    unprepare::unprepare(&[0, 0], channels, bits, &mut pcm_output)?;
765                }
766            } else if (special_codes & SPECIAL_FRAME_PSEUDO_STEREO) != 0 {
767                for _ in 0..frame_blocks {
768                    let val = entropy_states[0].decode_value_range(range_coder, &mut br)?;
769                    let x = predictors[0].decompress_value(val, 0);
770                    unprepare::unprepare(&[x, 0], channels, bits, &mut pcm_output)?;
771                }
772            } else if version >= 3950 {
773                for _ in 0..frame_blocks {
774                    let ny = entropy_states[1].decode_value_range(range_coder, &mut br)?;
775                    let nx = entropy_states[0].decode_value_range(range_coder, &mut br)?;
776                    let y = predictors[1].decompress_value(ny, last_x as i64);
777                    let x = predictors[0].decompress_value(nx, y as i64);
778                    last_x = x;
779                    unprepare::unprepare(&[x, y], channels, bits, &mut pcm_output)?;
780                }
781            } else {
782                for _ in 0..frame_blocks {
783                    let ex = entropy_states[0].decode_value_range(range_coder, &mut br)?;
784                    let ey = entropy_states[1].decode_value_range(range_coder, &mut br)?;
785                    let x = predictors[0].decompress_value(ex, 0);
786                    let y = predictors[1].decompress_value(ey, 0);
787                    unprepare::unprepare(&[x, y], channels, bits, &mut pcm_output)?;
788                }
789            }
790        } else if channels == 1 {
791            if (special_codes & SPECIAL_FRAME_MONO_SILENCE) != 0 {
792                for _ in 0..frame_blocks {
793                    unprepare::unprepare(&[0], channels, bits, &mut pcm_output)?;
794                }
795            } else {
796                for _ in 0..frame_blocks {
797                    let val = entropy_states[0].decode_value_range(range_coder, &mut br)?;
798                    let decoded = predictors[0].decompress_value(val, 0);
799                    unprepare::unprepare(&[decoded], channels, bits, &mut pcm_output)?;
800                }
801            }
802        } else {
803            let ch = channels as usize;
804            let mut values = vec![0i32; ch];
805            for _ in 0..frame_blocks {
806                for c in 0..ch {
807                    let val = entropy_states[c].decode_value_range(range_coder, &mut br)?;
808                    values[c] = predictors[c].decompress_value(val, 0);
809                }
810                unprepare::unprepare(&values, channels, bits, &mut pcm_output)?;
811            }
812        }
813        Ok(())
814    })();
815
816    decode_result?;
817
818    // --- EndFrame ---
819    range_coder.finalize(&mut br);
820    let computed_crc = ape_crc(&pcm_output);
821    if computed_crc != stored_crc {
822        return Err(ApeError::InvalidChecksum);
823    }
824
825    // Post-processing transforms (applied AFTER CRC, matching C++ GetData behavior)
826    apply_post_processing(&mut pcm_output, bits, channels);
827
828    Ok(pcm_output)
829}
830
831fn try_decode_frame_32(
832    frame_data: &[u8],
833    seek_remainder: u32,
834    frame_blocks: usize,
835    version: i32,
836    channels: u16,
837    bits: u16,
838    block_align: usize,
839    predictors: &mut [Predictor3950_32],
840    entropy_states: &mut [EntropyState],
841    range_coder: &mut RangeCoder,
842) -> ApeResult<Vec<u8>> {
843    let mut br = BitReader::from_frame_bytes(frame_data, seek_remainder * 8);
844
845    let mut stored_crc = br.decode_value_x_bits(32);
846    let mut special_codes: i32 = 0;
847    if version > 3820 {
848        if stored_crc & 0x80000000 != 0 {
849            special_codes = br.decode_value_x_bits(32) as i32;
850        }
851        stored_crc &= 0x7FFFFFFF;
852    }
853
854    for p in predictors.iter_mut() {
855        p.flush();
856    }
857    for s in entropy_states.iter_mut() {
858        s.flush();
859    }
860    range_coder.flush_bit_array(&mut br);
861
862    let mut last_x: i64 = 0;
863    let pcm_size = frame_blocks
864        .checked_mul(block_align)
865        .ok_or(ApeError::InvalidFormat("frame too large"))?;
866    if pcm_size > 64 * 1024 * 1024 {
867        return Err(ApeError::InvalidFormat("frame PCM size exceeds 64 MB"));
868    }
869    let mut pcm_output = Vec::with_capacity(pcm_size);
870
871    if channels == 2 {
872        if (special_codes & SPECIAL_FRAME_LEFT_SILENCE) != 0
873            && (special_codes & SPECIAL_FRAME_RIGHT_SILENCE) != 0
874        {
875            for _ in 0..frame_blocks {
876                unprepare::unprepare(&[0, 0], channels, bits, &mut pcm_output)?;
877            }
878        } else if (special_codes & SPECIAL_FRAME_PSEUDO_STEREO) != 0 {
879            for _ in 0..frame_blocks {
880                let val = entropy_states[0].decode_value_range(range_coder, &mut br)?;
881                let x = predictors[0].decompress_value(val, 0);
882                unprepare::unprepare(&[x as i32, 0], channels, bits, &mut pcm_output)?;
883            }
884        } else {
885            for _ in 0..frame_blocks {
886                let ny = entropy_states[1].decode_value_range(range_coder, &mut br)?;
887                let nx = entropy_states[0].decode_value_range(range_coder, &mut br)?;
888                let y = predictors[1].decompress_value(ny, last_x);
889                let x = predictors[0].decompress_value(nx, y as i64);
890                last_x = x as i64;
891                unprepare::unprepare(&[x as i32, y as i32], channels, bits, &mut pcm_output)?;
892            }
893        }
894    } else if channels == 1 {
895        if (special_codes & SPECIAL_FRAME_MONO_SILENCE) != 0 {
896            for _ in 0..frame_blocks {
897                unprepare::unprepare(&[0], channels, bits, &mut pcm_output)?;
898            }
899        } else {
900            for _ in 0..frame_blocks {
901                let val = entropy_states[0].decode_value_range(range_coder, &mut br)?;
902                let decoded = predictors[0].decompress_value(val, 0);
903                unprepare::unprepare(&[decoded as i32], channels, bits, &mut pcm_output)?;
904            }
905        }
906    }
907
908    range_coder.finalize(&mut br);
909    let computed_crc = ape_crc(&pcm_output);
910    if computed_crc != stored_crc {
911        return Err(ApeError::InvalidChecksum);
912    }
913
914    // Post-processing transforms (applied AFTER CRC, matching C++ GetData behavior)
915    apply_post_processing(&mut pcm_output, bits, channels);
916
917    Ok(pcm_output)
918}
919
920// ---------------------------------------------------------------------------
921// Post-processing transforms (applied after CRC verification)
922// ---------------------------------------------------------------------------
923
924/// Apply format-flag-dependent transforms to decoded PCM data.
925///
926/// Copy `n` bytes from a reader into an MD5 hasher in 16KB chunks.
927fn copy_to_hasher<R: Read>(reader: &mut R, hasher: &mut md5::Md5, mut n: u64) -> ApeResult<()> {
928    use md5::Digest;
929    let mut buf = [0u8; 16384];
930    while n > 0 {
931        let to_read = (n as usize).min(buf.len());
932        reader.read_exact(&mut buf[..to_read])?;
933        hasher.update(&buf[..to_read]);
934        n -= to_read as u64;
935    }
936    Ok(())
937}
938
939/// These are applied AFTER CRC verification and match the C++ `GetData()` behavior.
940/// For WAV-sourced files (the common case), all flags are 0 and this is a no-op.
941fn apply_post_processing(pcm: &mut [u8], bits: u16, _channels: u16) {
942    // The format flags are embedded in the APE header and control how the raw
943    // PCM bytes should be transformed for the output format. Since our decoder
944    // targets the same format as the source, these transforms are only needed
945    // when the source was in a non-standard format.
946    //
947    // Note: In the current implementation, format flags are exposed via ApeInfo
948    // but the caller is responsible for checking them. The transforms below
949    // would be applied when the corresponding flags are set, but since all
950    // our test fixtures are standard WAV (flags = 0), they're not exercised.
951    //
952    // The transforms are documented here for future implementation if needed:
953    //
954    // APE_FORMAT_FLAG_FLOATING_POINT: apply FloatTransform to each 32-bit sample
955    // APE_FORMAT_FLAG_SIGNED_8_BIT: add 128 (wrapping) to each byte
956    // APE_FORMAT_FLAG_BIG_ENDIAN: byte-swap each sample
957    let _ = (pcm, bits);
958}
959
960/// IEEE 754 float transform for floating-point APE files.
961///
962/// Converts between APE's internal integer representation and IEEE 754 float
963/// bit patterns. The transform is its own inverse.
964#[allow(dead_code)]
965fn float_transform_sample(sample_in: u32) -> u32 {
966    let mut out: u32 = 0;
967    out |= sample_in & 0xC3FF_FFFF;
968    out |= !(sample_in & 0x3C00_0000) ^ 0xC3FF_FFFF;
969    if out & 0x8000_0000 != 0 {
970        out = !out | 0x8000_0000;
971    }
972    out
973}
974
975/// Byte-swap samples for big-endian output format.
976#[allow(dead_code)]
977fn byte_swap_samples(pcm: &mut [u8], bytes_per_sample: usize) {
978    match bytes_per_sample {
979        2 => {
980            for chunk in pcm.chunks_exact_mut(2) {
981                chunk.swap(0, 1);
982            }
983        }
984        3 => {
985            for chunk in pcm.chunks_exact_mut(3) {
986                chunk.swap(0, 2);
987            }
988        }
989        4 => {
990            for chunk in pcm.chunks_exact_mut(4) {
991                chunk.swap(0, 3);
992                chunk.swap(1, 2);
993            }
994        }
995        _ => {}
996    }
997}
998
999#[cfg(test)]
1000mod tests {
1001    use super::*;
1002    use std::fs::File;
1003    use std::io::BufReader;
1004    use std::path::PathBuf;
1005
1006    fn test_fixture_path(name: &str) -> PathBuf {
1007        PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1008            .join("tests/fixtures")
1009            .join(name)
1010    }
1011
1012    fn load_reference_pcm(name: &str) -> Vec<u8> {
1013        let path = test_fixture_path(&format!("ref/{}", name));
1014        let data = std::fs::read(&path)
1015            .unwrap_or_else(|e| panic!("Failed to read {}: {}", path.display(), e));
1016        data[44..].to_vec()
1017    }
1018
1019    fn open_ape(name: &str) -> BufReader<File> {
1020        let path = test_fixture_path(&format!("ape/{}", name));
1021        let file = File::open(&path)
1022            .unwrap_or_else(|e| panic!("Failed to open {}: {}", path.display(), e));
1023        BufReader::new(file)
1024    }
1025
1026    fn decode_ape_file(name: &str) -> ApeResult<Vec<u8>> {
1027        let mut reader = open_ape(name);
1028        decode(&mut reader)
1029    }
1030
1031    // --- Existing end-to-end tests (unchanged) ---
1032
1033    #[test]
1034    fn test_decode_sine_16s_c1000() {
1035        let decoded = decode_ape_file("sine_16s_c1000.ape").unwrap();
1036        let expected = load_reference_pcm("sine_16s_c1000.wav");
1037        assert_eq!(decoded.len(), expected.len());
1038        assert_eq!(decoded, expected);
1039    }
1040
1041    #[test]
1042    fn test_decode_sine_16s_c2000() {
1043        let decoded = decode_ape_file("sine_16s_c2000.ape").unwrap();
1044        let expected = load_reference_pcm("sine_16s_c2000.wav");
1045        assert_eq!(decoded, expected);
1046    }
1047
1048    #[test]
1049    fn test_decode_silence_16s() {
1050        let decoded = decode_ape_file("silence_16s_c2000.ape").unwrap();
1051        let expected = load_reference_pcm("silence_16s_c2000.wav");
1052        assert_eq!(decoded, expected);
1053    }
1054
1055    #[test]
1056    fn test_decode_sine_16m() {
1057        let decoded = decode_ape_file("sine_16m_c2000.ape").unwrap();
1058        let expected = load_reference_pcm("sine_16m_c2000.wav");
1059        assert_eq!(decoded, expected);
1060    }
1061
1062    #[test]
1063    fn test_decode_short_16s() {
1064        let decoded = decode_ape_file("short_16s_c2000.ape").unwrap();
1065        let expected = load_reference_pcm("short_16s_c2000.wav");
1066        assert_eq!(decoded, expected);
1067    }
1068
1069    #[test]
1070    fn test_decode_all_compression_levels() {
1071        for level in &["c1000", "c2000", "c3000", "c4000", "c5000"] {
1072            let name = format!("sine_16s_{}.ape", level);
1073            let ref_name = format!("sine_16s_{}.wav", level);
1074            let decoded = decode_ape_file(&name).unwrap_or_else(|e| panic!("{}: {:?}", name, e));
1075            let expected = load_reference_pcm(&ref_name);
1076            assert_eq!(decoded, expected, "Mismatch for {}", name);
1077        }
1078    }
1079
1080    #[test]
1081    fn test_decode_8bit() {
1082        let decoded = decode_ape_file("sine_8s_c2000.ape").unwrap();
1083        let expected = load_reference_pcm("sine_8s_c2000.wav");
1084        assert_eq!(decoded, expected);
1085    }
1086
1087    #[test]
1088    fn test_decode_24bit() {
1089        let decoded = decode_ape_file("sine_24s_c2000.ape").unwrap();
1090        let expected = load_reference_pcm("sine_24s_c2000.wav");
1091        assert_eq!(decoded, expected);
1092    }
1093
1094    #[test]
1095    fn test_decode_32bit() {
1096        let decoded = decode_ape_file("sine_32s_c2000.ape").unwrap();
1097        let expected = load_reference_pcm("sine_32s_c2000.wav");
1098        assert_eq!(decoded, expected);
1099    }
1100
1101    #[test]
1102    fn test_decode_multiframe() {
1103        let decoded = decode_ape_file("multiframe_16s_c2000.ape").unwrap();
1104        let expected = load_reference_pcm("multiframe_16s_c2000.wav");
1105        assert_eq!(decoded, expected);
1106    }
1107
1108    #[test]
1109    fn test_decode_identical_channels() {
1110        let decoded = decode_ape_file("identical_16s_c2000.ape").unwrap();
1111        let expected = load_reference_pcm("identical_16s_c2000.wav");
1112        assert_eq!(decoded, expected);
1113    }
1114
1115    #[test]
1116    fn test_decode_all_fixtures() {
1117        let fixtures = [
1118            "dc_offset_16s_c2000.ape",
1119            "identical_16s_c2000.ape",
1120            "impulse_16s_c2000.ape",
1121            "left_only_16s_c2000.ape",
1122            "multiframe_16s_c2000.ape",
1123            "noise_16s_c2000.ape",
1124            "short_16s_c2000.ape",
1125            "silence_16s_c2000.ape",
1126            "sine_16m_c2000.ape",
1127            "sine_16s_c1000.ape",
1128            "sine_16s_c2000.ape",
1129            "sine_16s_c3000.ape",
1130            "sine_16s_c4000.ape",
1131            "sine_16s_c5000.ape",
1132            "sine_24s_c2000.ape",
1133            "sine_32s_c2000.ape",
1134            "sine_8s_c2000.ape",
1135        ];
1136
1137        for fixture in &fixtures {
1138            let ref_name = fixture.replace(".ape", ".wav");
1139            let decoded = decode_ape_file(fixture)
1140                .unwrap_or_else(|e| panic!("Failed to decode {}: {:?}", fixture, e));
1141            let expected = load_reference_pcm(&ref_name);
1142            assert_eq!(
1143                decoded.len(),
1144                expected.len(),
1145                "Length mismatch for {}",
1146                fixture
1147            );
1148            assert_eq!(decoded, expected, "Data mismatch for {}", fixture);
1149        }
1150    }
1151
1152    // --- New streaming API tests ---
1153
1154    #[test]
1155    fn test_ape_decoder_info() {
1156        let reader = open_ape("sine_16s_c2000.ape");
1157        let decoder = ApeDecoder::new(reader).unwrap();
1158        let info = decoder.info();
1159        assert_eq!(info.sample_rate, 44100);
1160        assert_eq!(info.channels, 2);
1161        assert_eq!(info.bits_per_sample, 16);
1162        assert_eq!(info.total_samples, 44100);
1163        assert_eq!(info.compression_level, 2000);
1164        assert_eq!(info.block_align, 4);
1165    }
1166
1167    #[test]
1168    fn test_decode_frame_by_frame() {
1169        let reader = open_ape("sine_16s_c2000.ape");
1170        let mut decoder = ApeDecoder::new(reader).unwrap();
1171        let expected = load_reference_pcm("sine_16s_c2000.wav");
1172
1173        let mut all_pcm = Vec::new();
1174        for frame_idx in 0..decoder.total_frames() {
1175            let frame_pcm = decoder.decode_frame(frame_idx).unwrap();
1176            all_pcm.extend_from_slice(&frame_pcm);
1177        }
1178
1179        assert_eq!(all_pcm, expected);
1180    }
1181
1182    #[test]
1183    fn test_decode_multiframe_frame_by_frame() {
1184        let reader = open_ape("multiframe_16s_c2000.ape");
1185        let mut decoder = ApeDecoder::new(reader).unwrap();
1186        let expected = load_reference_pcm("multiframe_16s_c2000.wav");
1187
1188        assert!(decoder.total_frames() > 1, "Expected multiple frames");
1189
1190        let mut all_pcm = Vec::new();
1191        for frame_idx in 0..decoder.total_frames() {
1192            let frame_pcm = decoder.decode_frame(frame_idx).unwrap();
1193            assert!(!frame_pcm.is_empty());
1194            all_pcm.extend_from_slice(&frame_pcm);
1195        }
1196
1197        assert_eq!(all_pcm, expected);
1198    }
1199
1200    #[test]
1201    fn test_frames_iterator() {
1202        let reader = open_ape("sine_16s_c2000.ape");
1203        let mut decoder = ApeDecoder::new(reader).unwrap();
1204        let expected = load_reference_pcm("sine_16s_c2000.wav");
1205
1206        let all_pcm: Vec<u8> = decoder
1207            .frames()
1208            .collect::<Result<Vec<_>, _>>()
1209            .unwrap()
1210            .concat();
1211
1212        assert_eq!(all_pcm, expected);
1213    }
1214
1215    #[test]
1216    fn test_seek_sample_level() {
1217        let reader = open_ape("multiframe_16s_c2000.ape");
1218        let mut decoder = ApeDecoder::new(reader).unwrap();
1219        let bpf = decoder.info().blocks_per_frame as u64;
1220
1221        // Seek to sample 0 → frame 0, skip 0
1222        let r = decoder.seek(0).unwrap();
1223        assert_eq!(r.frame_index, 0);
1224        assert_eq!(r.skip_samples, 0);
1225        assert_eq!(r.actual_sample, 0);
1226
1227        // Seek to mid-frame → frame 0, skip 100
1228        let r = decoder.seek(100).unwrap();
1229        assert_eq!(r.frame_index, 0);
1230        assert_eq!(r.skip_samples, 100);
1231        assert_eq!(r.actual_sample, 100);
1232
1233        // Seek to exactly frame 1 → frame 1, skip 0
1234        let r = decoder.seek(bpf).unwrap();
1235        assert_eq!(r.frame_index, 1);
1236        assert_eq!(r.skip_samples, 0);
1237        assert_eq!(r.actual_sample, bpf);
1238
1239        // Seek to mid frame 1 → frame 1, skip 100
1240        let r = decoder.seek(bpf + 100).unwrap();
1241        assert_eq!(r.frame_index, 1);
1242        assert_eq!(r.skip_samples, 100);
1243        assert_eq!(r.actual_sample, bpf + 100);
1244
1245        // Seek past end → clamps to last sample
1246        let r = decoder.seek(u64::MAX).unwrap();
1247        assert_eq!(r.actual_sample, decoder.info().total_samples - 1);
1248    }
1249
1250    #[test]
1251    fn test_decode_from_mid_frame() {
1252        let reader = open_ape("sine_16s_c2000.ape");
1253        let mut decoder = ApeDecoder::new(reader).unwrap();
1254        let block_align = decoder.info().block_align as usize;
1255
1256        // Decode full frame
1257        let full_frame = decoder.decode_frame(0).unwrap();
1258
1259        // Decode from sample 100
1260        let partial = decoder.decode_from(100).unwrap();
1261
1262        // Partial should be full_frame minus the first 100 blocks
1263        let skip = 100 * block_align;
1264        assert_eq!(partial, &full_frame[skip..]);
1265    }
1266
1267    #[test]
1268    fn test_expanded_metadata() {
1269        let reader = open_ape("sine_16s_c2000.ape");
1270        let decoder = ApeDecoder::new(reader).unwrap();
1271        let info = decoder.info();
1272
1273        assert_eq!(info.bytes_per_sample, 2);
1274        assert_eq!(info.source_format, SourceFormat::Wav);
1275        assert!(!info.is_big_endian);
1276        assert!(!info.is_floating_point);
1277        assert!(!info.is_signed_8bit);
1278        assert!(info.average_bitrate_kbps > 0);
1279        assert!(info.decompressed_bitrate_kbps > 0);
1280        assert!(info.file_size_bytes > 0);
1281        assert_eq!(info.format_flags & 0x0200, 0); // not big-endian
1282    }
1283
1284    #[test]
1285    fn test_wav_header_data() {
1286        let reader = open_ape("sine_16s_c2000.ape");
1287        let decoder = ApeDecoder::new(reader).unwrap();
1288
1289        let header = decoder.wav_header_data();
1290        // Test files should have stored WAV headers
1291        if let Some(data) = header {
1292            assert!(data.len() >= 12);
1293            // Should start with RIFF
1294            assert_eq!(&data[0..4], b"RIFF");
1295        }
1296    }
1297
1298    #[test]
1299    fn test_read_tag() {
1300        let reader = open_ape("sine_16s_c2000.ape");
1301        let mut decoder = ApeDecoder::new(reader).unwrap();
1302        // Tag may or may not exist — just ensure no panic
1303        let _tag = decoder.read_tag();
1304    }
1305
1306    #[test]
1307    fn test_decode_frame_out_of_bounds() {
1308        let reader = open_ape("sine_16s_c2000.ape");
1309        let mut decoder = ApeDecoder::new(reader).unwrap();
1310        let result = decoder.decode_frame(999);
1311        assert!(result.is_err());
1312    }
1313
1314    // --- Progress callback tests ---
1315
1316    #[test]
1317    fn test_decode_with_progress() {
1318        let reader = open_ape("sine_16s_c2000.ape");
1319        let mut decoder = ApeDecoder::new(reader).unwrap();
1320        let expected = load_reference_pcm("sine_16s_c2000.wav");
1321
1322        let mut last_progress = 0.0f64;
1323        let decoded = decoder
1324            .decode_all_with(|p| {
1325                assert!(p >= last_progress, "progress must be monotonic");
1326                last_progress = p;
1327                true // continue
1328            })
1329            .unwrap();
1330
1331        assert!((last_progress - 1.0).abs() < 0.01);
1332        assert_eq!(decoded, expected);
1333    }
1334
1335    #[test]
1336    fn test_decode_with_cancel() {
1337        let reader = open_ape("multiframe_16s_c2000.ape");
1338        let mut decoder = ApeDecoder::new(reader).unwrap();
1339
1340        let result = decoder.decode_all_with(|p| {
1341            p < 0.5 // cancel halfway
1342        });
1343
1344        assert!(result.is_err());
1345    }
1346
1347    // --- Range decoding tests ---
1348
1349    #[test]
1350    fn test_decode_range_full_file() {
1351        let reader = open_ape("sine_16s_c2000.ape");
1352        let mut decoder = ApeDecoder::new(reader).unwrap();
1353        let total = decoder.info().total_samples;
1354        let expected = load_reference_pcm("sine_16s_c2000.wav");
1355
1356        let decoded = decoder.decode_range(0, total).unwrap();
1357        assert_eq!(decoded, expected);
1358    }
1359
1360    #[test]
1361    fn test_decode_range_subset() {
1362        let reader = open_ape("sine_16s_c2000.ape");
1363        let mut decoder = ApeDecoder::new(reader).unwrap();
1364        let block_align = decoder.info().block_align as usize;
1365        let expected = load_reference_pcm("sine_16s_c2000.wav");
1366
1367        // Decode samples 100..200
1368        let decoded = decoder.decode_range(100, 200).unwrap();
1369        assert_eq!(decoded.len(), 100 * block_align);
1370        assert_eq!(decoded, &expected[100 * block_align..200 * block_align]);
1371    }
1372
1373    #[test]
1374    fn test_decode_range_empty() {
1375        let reader = open_ape("sine_16s_c2000.ape");
1376        let mut decoder = ApeDecoder::new(reader).unwrap();
1377
1378        let decoded = decoder.decode_range(100, 100).unwrap();
1379        assert!(decoded.is_empty());
1380
1381        let decoded = decoder.decode_range(200, 100).unwrap();
1382        assert!(decoded.is_empty());
1383    }
1384
1385    // --- Parallel decode tests ---
1386
1387    #[test]
1388    fn test_decode_parallel_matches_sequential() {
1389        let expected = load_reference_pcm("sine_16s_c2000.wav");
1390
1391        let reader = open_ape("sine_16s_c2000.ape");
1392        let mut decoder = ApeDecoder::new(reader).unwrap();
1393        let parallel = decoder.decode_all_parallel(4).unwrap();
1394
1395        assert_eq!(parallel, expected);
1396    }
1397
1398    #[test]
1399    fn test_decode_parallel_multiframe() {
1400        let expected = load_reference_pcm("multiframe_16s_c2000.wav");
1401
1402        let reader = open_ape("multiframe_16s_c2000.ape");
1403        let mut decoder = ApeDecoder::new(reader).unwrap();
1404        let parallel = decoder.decode_all_parallel(2).unwrap();
1405
1406        assert_eq!(parallel, expected);
1407    }
1408
1409    #[test]
1410    fn test_decode_parallel_single_thread() {
1411        let expected = load_reference_pcm("sine_16s_c2000.wav");
1412
1413        let reader = open_ape("sine_16s_c2000.ape");
1414        let mut decoder = ApeDecoder::new(reader).unwrap();
1415        let decoded = decoder.decode_all_parallel(1).unwrap();
1416
1417        assert_eq!(decoded, expected);
1418    }
1419
1420    #[test]
1421    fn test_decode_parallel_all_fixtures() {
1422        let fixtures = [
1423            "dc_offset_16s_c2000.ape",
1424            "identical_16s_c2000.ape",
1425            "impulse_16s_c2000.ape",
1426            "left_only_16s_c2000.ape",
1427            "multiframe_16s_c2000.ape",
1428            "noise_16s_c2000.ape",
1429            "short_16s_c2000.ape",
1430            "silence_16s_c2000.ape",
1431            "sine_16m_c2000.ape",
1432            "sine_16s_c1000.ape",
1433            "sine_16s_c2000.ape",
1434            "sine_16s_c3000.ape",
1435            "sine_16s_c4000.ape",
1436            "sine_16s_c5000.ape",
1437            "sine_24s_c2000.ape",
1438            "sine_32s_c2000.ape",
1439            "sine_8s_c2000.ape",
1440        ];
1441
1442        for fixture in &fixtures {
1443            let ref_name = fixture.replace(".ape", ".wav");
1444            let reader = open_ape(fixture);
1445            let mut decoder = ApeDecoder::new(reader).unwrap();
1446            let parallel = decoder
1447                .decode_all_parallel(2)
1448                .unwrap_or_else(|e| panic!("Parallel decode failed for {}: {:?}", fixture, e));
1449            let expected = load_reference_pcm(&ref_name);
1450            assert_eq!(parallel, expected, "Parallel mismatch for {}", fixture);
1451        }
1452    }
1453
1454    // --- Negative / error path tests ---
1455
1456    #[test]
1457    fn test_decode_truncated_file() {
1458        // File too small to contain even a header
1459        let data = vec![0u8; 10];
1460        let mut cursor = std::io::Cursor::new(data);
1461        let result = decode(&mut cursor);
1462        assert!(result.is_err());
1463    }
1464
1465    #[test]
1466    fn test_decode_wrong_magic() {
1467        // Valid size but wrong magic bytes
1468        let mut data = vec![0u8; 200];
1469        data[0..4].copy_from_slice(b"NOPE");
1470        let mut cursor = std::io::Cursor::new(data);
1471        let result = decode(&mut cursor);
1472        assert!(result.is_err());
1473    }
1474
1475    #[test]
1476    fn test_decode_empty_file() {
1477        let data = vec![];
1478        let mut cursor = std::io::Cursor::new(data);
1479        let result = decode(&mut cursor);
1480        assert!(result.is_err());
1481    }
1482
1483    #[test]
1484    fn test_decoder_new_truncated() {
1485        let data = vec![0u8; 50]; // too small for APE header
1486        let cursor = std::io::Cursor::new(data);
1487        let result = ApeDecoder::new(cursor);
1488        assert!(result.is_err());
1489    }
1490
1491    // --- Post-processing transform tests ---
1492
1493    #[test]
1494    fn test_float_transform_roundtrip() {
1495        // FloatTransform is its own inverse
1496        let original: u32 = 0x3F800000; // IEEE 754 float 1.0
1497        let transformed = super::float_transform_sample(original);
1498        let restored = super::float_transform_sample(transformed);
1499        assert_eq!(restored, original);
1500    }
1501
1502    #[test]
1503    fn test_float_transform_zero() {
1504        let transformed = super::float_transform_sample(0);
1505        let restored = super::float_transform_sample(transformed);
1506        assert_eq!(restored, 0);
1507    }
1508
1509    #[test]
1510    fn test_byte_swap_16bit() {
1511        let mut data = vec![0x01, 0x02, 0x03, 0x04];
1512        super::byte_swap_samples(&mut data, 2);
1513        assert_eq!(data, vec![0x02, 0x01, 0x04, 0x03]);
1514    }
1515
1516    #[test]
1517    fn test_byte_swap_24bit() {
1518        let mut data = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
1519        super::byte_swap_samples(&mut data, 3);
1520        assert_eq!(data, vec![0x03, 0x02, 0x01, 0x06, 0x05, 0x04]);
1521    }
1522
1523    #[test]
1524    fn test_byte_swap_32bit() {
1525        let mut data = vec![0x01, 0x02, 0x03, 0x04];
1526        super::byte_swap_samples(&mut data, 4);
1527        assert_eq!(data, vec![0x04, 0x03, 0x02, 0x01]);
1528    }
1529
1530    // --- MD5 verification tests ---
1531
1532    #[test]
1533    fn test_verify_md5_all_fixtures() {
1534        let fixtures = [
1535            "dc_offset_16s_c2000.ape",
1536            "identical_16s_c2000.ape",
1537            "impulse_16s_c2000.ape",
1538            "left_only_16s_c2000.ape",
1539            "multiframe_16s_c2000.ape",
1540            "noise_16s_c2000.ape",
1541            "short_16s_c2000.ape",
1542            "silence_16s_c2000.ape",
1543            "sine_16m_c2000.ape",
1544            "sine_16s_c1000.ape",
1545            "sine_16s_c2000.ape",
1546            "sine_16s_c3000.ape",
1547            "sine_16s_c4000.ape",
1548            "sine_16s_c5000.ape",
1549            "sine_24s_c2000.ape",
1550            "sine_32s_c2000.ape",
1551            "sine_8s_c2000.ape",
1552        ];
1553
1554        for fixture in &fixtures {
1555            let reader = open_ape(fixture);
1556            let mut decoder = ApeDecoder::new(reader).unwrap();
1557            let result = decoder
1558                .verify_md5()
1559                .unwrap_or_else(|e| panic!("MD5 verify failed for {}: {:?}", fixture, e));
1560            assert!(result, "MD5 mismatch for {}", fixture);
1561        }
1562    }
1563
1564    #[test]
1565    fn test_stored_md5_nonzero() {
1566        let reader = open_ape("sine_16s_c2000.ape");
1567        let decoder = ApeDecoder::new(reader).unwrap();
1568        let md5 = decoder.stored_md5();
1569        // The mac tool should have stored a valid MD5
1570        assert_ne!(md5, &[0u8; 16], "MD5 should not be all zeros");
1571    }
1572
1573    // --- WAV header generation test ---
1574
1575    #[test]
1576    fn test_generate_wav_header() {
1577        let reader = open_ape("sine_16s_c2000.ape");
1578        let decoder = ApeDecoder::new(reader).unwrap();
1579        let header = decoder.info().generate_wav_header();
1580
1581        // Standard WAV header is 44 bytes
1582        assert_eq!(header.len(), 44);
1583
1584        // Check RIFF magic
1585        assert_eq!(&header[0..4], b"RIFF");
1586        assert_eq!(&header[8..12], b"WAVE");
1587        assert_eq!(&header[12..16], b"fmt ");
1588        assert_eq!(&header[36..40], b"data");
1589
1590        // Check format: PCM, 2 channels, 44100 Hz, 16-bit
1591        let channels = u16::from_le_bytes([header[22], header[23]]);
1592        let sample_rate = u32::from_le_bytes([header[24], header[25], header[26], header[27]]);
1593        let bits = u16::from_le_bytes([header[34], header[35]]);
1594        assert_eq!(channels, 2);
1595        assert_eq!(sample_rate, 44100);
1596        assert_eq!(bits, 16);
1597
1598        // Data size should match total_samples * block_align
1599        let data_size = u32::from_le_bytes([header[40], header[41], header[42], header[43]]);
1600        let expected = decoder.info().total_samples as u32 * decoder.info().block_align as u32;
1601        assert_eq!(data_size, expected);
1602    }
1603
1604    #[test]
1605    fn test_generate_wav_header_matches_stored() {
1606        let reader = open_ape("sine_16s_c2000.ape");
1607        let decoder = ApeDecoder::new(reader).unwrap();
1608
1609        let generated = decoder.info().generate_wav_header();
1610        if let Some(stored) = decoder.wav_header_data() {
1611            // Both should be 44 bytes for standard WAV
1612            if stored.len() == 44 {
1613                // Format fields should match (channels, rate, bits)
1614                assert_eq!(&generated[22..24], &stored[22..24]); // channels
1615                assert_eq!(&generated[24..28], &stored[24..28]); // sample rate
1616                assert_eq!(&generated[34..36], &stored[34..36]); // bits per sample
1617            }
1618        }
1619    }
1620}