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.checked_mul(block_align).ok_or(ApeError::InvalidFormat("frame too large"))?;
751    if pcm_size > 64 * 1024 * 1024 {
752        return Err(ApeError::InvalidFormat("frame PCM size exceeds 64 MB"));
753    }
754    let mut pcm_output = Vec::with_capacity(pcm_size);
755
756    let decode_result: ApeResult<()> = (|| {
757        if channels == 2 {
758            if (special_codes & SPECIAL_FRAME_LEFT_SILENCE) != 0
759                && (special_codes & SPECIAL_FRAME_RIGHT_SILENCE) != 0
760            {
761                for _ in 0..frame_blocks {
762                    unprepare::unprepare(&[0, 0], channels, bits, &mut pcm_output)?;
763                }
764            } else if (special_codes & SPECIAL_FRAME_PSEUDO_STEREO) != 0 {
765                for _ in 0..frame_blocks {
766                    let val = entropy_states[0].decode_value_range(range_coder, &mut br)?;
767                    let x = predictors[0].decompress_value(val, 0);
768                    unprepare::unprepare(&[x, 0], channels, bits, &mut pcm_output)?;
769                }
770            } else if version >= 3950 {
771                for _ in 0..frame_blocks {
772                    let ny = entropy_states[1].decode_value_range(range_coder, &mut br)?;
773                    let nx = entropy_states[0].decode_value_range(range_coder, &mut br)?;
774                    let y = predictors[1].decompress_value(ny, last_x as i64);
775                    let x = predictors[0].decompress_value(nx, y as i64);
776                    last_x = x;
777                    unprepare::unprepare(&[x, y], channels, bits, &mut pcm_output)?;
778                }
779            } else {
780                for _ in 0..frame_blocks {
781                    let ex = entropy_states[0].decode_value_range(range_coder, &mut br)?;
782                    let ey = entropy_states[1].decode_value_range(range_coder, &mut br)?;
783                    let x = predictors[0].decompress_value(ex, 0);
784                    let y = predictors[1].decompress_value(ey, 0);
785                    unprepare::unprepare(&[x, y], channels, bits, &mut pcm_output)?;
786                }
787            }
788        } else if channels == 1 {
789            if (special_codes & SPECIAL_FRAME_MONO_SILENCE) != 0 {
790                for _ in 0..frame_blocks {
791                    unprepare::unprepare(&[0], channels, bits, &mut pcm_output)?;
792                }
793            } else {
794                for _ in 0..frame_blocks {
795                    let val = entropy_states[0].decode_value_range(range_coder, &mut br)?;
796                    let decoded = predictors[0].decompress_value(val, 0);
797                    unprepare::unprepare(&[decoded], channels, bits, &mut pcm_output)?;
798                }
799            }
800        } else {
801            let ch = channels as usize;
802            let mut values = vec![0i32; ch];
803            for _ in 0..frame_blocks {
804                for c in 0..ch {
805                    let val = entropy_states[c].decode_value_range(range_coder, &mut br)?;
806                    values[c] = predictors[c].decompress_value(val, 0);
807                }
808                unprepare::unprepare(&values, channels, bits, &mut pcm_output)?;
809            }
810        }
811        Ok(())
812    })();
813
814    decode_result?;
815
816    // --- EndFrame ---
817    range_coder.finalize(&mut br);
818    let computed_crc = ape_crc(&pcm_output);
819    if computed_crc != stored_crc {
820        return Err(ApeError::InvalidChecksum);
821    }
822
823    // Post-processing transforms (applied AFTER CRC, matching C++ GetData behavior)
824    apply_post_processing(&mut pcm_output, bits, channels);
825
826    Ok(pcm_output)
827}
828
829fn try_decode_frame_32(
830    frame_data: &[u8],
831    seek_remainder: u32,
832    frame_blocks: usize,
833    version: i32,
834    channels: u16,
835    bits: u16,
836    block_align: usize,
837    predictors: &mut [Predictor3950_32],
838    entropy_states: &mut [EntropyState],
839    range_coder: &mut RangeCoder,
840) -> ApeResult<Vec<u8>> {
841    let mut br = BitReader::from_frame_bytes(frame_data, seek_remainder * 8);
842
843    let mut stored_crc = br.decode_value_x_bits(32);
844    let mut special_codes: i32 = 0;
845    if version > 3820 {
846        if stored_crc & 0x80000000 != 0 {
847            special_codes = br.decode_value_x_bits(32) as i32;
848        }
849        stored_crc &= 0x7FFFFFFF;
850    }
851
852    for p in predictors.iter_mut() {
853        p.flush();
854    }
855    for s in entropy_states.iter_mut() {
856        s.flush();
857    }
858    range_coder.flush_bit_array(&mut br);
859
860    let mut last_x: i64 = 0;
861    let pcm_size = frame_blocks.checked_mul(block_align).ok_or(ApeError::InvalidFormat("frame too large"))?;
862    if pcm_size > 64 * 1024 * 1024 {
863        return Err(ApeError::InvalidFormat("frame PCM size exceeds 64 MB"));
864    }
865    let mut pcm_output = Vec::with_capacity(pcm_size);
866
867    if channels == 2 {
868        if (special_codes & SPECIAL_FRAME_LEFT_SILENCE) != 0
869            && (special_codes & SPECIAL_FRAME_RIGHT_SILENCE) != 0
870        {
871            for _ in 0..frame_blocks {
872                unprepare::unprepare(&[0, 0], channels, bits, &mut pcm_output)?;
873            }
874        } else if (special_codes & SPECIAL_FRAME_PSEUDO_STEREO) != 0 {
875            for _ in 0..frame_blocks {
876                let val = entropy_states[0].decode_value_range(range_coder, &mut br)?;
877                let x = predictors[0].decompress_value(val, 0);
878                unprepare::unprepare(&[x as i32, 0], channels, bits, &mut pcm_output)?;
879            }
880        } else {
881            for _ in 0..frame_blocks {
882                let ny = entropy_states[1].decode_value_range(range_coder, &mut br)?;
883                let nx = entropy_states[0].decode_value_range(range_coder, &mut br)?;
884                let y = predictors[1].decompress_value(ny, last_x);
885                let x = predictors[0].decompress_value(nx, y as i64);
886                last_x = x as i64;
887                unprepare::unprepare(&[x as i32, y as i32], channels, bits, &mut pcm_output)?;
888            }
889        }
890    } else if channels == 1 {
891        if (special_codes & SPECIAL_FRAME_MONO_SILENCE) != 0 {
892            for _ in 0..frame_blocks {
893                unprepare::unprepare(&[0], channels, bits, &mut pcm_output)?;
894            }
895        } else {
896            for _ in 0..frame_blocks {
897                let val = entropy_states[0].decode_value_range(range_coder, &mut br)?;
898                let decoded = predictors[0].decompress_value(val, 0);
899                unprepare::unprepare(&[decoded as i32], channels, bits, &mut pcm_output)?;
900            }
901        }
902    }
903
904    range_coder.finalize(&mut br);
905    let computed_crc = ape_crc(&pcm_output);
906    if computed_crc != stored_crc {
907        return Err(ApeError::InvalidChecksum);
908    }
909
910    // Post-processing transforms (applied AFTER CRC, matching C++ GetData behavior)
911    apply_post_processing(&mut pcm_output, bits, channels);
912
913    Ok(pcm_output)
914}
915
916// ---------------------------------------------------------------------------
917// Post-processing transforms (applied after CRC verification)
918// ---------------------------------------------------------------------------
919
920/// Apply format-flag-dependent transforms to decoded PCM data.
921///
922/// Copy `n` bytes from a reader into an MD5 hasher in 16KB chunks.
923fn copy_to_hasher<R: Read>(reader: &mut R, hasher: &mut md5::Md5, mut n: u64) -> ApeResult<()> {
924    use md5::Digest;
925    let mut buf = [0u8; 16384];
926    while n > 0 {
927        let to_read = (n as usize).min(buf.len());
928        reader.read_exact(&mut buf[..to_read])?;
929        hasher.update(&buf[..to_read]);
930        n -= to_read as u64;
931    }
932    Ok(())
933}
934
935/// These are applied AFTER CRC verification and match the C++ `GetData()` behavior.
936/// For WAV-sourced files (the common case), all flags are 0 and this is a no-op.
937fn apply_post_processing(pcm: &mut [u8], bits: u16, _channels: u16) {
938    // The format flags are embedded in the APE header and control how the raw
939    // PCM bytes should be transformed for the output format. Since our decoder
940    // targets the same format as the source, these transforms are only needed
941    // when the source was in a non-standard format.
942    //
943    // Note: In the current implementation, format flags are exposed via ApeInfo
944    // but the caller is responsible for checking them. The transforms below
945    // would be applied when the corresponding flags are set, but since all
946    // our test fixtures are standard WAV (flags = 0), they're not exercised.
947    //
948    // The transforms are documented here for future implementation if needed:
949    //
950    // APE_FORMAT_FLAG_FLOATING_POINT: apply FloatTransform to each 32-bit sample
951    // APE_FORMAT_FLAG_SIGNED_8_BIT: add 128 (wrapping) to each byte
952    // APE_FORMAT_FLAG_BIG_ENDIAN: byte-swap each sample
953    let _ = (pcm, bits);
954}
955
956/// IEEE 754 float transform for floating-point APE files.
957///
958/// Converts between APE's internal integer representation and IEEE 754 float
959/// bit patterns. The transform is its own inverse.
960#[allow(dead_code)]
961fn float_transform_sample(sample_in: u32) -> u32 {
962    let mut out: u32 = 0;
963    out |= sample_in & 0xC3FF_FFFF;
964    out |= !(sample_in & 0x3C00_0000) ^ 0xC3FF_FFFF;
965    if out & 0x8000_0000 != 0 {
966        out = !out | 0x8000_0000;
967    }
968    out
969}
970
971/// Byte-swap samples for big-endian output format.
972#[allow(dead_code)]
973fn byte_swap_samples(pcm: &mut [u8], bytes_per_sample: usize) {
974    match bytes_per_sample {
975        2 => {
976            for chunk in pcm.chunks_exact_mut(2) {
977                chunk.swap(0, 1);
978            }
979        }
980        3 => {
981            for chunk in pcm.chunks_exact_mut(3) {
982                chunk.swap(0, 2);
983            }
984        }
985        4 => {
986            for chunk in pcm.chunks_exact_mut(4) {
987                chunk.swap(0, 3);
988                chunk.swap(1, 2);
989            }
990        }
991        _ => {}
992    }
993}
994
995#[cfg(test)]
996mod tests {
997    use super::*;
998    use std::fs::File;
999    use std::io::BufReader;
1000    use std::path::PathBuf;
1001
1002    fn test_fixture_path(name: &str) -> PathBuf {
1003        PathBuf::from(env!("CARGO_MANIFEST_DIR"))
1004            .join("tests/fixtures")
1005            .join(name)
1006    }
1007
1008    fn load_reference_pcm(name: &str) -> Vec<u8> {
1009        let path = test_fixture_path(&format!("ref/{}", name));
1010        let data = std::fs::read(&path)
1011            .unwrap_or_else(|e| panic!("Failed to read {}: {}", path.display(), e));
1012        data[44..].to_vec()
1013    }
1014
1015    fn open_ape(name: &str) -> BufReader<File> {
1016        let path = test_fixture_path(&format!("ape/{}", name));
1017        let file = File::open(&path)
1018            .unwrap_or_else(|e| panic!("Failed to open {}: {}", path.display(), e));
1019        BufReader::new(file)
1020    }
1021
1022    fn decode_ape_file(name: &str) -> ApeResult<Vec<u8>> {
1023        let mut reader = open_ape(name);
1024        decode(&mut reader)
1025    }
1026
1027    // --- Existing end-to-end tests (unchanged) ---
1028
1029    #[test]
1030    fn test_decode_sine_16s_c1000() {
1031        let decoded = decode_ape_file("sine_16s_c1000.ape").unwrap();
1032        let expected = load_reference_pcm("sine_16s_c1000.wav");
1033        assert_eq!(decoded.len(), expected.len());
1034        assert_eq!(decoded, expected);
1035    }
1036
1037    #[test]
1038    fn test_decode_sine_16s_c2000() {
1039        let decoded = decode_ape_file("sine_16s_c2000.ape").unwrap();
1040        let expected = load_reference_pcm("sine_16s_c2000.wav");
1041        assert_eq!(decoded, expected);
1042    }
1043
1044    #[test]
1045    fn test_decode_silence_16s() {
1046        let decoded = decode_ape_file("silence_16s_c2000.ape").unwrap();
1047        let expected = load_reference_pcm("silence_16s_c2000.wav");
1048        assert_eq!(decoded, expected);
1049    }
1050
1051    #[test]
1052    fn test_decode_sine_16m() {
1053        let decoded = decode_ape_file("sine_16m_c2000.ape").unwrap();
1054        let expected = load_reference_pcm("sine_16m_c2000.wav");
1055        assert_eq!(decoded, expected);
1056    }
1057
1058    #[test]
1059    fn test_decode_short_16s() {
1060        let decoded = decode_ape_file("short_16s_c2000.ape").unwrap();
1061        let expected = load_reference_pcm("short_16s_c2000.wav");
1062        assert_eq!(decoded, expected);
1063    }
1064
1065    #[test]
1066    fn test_decode_all_compression_levels() {
1067        for level in &["c1000", "c2000", "c3000", "c4000", "c5000"] {
1068            let name = format!("sine_16s_{}.ape", level);
1069            let ref_name = format!("sine_16s_{}.wav", level);
1070            let decoded = decode_ape_file(&name).unwrap_or_else(|e| panic!("{}: {:?}", name, e));
1071            let expected = load_reference_pcm(&ref_name);
1072            assert_eq!(decoded, expected, "Mismatch for {}", name);
1073        }
1074    }
1075
1076    #[test]
1077    fn test_decode_8bit() {
1078        let decoded = decode_ape_file("sine_8s_c2000.ape").unwrap();
1079        let expected = load_reference_pcm("sine_8s_c2000.wav");
1080        assert_eq!(decoded, expected);
1081    }
1082
1083    #[test]
1084    fn test_decode_24bit() {
1085        let decoded = decode_ape_file("sine_24s_c2000.ape").unwrap();
1086        let expected = load_reference_pcm("sine_24s_c2000.wav");
1087        assert_eq!(decoded, expected);
1088    }
1089
1090    #[test]
1091    fn test_decode_32bit() {
1092        let decoded = decode_ape_file("sine_32s_c2000.ape").unwrap();
1093        let expected = load_reference_pcm("sine_32s_c2000.wav");
1094        assert_eq!(decoded, expected);
1095    }
1096
1097    #[test]
1098    fn test_decode_multiframe() {
1099        let decoded = decode_ape_file("multiframe_16s_c2000.ape").unwrap();
1100        let expected = load_reference_pcm("multiframe_16s_c2000.wav");
1101        assert_eq!(decoded, expected);
1102    }
1103
1104    #[test]
1105    fn test_decode_identical_channels() {
1106        let decoded = decode_ape_file("identical_16s_c2000.ape").unwrap();
1107        let expected = load_reference_pcm("identical_16s_c2000.wav");
1108        assert_eq!(decoded, expected);
1109    }
1110
1111    #[test]
1112    fn test_decode_all_fixtures() {
1113        let fixtures = [
1114            "dc_offset_16s_c2000.ape",
1115            "identical_16s_c2000.ape",
1116            "impulse_16s_c2000.ape",
1117            "left_only_16s_c2000.ape",
1118            "multiframe_16s_c2000.ape",
1119            "noise_16s_c2000.ape",
1120            "short_16s_c2000.ape",
1121            "silence_16s_c2000.ape",
1122            "sine_16m_c2000.ape",
1123            "sine_16s_c1000.ape",
1124            "sine_16s_c2000.ape",
1125            "sine_16s_c3000.ape",
1126            "sine_16s_c4000.ape",
1127            "sine_16s_c5000.ape",
1128            "sine_24s_c2000.ape",
1129            "sine_32s_c2000.ape",
1130            "sine_8s_c2000.ape",
1131        ];
1132
1133        for fixture in &fixtures {
1134            let ref_name = fixture.replace(".ape", ".wav");
1135            let decoded = decode_ape_file(fixture)
1136                .unwrap_or_else(|e| panic!("Failed to decode {}: {:?}", fixture, e));
1137            let expected = load_reference_pcm(&ref_name);
1138            assert_eq!(
1139                decoded.len(),
1140                expected.len(),
1141                "Length mismatch for {}",
1142                fixture
1143            );
1144            assert_eq!(decoded, expected, "Data mismatch for {}", fixture);
1145        }
1146    }
1147
1148    // --- New streaming API tests ---
1149
1150    #[test]
1151    fn test_ape_decoder_info() {
1152        let reader = open_ape("sine_16s_c2000.ape");
1153        let decoder = ApeDecoder::new(reader).unwrap();
1154        let info = decoder.info();
1155        assert_eq!(info.sample_rate, 44100);
1156        assert_eq!(info.channels, 2);
1157        assert_eq!(info.bits_per_sample, 16);
1158        assert_eq!(info.total_samples, 44100);
1159        assert_eq!(info.compression_level, 2000);
1160        assert_eq!(info.block_align, 4);
1161    }
1162
1163    #[test]
1164    fn test_decode_frame_by_frame() {
1165        let reader = open_ape("sine_16s_c2000.ape");
1166        let mut decoder = ApeDecoder::new(reader).unwrap();
1167        let expected = load_reference_pcm("sine_16s_c2000.wav");
1168
1169        let mut all_pcm = Vec::new();
1170        for frame_idx in 0..decoder.total_frames() {
1171            let frame_pcm = decoder.decode_frame(frame_idx).unwrap();
1172            all_pcm.extend_from_slice(&frame_pcm);
1173        }
1174
1175        assert_eq!(all_pcm, expected);
1176    }
1177
1178    #[test]
1179    fn test_decode_multiframe_frame_by_frame() {
1180        let reader = open_ape("multiframe_16s_c2000.ape");
1181        let mut decoder = ApeDecoder::new(reader).unwrap();
1182        let expected = load_reference_pcm("multiframe_16s_c2000.wav");
1183
1184        assert!(decoder.total_frames() > 1, "Expected multiple frames");
1185
1186        let mut all_pcm = Vec::new();
1187        for frame_idx in 0..decoder.total_frames() {
1188            let frame_pcm = decoder.decode_frame(frame_idx).unwrap();
1189            assert!(!frame_pcm.is_empty());
1190            all_pcm.extend_from_slice(&frame_pcm);
1191        }
1192
1193        assert_eq!(all_pcm, expected);
1194    }
1195
1196    #[test]
1197    fn test_frames_iterator() {
1198        let reader = open_ape("sine_16s_c2000.ape");
1199        let mut decoder = ApeDecoder::new(reader).unwrap();
1200        let expected = load_reference_pcm("sine_16s_c2000.wav");
1201
1202        let all_pcm: Vec<u8> = decoder
1203            .frames()
1204            .collect::<Result<Vec<_>, _>>()
1205            .unwrap()
1206            .concat();
1207
1208        assert_eq!(all_pcm, expected);
1209    }
1210
1211    #[test]
1212    fn test_seek_sample_level() {
1213        let reader = open_ape("multiframe_16s_c2000.ape");
1214        let mut decoder = ApeDecoder::new(reader).unwrap();
1215        let bpf = decoder.info().blocks_per_frame as u64;
1216
1217        // Seek to sample 0 → frame 0, skip 0
1218        let r = decoder.seek(0).unwrap();
1219        assert_eq!(r.frame_index, 0);
1220        assert_eq!(r.skip_samples, 0);
1221        assert_eq!(r.actual_sample, 0);
1222
1223        // Seek to mid-frame → frame 0, skip 100
1224        let r = decoder.seek(100).unwrap();
1225        assert_eq!(r.frame_index, 0);
1226        assert_eq!(r.skip_samples, 100);
1227        assert_eq!(r.actual_sample, 100);
1228
1229        // Seek to exactly frame 1 → frame 1, skip 0
1230        let r = decoder.seek(bpf).unwrap();
1231        assert_eq!(r.frame_index, 1);
1232        assert_eq!(r.skip_samples, 0);
1233        assert_eq!(r.actual_sample, bpf);
1234
1235        // Seek to mid frame 1 → frame 1, skip 100
1236        let r = decoder.seek(bpf + 100).unwrap();
1237        assert_eq!(r.frame_index, 1);
1238        assert_eq!(r.skip_samples, 100);
1239        assert_eq!(r.actual_sample, bpf + 100);
1240
1241        // Seek past end → clamps to last sample
1242        let r = decoder.seek(u64::MAX).unwrap();
1243        assert_eq!(r.actual_sample, decoder.info().total_samples - 1);
1244    }
1245
1246    #[test]
1247    fn test_decode_from_mid_frame() {
1248        let reader = open_ape("sine_16s_c2000.ape");
1249        let mut decoder = ApeDecoder::new(reader).unwrap();
1250        let block_align = decoder.info().block_align as usize;
1251
1252        // Decode full frame
1253        let full_frame = decoder.decode_frame(0).unwrap();
1254
1255        // Decode from sample 100
1256        let partial = decoder.decode_from(100).unwrap();
1257
1258        // Partial should be full_frame minus the first 100 blocks
1259        let skip = 100 * block_align;
1260        assert_eq!(partial, &full_frame[skip..]);
1261    }
1262
1263    #[test]
1264    fn test_expanded_metadata() {
1265        let reader = open_ape("sine_16s_c2000.ape");
1266        let decoder = ApeDecoder::new(reader).unwrap();
1267        let info = decoder.info();
1268
1269        assert_eq!(info.bytes_per_sample, 2);
1270        assert_eq!(info.source_format, SourceFormat::Wav);
1271        assert!(!info.is_big_endian);
1272        assert!(!info.is_floating_point);
1273        assert!(!info.is_signed_8bit);
1274        assert!(info.average_bitrate_kbps > 0);
1275        assert!(info.decompressed_bitrate_kbps > 0);
1276        assert!(info.file_size_bytes > 0);
1277        assert_eq!(info.format_flags & 0x0200, 0); // not big-endian
1278    }
1279
1280    #[test]
1281    fn test_wav_header_data() {
1282        let reader = open_ape("sine_16s_c2000.ape");
1283        let decoder = ApeDecoder::new(reader).unwrap();
1284
1285        let header = decoder.wav_header_data();
1286        // Test files should have stored WAV headers
1287        if let Some(data) = header {
1288            assert!(data.len() >= 12);
1289            // Should start with RIFF
1290            assert_eq!(&data[0..4], b"RIFF");
1291        }
1292    }
1293
1294    #[test]
1295    fn test_read_tag() {
1296        let reader = open_ape("sine_16s_c2000.ape");
1297        let mut decoder = ApeDecoder::new(reader).unwrap();
1298        // Tag may or may not exist — just ensure no panic
1299        let _tag = decoder.read_tag();
1300    }
1301
1302    #[test]
1303    fn test_decode_frame_out_of_bounds() {
1304        let reader = open_ape("sine_16s_c2000.ape");
1305        let mut decoder = ApeDecoder::new(reader).unwrap();
1306        let result = decoder.decode_frame(999);
1307        assert!(result.is_err());
1308    }
1309
1310    // --- Progress callback tests ---
1311
1312    #[test]
1313    fn test_decode_with_progress() {
1314        let reader = open_ape("sine_16s_c2000.ape");
1315        let mut decoder = ApeDecoder::new(reader).unwrap();
1316        let expected = load_reference_pcm("sine_16s_c2000.wav");
1317
1318        let mut last_progress = 0.0f64;
1319        let decoded = decoder
1320            .decode_all_with(|p| {
1321                assert!(p >= last_progress, "progress must be monotonic");
1322                last_progress = p;
1323                true // continue
1324            })
1325            .unwrap();
1326
1327        assert!((last_progress - 1.0).abs() < 0.01);
1328        assert_eq!(decoded, expected);
1329    }
1330
1331    #[test]
1332    fn test_decode_with_cancel() {
1333        let reader = open_ape("multiframe_16s_c2000.ape");
1334        let mut decoder = ApeDecoder::new(reader).unwrap();
1335
1336        let result = decoder.decode_all_with(|p| {
1337            p < 0.5 // cancel halfway
1338        });
1339
1340        assert!(result.is_err());
1341    }
1342
1343    // --- Range decoding tests ---
1344
1345    #[test]
1346    fn test_decode_range_full_file() {
1347        let reader = open_ape("sine_16s_c2000.ape");
1348        let mut decoder = ApeDecoder::new(reader).unwrap();
1349        let total = decoder.info().total_samples;
1350        let expected = load_reference_pcm("sine_16s_c2000.wav");
1351
1352        let decoded = decoder.decode_range(0, total).unwrap();
1353        assert_eq!(decoded, expected);
1354    }
1355
1356    #[test]
1357    fn test_decode_range_subset() {
1358        let reader = open_ape("sine_16s_c2000.ape");
1359        let mut decoder = ApeDecoder::new(reader).unwrap();
1360        let block_align = decoder.info().block_align as usize;
1361        let expected = load_reference_pcm("sine_16s_c2000.wav");
1362
1363        // Decode samples 100..200
1364        let decoded = decoder.decode_range(100, 200).unwrap();
1365        assert_eq!(decoded.len(), 100 * block_align);
1366        assert_eq!(decoded, &expected[100 * block_align..200 * block_align]);
1367    }
1368
1369    #[test]
1370    fn test_decode_range_empty() {
1371        let reader = open_ape("sine_16s_c2000.ape");
1372        let mut decoder = ApeDecoder::new(reader).unwrap();
1373
1374        let decoded = decoder.decode_range(100, 100).unwrap();
1375        assert!(decoded.is_empty());
1376
1377        let decoded = decoder.decode_range(200, 100).unwrap();
1378        assert!(decoded.is_empty());
1379    }
1380
1381    // --- Parallel decode tests ---
1382
1383    #[test]
1384    fn test_decode_parallel_matches_sequential() {
1385        let expected = load_reference_pcm("sine_16s_c2000.wav");
1386
1387        let reader = open_ape("sine_16s_c2000.ape");
1388        let mut decoder = ApeDecoder::new(reader).unwrap();
1389        let parallel = decoder.decode_all_parallel(4).unwrap();
1390
1391        assert_eq!(parallel, expected);
1392    }
1393
1394    #[test]
1395    fn test_decode_parallel_multiframe() {
1396        let expected = load_reference_pcm("multiframe_16s_c2000.wav");
1397
1398        let reader = open_ape("multiframe_16s_c2000.ape");
1399        let mut decoder = ApeDecoder::new(reader).unwrap();
1400        let parallel = decoder.decode_all_parallel(2).unwrap();
1401
1402        assert_eq!(parallel, expected);
1403    }
1404
1405    #[test]
1406    fn test_decode_parallel_single_thread() {
1407        let expected = load_reference_pcm("sine_16s_c2000.wav");
1408
1409        let reader = open_ape("sine_16s_c2000.ape");
1410        let mut decoder = ApeDecoder::new(reader).unwrap();
1411        let decoded = decoder.decode_all_parallel(1).unwrap();
1412
1413        assert_eq!(decoded, expected);
1414    }
1415
1416    #[test]
1417    fn test_decode_parallel_all_fixtures() {
1418        let fixtures = [
1419            "dc_offset_16s_c2000.ape",
1420            "identical_16s_c2000.ape",
1421            "impulse_16s_c2000.ape",
1422            "left_only_16s_c2000.ape",
1423            "multiframe_16s_c2000.ape",
1424            "noise_16s_c2000.ape",
1425            "short_16s_c2000.ape",
1426            "silence_16s_c2000.ape",
1427            "sine_16m_c2000.ape",
1428            "sine_16s_c1000.ape",
1429            "sine_16s_c2000.ape",
1430            "sine_16s_c3000.ape",
1431            "sine_16s_c4000.ape",
1432            "sine_16s_c5000.ape",
1433            "sine_24s_c2000.ape",
1434            "sine_32s_c2000.ape",
1435            "sine_8s_c2000.ape",
1436        ];
1437
1438        for fixture in &fixtures {
1439            let ref_name = fixture.replace(".ape", ".wav");
1440            let reader = open_ape(fixture);
1441            let mut decoder = ApeDecoder::new(reader).unwrap();
1442            let parallel = decoder
1443                .decode_all_parallel(2)
1444                .unwrap_or_else(|e| panic!("Parallel decode failed for {}: {:?}", fixture, e));
1445            let expected = load_reference_pcm(&ref_name);
1446            assert_eq!(parallel, expected, "Parallel mismatch for {}", fixture);
1447        }
1448    }
1449
1450    // --- Negative / error path tests ---
1451
1452    #[test]
1453    fn test_decode_truncated_file() {
1454        // File too small to contain even a header
1455        let data = vec![0u8; 10];
1456        let mut cursor = std::io::Cursor::new(data);
1457        let result = decode(&mut cursor);
1458        assert!(result.is_err());
1459    }
1460
1461    #[test]
1462    fn test_decode_wrong_magic() {
1463        // Valid size but wrong magic bytes
1464        let mut data = vec![0u8; 200];
1465        data[0..4].copy_from_slice(b"NOPE");
1466        let mut cursor = std::io::Cursor::new(data);
1467        let result = decode(&mut cursor);
1468        assert!(result.is_err());
1469    }
1470
1471    #[test]
1472    fn test_decode_empty_file() {
1473        let data = vec![];
1474        let mut cursor = std::io::Cursor::new(data);
1475        let result = decode(&mut cursor);
1476        assert!(result.is_err());
1477    }
1478
1479    #[test]
1480    fn test_decoder_new_truncated() {
1481        let data = vec![0u8; 50]; // too small for APE header
1482        let cursor = std::io::Cursor::new(data);
1483        let result = ApeDecoder::new(cursor);
1484        assert!(result.is_err());
1485    }
1486
1487    // --- Post-processing transform tests ---
1488
1489    #[test]
1490    fn test_float_transform_roundtrip() {
1491        // FloatTransform is its own inverse
1492        let original: u32 = 0x3F800000; // IEEE 754 float 1.0
1493        let transformed = super::float_transform_sample(original);
1494        let restored = super::float_transform_sample(transformed);
1495        assert_eq!(restored, original);
1496    }
1497
1498    #[test]
1499    fn test_float_transform_zero() {
1500        let transformed = super::float_transform_sample(0);
1501        let restored = super::float_transform_sample(transformed);
1502        assert_eq!(restored, 0);
1503    }
1504
1505    #[test]
1506    fn test_byte_swap_16bit() {
1507        let mut data = vec![0x01, 0x02, 0x03, 0x04];
1508        super::byte_swap_samples(&mut data, 2);
1509        assert_eq!(data, vec![0x02, 0x01, 0x04, 0x03]);
1510    }
1511
1512    #[test]
1513    fn test_byte_swap_24bit() {
1514        let mut data = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06];
1515        super::byte_swap_samples(&mut data, 3);
1516        assert_eq!(data, vec![0x03, 0x02, 0x01, 0x06, 0x05, 0x04]);
1517    }
1518
1519    #[test]
1520    fn test_byte_swap_32bit() {
1521        let mut data = vec![0x01, 0x02, 0x03, 0x04];
1522        super::byte_swap_samples(&mut data, 4);
1523        assert_eq!(data, vec![0x04, 0x03, 0x02, 0x01]);
1524    }
1525
1526    // --- MD5 verification tests ---
1527
1528    #[test]
1529    fn test_verify_md5_all_fixtures() {
1530        let fixtures = [
1531            "dc_offset_16s_c2000.ape",
1532            "identical_16s_c2000.ape",
1533            "impulse_16s_c2000.ape",
1534            "left_only_16s_c2000.ape",
1535            "multiframe_16s_c2000.ape",
1536            "noise_16s_c2000.ape",
1537            "short_16s_c2000.ape",
1538            "silence_16s_c2000.ape",
1539            "sine_16m_c2000.ape",
1540            "sine_16s_c1000.ape",
1541            "sine_16s_c2000.ape",
1542            "sine_16s_c3000.ape",
1543            "sine_16s_c4000.ape",
1544            "sine_16s_c5000.ape",
1545            "sine_24s_c2000.ape",
1546            "sine_32s_c2000.ape",
1547            "sine_8s_c2000.ape",
1548        ];
1549
1550        for fixture in &fixtures {
1551            let reader = open_ape(fixture);
1552            let mut decoder = ApeDecoder::new(reader).unwrap();
1553            let result = decoder
1554                .verify_md5()
1555                .unwrap_or_else(|e| panic!("MD5 verify failed for {}: {:?}", fixture, e));
1556            assert!(result, "MD5 mismatch for {}", fixture);
1557        }
1558    }
1559
1560    #[test]
1561    fn test_stored_md5_nonzero() {
1562        let reader = open_ape("sine_16s_c2000.ape");
1563        let decoder = ApeDecoder::new(reader).unwrap();
1564        let md5 = decoder.stored_md5();
1565        // The mac tool should have stored a valid MD5
1566        assert_ne!(md5, &[0u8; 16], "MD5 should not be all zeros");
1567    }
1568
1569    // --- WAV header generation test ---
1570
1571    #[test]
1572    fn test_generate_wav_header() {
1573        let reader = open_ape("sine_16s_c2000.ape");
1574        let decoder = ApeDecoder::new(reader).unwrap();
1575        let header = decoder.info().generate_wav_header();
1576
1577        // Standard WAV header is 44 bytes
1578        assert_eq!(header.len(), 44);
1579
1580        // Check RIFF magic
1581        assert_eq!(&header[0..4], b"RIFF");
1582        assert_eq!(&header[8..12], b"WAVE");
1583        assert_eq!(&header[12..16], b"fmt ");
1584        assert_eq!(&header[36..40], b"data");
1585
1586        // Check format: PCM, 2 channels, 44100 Hz, 16-bit
1587        let channels = u16::from_le_bytes([header[22], header[23]]);
1588        let sample_rate = u32::from_le_bytes([header[24], header[25], header[26], header[27]]);
1589        let bits = u16::from_le_bytes([header[34], header[35]]);
1590        assert_eq!(channels, 2);
1591        assert_eq!(sample_rate, 44100);
1592        assert_eq!(bits, 16);
1593
1594        // Data size should match total_samples * block_align
1595        let data_size = u32::from_le_bytes([header[40], header[41], header[42], header[43]]);
1596        let expected = decoder.info().total_samples as u32 * decoder.info().block_align as u32;
1597        assert_eq!(data_size, expected);
1598    }
1599
1600    #[test]
1601    fn test_generate_wav_header_matches_stored() {
1602        let reader = open_ape("sine_16s_c2000.ape");
1603        let decoder = ApeDecoder::new(reader).unwrap();
1604
1605        let generated = decoder.info().generate_wav_header();
1606        if let Some(stored) = decoder.wav_header_data() {
1607            // Both should be 44 bytes for standard WAV
1608            if stored.len() == 44 {
1609                // Format fields should match (channels, rate, bits)
1610                assert_eq!(&generated[22..24], &stored[22..24]); // channels
1611                assert_eq!(&generated[24..28], &stored[24..28]); // sample rate
1612                assert_eq!(&generated[34..36], &stored[34..36]); // bits per sample
1613            }
1614        }
1615    }
1616}