1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
use std::io::Cursor;
use std::marker::PhantomData;
use std::mem;

use byteorder::{BigEndian, ByteOrder, LittleEndian, WriteBytesExt};
use claxon::frame::FrameReader;

use crate::cdrom::{CD_FRAME_SIZE, CD_MAX_SECTOR_DATA, CD_MAX_SUBCODE_DATA};
use crate::compression::zlib::ZlibCodec;
use crate::compression::{
    CodecImplementation, CompressionCodec, CompressionCodecType, DecompressResult,
};
use crate::error::{Error, Result};
use crate::header::CodecType;

/// Generic block decoder for FLAC.
///
/// Defaults assume 2 channel interleaved FLAC.
/// The byte order determines the endianness of the output data.
struct FlacCodec<T: ByteOrder, const CHANNELS: usize = 2> {
    buffer: Vec<i32>,
    _byteorder: PhantomData<T>,
}

impl<T: ByteOrder, const CHANNELS: usize> CodecImplementation for FlacCodec<T, CHANNELS> {
    fn new(hunk_bytes: u32) -> Result<Self>
    where
        Self: Sized,
    {
        if hunk_bytes % (CHANNELS * mem::size_of::<i16>()) as u32 != 0 {
            return Err(Error::CodecError);
        }

        Ok(FlacCodec {
            buffer: Vec::new(),
            _byteorder: PhantomData::default(),
        })
    }

    fn decompress(&mut self, input: &[u8], output: &mut [u8]) -> Result<DecompressResult> {
        let comp_buf = Cursor::new(input);

        // Number of samples to write to the buffer.
        let sample_len = output.len() / (CHANNELS * mem::size_of::<i16>());

        // We don't need to create a fake header since claxon will read raw FLAC frames just fine.
        // We just need to be careful not to read past the number of blocks in the input buffer.
        let mut frame_read = FrameReader::new(comp_buf);

        let mut cursor = Cursor::new(output);

        // Buffer to hold decompressed FLAC block data.
        let mut block_buf = mem::take(&mut self.buffer);

        // A little bit of a misnomer. 1 'sample' refers to a sample for all channels.
        let mut samples_written = 0;

        while samples_written < sample_len {
            // Loop through all blocks until we have enough samples written.
            match frame_read.read_next_or_eof(block_buf) {
                Ok(Some(block)) => {
                    // We assume 2 channels (by default), so we can use claxon's stereo_samples
                    // iterator for slightly better performance.
                    #[cfg(not(feature = "nonstandard_channel_count"))]
                    for (l, r) in block.stereo_samples() {
                        cursor.write_i16::<T>(l as i16)?;
                        cursor.write_i16::<T>(r as i16)?;
                        samples_written += 1;
                    }

                    // This is generic over number of assumed channels, but is broken effectively
                    // for any value other than 2.
                    // What we really want here is specialization for CHANNELS = 2 ...
                    #[cfg(feature = "nonstandard_channel_count")]
                    for sample in 0..block.len() / block.channels() {
                        for channel in 0..block.channels() {
                            let sample_data = block.sample(channel, sample) as u16;
                            cursor.write_i16::<T>(sample_data as i16)?;
                        }
                        samples_written += 1;
                    }

                    block_buf = block.into_buffer();
                }
                _ => {
                    // If frame_read dies our buffer just gets eaten. The Error return for a failed
                    // read does not expose the inner buffer.
                    return Err(Error::DecompressionError);
                }
            }
        }

        self.buffer = block_buf;
        let bytes_in = frame_read.into_inner().position();
        Ok(DecompressResult::new(
            samples_written * 4,
            bytes_in as usize,
        ))
    }
}

/// Raw FLAC (flac) decompression codec.
///
/// ## Format details
/// Raw FLAC expects the first byte as either 'L' (0x4C) or 'B' (0x42) to indicate the endianness
/// of the output data, followed by the compressed FLAC data.
///
/// FLAC compressed audio data is assumed to be 2-channel 16-bit signed integer PCM.
/// The audio data is decompressed in interleaved format, with the left channel first, then
/// the right channel for each sample, for 32 bits each sample.
///
/// ## Buffer Restrictions
/// Each compressed FLAC hunk decompresses to a hunk-sized chunk.
/// The input buffer must contain enough samples to fill the hunk-sized output buffer.
pub struct RawFlacCodec {
    be: FlacCodec<BigEndian>,
    le: FlacCodec<LittleEndian>,
}

impl CompressionCodec for RawFlacCodec {}

impl CompressionCodecType for RawFlacCodec {
    fn codec_type(&self) -> CodecType
    where
        Self: Sized,
    {
        CodecType::FlacV5
    }
}

impl CodecImplementation for RawFlacCodec {
    fn new(hunk_bytes: u32) -> Result<Self> {
        Ok(RawFlacCodec {
            be: FlacCodec::new(hunk_bytes)?,
            le: FlacCodec::new(hunk_bytes)?,
        })
    }

    fn decompress(&mut self, input: &[u8], output: &mut [u8]) -> Result<DecompressResult> {
        match input[0] {
            b'L' => self.le.decompress(&input[1..], output),
            b'B' => self.be.decompress(&input[1..], output),
            _ => Err(Error::DecompressionError),
        }
    }
}

/// CD-ROM wrapper decompression codec (cdfl) using the FLAC
/// for decompression of sector data and the [Deflate codec](crate::codecs::ZlibCodec) for
/// decompression of subcode data.
///
/// ## Format Details
/// FLAC compressed audio data is assumed to be 2-channel 16-bit signed integer PCM.
/// The audio data is decompressed in interleaved format, with the left channel first, then
/// the right channel for each sample, for 32 bits each sample.
///
/// CD-ROM wrapped FLAC is always written to the output stream in big-endian byte order.
///
/// CD-ROM compressed hunks have a layout with all compressed frame data in sequential order,
/// followed by compressed subcode data.
///
/// ```c
/// [Frame0, Frame1, ..., FrameN, Subcode0, Subcode1, ..., SubcodeN]
/// ```
/// Unlike CDLZ or CDZL, there is no header before the compressed data begins.
/// The length of the compressed data is determined by the number of 2448-sized frames
/// that can fit into the hunk-sized output buffer. Following the FLAC compressed blocks,
/// the subcode data is a single Deflate stream.
///
/// After decompression, the data is swizzled so that each frame is followed by its corresponding
/// subcode data.
/// ```c
/// [Frame0, Subcode0, Frame1, Subcode1, ..., FrameN, SubcodeN]
/// ```
/// FLAC compressed frames does not require manual reconstruction of the sync header or ECC bytes.
///
/// ## Buffer Restrictions
/// Each compressed CDFL hunk decompresses to a hunk-sized chunk. The hunk size must be a multiple
/// of 2448, the size of each CD frame. The input buffer must contain enough samples to fill
/// the number of CD sectors that can fit into the output buffer.
pub struct CdFlacCodec {
    // cdfl always writes in big endian.
    engine: FlacCodec<BigEndian>,
    sub_engine: ZlibCodec,
    buffer: Vec<u8>,
}

impl CompressionCodec for CdFlacCodec {}

impl CompressionCodecType for CdFlacCodec {
    fn codec_type(&self) -> CodecType {
        CodecType::FlacCdV5
    }
}

impl CodecImplementation for CdFlacCodec {
    fn new(hunk_size: u32) -> Result<Self>
    where
        Self: Sized,
    {
        if hunk_size % CD_FRAME_SIZE != 0 {
            return Err(Error::CodecError);
        }

        // The size of the FLAC data in each cdfl hunk, excluding the subcode data.
        let max_frames = hunk_size / CD_FRAME_SIZE;
        let flac_data_size = max_frames * CD_MAX_SECTOR_DATA;

        // neither FlacCodec nor ZlibCodec actually make use of hunk_size.
        Ok(CdFlacCodec {
            engine: FlacCodec::new(flac_data_size)?,
            sub_engine: ZlibCodec::new(hunk_size)?,
            buffer: vec![0u8; hunk_size as usize],
        })
    }

    fn decompress(&mut self, input: &[u8], output: &mut [u8]) -> Result<DecompressResult> {
        let total_frames = output.len() / CD_FRAME_SIZE as usize;
        let frame_res = self.engine.decompress(
            input,
            &mut self.buffer[..total_frames * CD_MAX_SECTOR_DATA as usize],
        )?;

        #[cfg(feature = "want_subcode")]
        let sub_res = self.sub_engine.decompress(
            &input[frame_res.total_in()..],
            &mut self.buffer[total_frames * CD_MAX_SECTOR_DATA as usize..]
                [..total_frames * CD_MAX_SUBCODE_DATA as usize],
        )?;

        #[cfg(not(feature = "want_subcode"))]
        let sub_res = DecompressResult::default();

        // Decompressed FLAC data has layout
        // [Frame0, Frame1, ..., FrameN, Subcode0, Subcode1, ..., SubcodeN]
        // We need to reassemble the data to be
        // [Frame0, Subcode0, Frame1, Subcode1, ..., FrameN, SubcodeN]

        // Reassemble frame data to expected layout.
        for (frame_num, chunk) in self.buffer[..total_frames * CD_MAX_SECTOR_DATA as usize]
            .chunks_exact(CD_MAX_SECTOR_DATA as usize)
            .enumerate()
        {
            output[frame_num * CD_FRAME_SIZE as usize..][..CD_MAX_SECTOR_DATA as usize]
                .copy_from_slice(chunk);
        }

        // Reassemble subcode data to expected layout.
        #[cfg(feature = "want_subcode")]
        for (frame_num, chunk) in self.buffer[total_frames * CD_MAX_SECTOR_DATA as usize..]
            .chunks_exact(CD_MAX_SUBCODE_DATA as usize)
            .enumerate()
        {
            output[frame_num * CD_FRAME_SIZE as usize + CD_MAX_SECTOR_DATA as usize..]
                [..CD_MAX_SUBCODE_DATA as usize]
                .copy_from_slice(chunk);
        }

        Ok(frame_res + sub_res)
    }
}