rustwav_core/
wavcore.rs

1#![allow(dead_code)]
2
3use std::{
4    collections::{BTreeMap, HashMap},
5    convert::From,
6    fmt::{self, Debug, Display, Formatter},
7    io::{self, Read, SeekFrom, Write},
8};
9
10use sampletypes::SampleType;
11use savagestr::{SavageStringCodecs, StringCodecMaps};
12use downmixer::*;
13use io_utils::{Reader, Writer, string_io::*};
14use crate::errors::{AudioError, AudioReadError, AudioWriteError};
15
16use mp3::*;
17use opus::*;
18use flac::*;
19use oggvorbis::*;
20
21/// * Specify the audio codecs of the WAV file.
22#[derive(Debug, Clone, PartialEq)]
23#[allow(clippy::large_enum_variant)]
24pub enum DataFormat {
25    /// * This is used for creating a new `DataFormat` to specify an `unknown` format.
26    Unspecified,
27
28    /// * PCM format, supports `u8`, `i16`, `i24`, `i32`, `f32`, `f64` for WAV, supports channel number >= 2, no compresion, lossless.
29    Pcm,
30
31    /// * ADPCM format, every sample stores as nibbles (One 4-bit nibble for a 16-bit sample), max channels is 2, lossy. Good for voice chatting, and very small memory usage.
32    Adpcm(AdpcmSubFormat),
33
34    /// * PCM-aLaw, every sample stores as a byte (One byte for a 16-bit sample), max channels is 2, lossy. Encoding/decoding is by table lookup. These tables are not that small.
35    /// * Kind of useless. I prefer just to use the plain `u8` PCM format to replace it. My supreme audio card can handle my `u8` PCM and the playback is just as perfect as `i16` does.
36    PcmALaw,
37
38    /// * PCM-MuLaw. Not much different than the PCM-aLaw. Uses a different algorithm to encode.
39    PcmMuLaw,
40
41    /// * MP3. Just a pure MP3 file encapsulated in the `data` chunk. It needs some extra extension data in the `fmt ` chunk.
42    /// * With the help of the WAV `fmt ` chunk, you can get the spec of the audio file without decoding it first.
43    /// * The WAV file which encapsulates the MP3 file as its content, the size of the WAV file looks like an MP3 file size.
44    Mp3(Mp3EncoderOptions),
45
46    /// * Naked opus stream, without the Ogg container. The encoded data is stored as blocks in the `data` chunk, the block size is stored in the `fmt ` chunk.
47    /// * Just take a look at the blocks, there are lots of zero bytes at the end, indicating that the Opus format is excellent at compressing the audio data.
48    /// * But WAV can't get rid of these zero bytes, resulting in the compression ratio just like encoding each sample into a byte.
49    /// * Opus was originally designed for low-lag digital audio transmission with good quality. Encapsulating this thing into a WAV file is very weird.
50    Opus(OpusEncoderOptions),
51
52    /// * FLAC. Just a pure FLAC file encapsulated in the `data` chunk.
53    /// * With the help of the WAV `fmt ` chunk, you can get the spec of the audio file without decoding it first.
54    /// * The WAV file which encapsulates the FLAC file as its content, the size of the WAV file looks like an FLAC file size.
55    Flac(FlacEncoderParams),
56
57    /// * OggVorbis. Just a pure OggVorbis file encapsulated in the `data` chunk.
58    /// * The WAV file which encapsulates the OggVorbis file as its content, the size of the WAV file looks like an OggVorbis file size.
59    OggVorbis(OggVorbisEncoderParams),
60}
61
62/// * When to encode audio to ADPCM format, choose one of the subformats.
63/// * The value of the subformat is the `format_tag` field of the `fmt ` chunk.
64#[derive(Debug, Clone, Copy, PartialEq)]
65#[repr(u16)]
66pub enum AdpcmSubFormat {
67    /// * This is for ADPCM-MS
68    Ms = 0x0002,
69
70    /// * This is for ADPCM-IMA
71    Ima = 0x0011,
72
73    /// * This is for ADPCM-YAMAHA. The Yamaha ADPCM algorithm is the easiest one to implement.
74    Yamaha = 0x0020,
75}
76
77impl From<AdpcmSubFormat> for u16 {
78    fn from(val: AdpcmSubFormat) -> Self {
79        val as u16
80    }
81}
82
83impl Display for DataFormat {
84    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
85        match self {
86            Self::Unspecified => write!(f, "Unspecified"),
87            Self::Pcm => write!(f, "PCM"),
88            Self::Adpcm(subformat) => write!(f, "{:?}", subformat),
89            Self::PcmALaw => write!(f, "PCM-ALaw"),
90            Self::PcmMuLaw => write!(f, "PCM-MuLaw"),
91            Self::Mp3(options) => write!(f, "MP3({:?})", options),
92            Self::Opus(options) => write!(f, "Opus({:?})", options),
93            Self::Flac(options) => write!(f, "Flac({:?})", options),
94            Self::OggVorbis(options) => write!(f, "OggVorbis({:?})", options),
95        }
96    }
97}
98
99impl Display for AdpcmSubFormat {
100    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
101        match self {
102            Self::Ms => write!(f, "ADPCM-MS"),
103            Self::Ima => write!(f, "ADPCM-IMA"),
104            Self::Yamaha => write!(f, "ADPCM-YAMAHA"),
105        }
106    }
107}
108
109pub mod format_tags {
110    pub const FORMAT_TAG_PCM          : u16 = 0x0001;
111    pub const FORMAT_TAG_ADPCM_MS     : u16 = 0x0002;
112    pub const FORMAT_TAG_PCM_IEEE     : u16 = 0x0003;
113    pub const FORMAT_TAG_ALAW         : u16 = 0x0006;
114    pub const FORMAT_TAG_MULAW        : u16 = 0x0007;
115    pub const FORMAT_TAG_ADPCM_IMA    : u16 = 0x0011;
116    pub const FORMAT_TAG_ADPCM_IMA_   : u16 = 0x0067;
117    pub const FORMAT_TAG_ADPCM_YAMAHA : u16 = 0x0020;
118    pub const FORMAT_TAG_MP3          : u16 = 0x0055;
119    pub const FORMAT_TAG_OPUS         : u16 = 0x704F;
120    pub const FORMAT_TAG_OGG_VORBIS1  : u16 = ('O' as u16) | (('g' as u16) << 8);
121    pub const FORMAT_TAG_OGG_VORBIS2  : u16 = ('P' as u16) | (('g' as u16) << 8);
122    pub const FORMAT_TAG_OGG_VORBIS3  : u16 = ('Q' as u16) | (('g' as u16) << 8);
123    pub const FORMAT_TAG_OGG_VORBIS1P : u16 = ('o' as u16) | (('g' as u16) << 8);
124    pub const FORMAT_TAG_OGG_VORBIS2P : u16 = ('p' as u16) | (('g' as u16) << 8);
125    pub const FORMAT_TAG_OGG_VORBIS3P : u16 = ('q' as u16) | (('g' as u16) << 8);
126    pub const FORMAT_TAG_VORBIS       : u16 = ('o' as u16) | (('V' as u16) << 8);
127    pub const FORMAT_TAG_FLAC         : u16 = 0xF1AC;
128    pub const FORMAT_TAG_EXTENSIBLE   : u16 = 0xFFFE;
129}
130
131#[allow(unused_imports)]
132pub use format_tags::*;
133
134/// * The rough type of the sample format.
135#[derive(Debug, Clone, Copy)]
136pub enum SampleFormat {
137    Unknown,
138
139    /// * IEEE 754 floaing number including `f32` and `f64`
140    Float,
141
142    /// * Unsigned integer
143    UInt,
144
145    /// * Signed integer
146    Int,
147}
148
149impl Display for SampleFormat {
150    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
151        match self {
152            SampleFormat::Unknown => write!(f, "Unknown"),
153            SampleFormat::Float => write!(f, "Floating Point Number"),
154            SampleFormat::UInt => write!(f, "Unsigned Integer"),
155            SampleFormat::Int => write!(f, "Integer"),
156        }
157    }
158}
159
160/// * The concrete type of the sample format.
161#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
162pub enum WaveSampleType {
163    Unknown,
164    S8,
165    S16,
166    S24,
167    S32,
168    S64,
169    U8,
170    U16,
171    U24,
172    U32,
173    U64,
174    F32,
175    F64,
176}
177
178impl Display for WaveSampleType {
179    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
180        use WaveSampleType::{F32, F64, S8, S16, S24, S32, S64, U8, U16, U24, U32, U64, Unknown};
181        match self {
182            Unknown => write!(f, "Unknown"),
183            S8  => write!(f, "i8"),
184            S16 => write!(f, "i16"),
185            S24 => write!(f, "i24"),
186            S32 => write!(f, "i32"),
187            S64 => write!(f, "i64"),
188            U8  => write!(f, "u8"),
189            U16 => write!(f, "u16"),
190            U24 => write!(f, "u24"),
191            U32 => write!(f, "u32"),
192            U64 => write!(f, "u64"),
193            F32 => write!(f, "f32"),
194            F64 => write!(f, "f64"),
195        }
196    }
197}
198
199impl WaveSampleType {
200    pub fn sizeof(&self) -> u16 {
201        use WaveSampleType::{F32, F64, S8, S16, S24, S32, S64, U8, U16, U24, U32, U64, Unknown};
202        match self {
203            S8 =>  1,
204            S16 => 2,
205            S24 => 3,
206            S32 => 4,
207            S64 => 8,
208            U8 =>  1,
209            U16 => 2,
210            U24 => 3,
211            U32 => 4,
212            U64 => 8,
213            F32 => 4,
214            F64 => 8,
215            Unknown => 0,
216        }
217    }
218}
219
220#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
221#[allow(clippy::upper_case_acronyms)]
222pub struct GUID(pub u32, pub u16, pub u16, pub [u8; 8]);
223
224impl Debug for GUID {
225    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
226        f.debug_tuple("GUID")
227            .field(&format_args!(
228                "{:08x}-{:04x}-{:04x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
229                self.0,
230                self.1,
231                self.2,
232                self.3[0],
233                self.3[1],
234                self.3[2],
235                self.3[3],
236                self.3[4],
237                self.3[5],
238                self.3[6],
239                self.3[7]
240            ))
241            .finish()
242    }
243}
244
245impl Display for GUID {
246    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
247        <GUID as Debug>::fmt(self, f)
248    }
249}
250
251impl GUID {
252    pub fn read<T>(r: &mut T) -> io::Result<Self>
253    where
254        T: Read,
255    {
256        Ok(Self(
257            u32::read_le(r)?,
258            u16::read_le(r)?,
259            u16::read_le(r)?,
260            [
261                u8::read_le(r)?,
262                u8::read_le(r)?,
263                u8::read_le(r)?,
264                u8::read_le(r)?,
265                u8::read_le(r)?,
266                u8::read_le(r)?,
267                u8::read_le(r)?,
268                u8::read_le(r)?,
269            ],
270        ))
271    }
272
273    pub fn write<T>(&self, w: &mut T) -> io::Result<()>
274    where
275        T: Write + ?Sized,
276    {
277        self.0.write_le(w)?;
278        self.1.write_le(w)?;
279        self.2.write_le(w)?;
280        w.write_all(&self.3)?;
281        Ok(())
282    }
283}
284
285pub mod guids {
286    pub use super::GUID;
287
288    pub const GUID_PCM_FORMAT: GUID =        GUID(0x00000001, 0x0000, 0x0010, [0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71]);
289    pub const GUID_IEEE_FLOAT_FORMAT: GUID = GUID(0x00000003, 0x0000, 0x0010, [0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71]);
290}
291
292pub use guids::*;
293
294/// * The spec info for a generic audio file.
295#[derive(Debug, Clone, Copy)]
296pub struct Spec {
297    /// * Num channels
298    pub channels: u16,
299
300    /// * The channel mask indicates the position of the speakers.
301    pub channel_mask: u32,
302
303    /// * The sample rate. How many audio frames are to be played in a second.
304    pub sample_rate: u32,
305
306    /// * For PCM, this indicates how many bits is for a sample.
307    pub bits_per_sample: u16,
308
309    /// * The roughly described sample format
310    pub sample_format: SampleFormat,
311}
312
313impl Default for Spec {
314    fn default() -> Self {
315        Self::new()
316    }
317}
318
319/// * Infer the concrete type of the sample format from some rough data
320#[allow(unused_imports)]
321pub fn get_sample_type(bits_per_sample: u16, sample_format: SampleFormat) -> WaveSampleType {
322    use SampleFormat::{Float, Int, UInt};
323    use WaveSampleType::{F32, F64, S8, S16, S24, S32, S64, U8, U16, U24, U32, U64, Unknown};
324    match (bits_per_sample, sample_format) {
325        (8, UInt) => U8,
326        (16, Int) => S16,
327        (24, Int) => S24,
328        (32, Int) => S32,
329        (64, Int) => S64,
330        (32, Float) => F32,
331        (64, Float) => F64,
332        // WAV PCM supports only the formats listed above.
333        (_, _) => Unknown,
334    }
335}
336
337impl Spec {
338    pub fn new() -> Self {
339        Self {
340            channels: 0,
341            channel_mask: 0,
342            sample_rate: 0,
343            bits_per_sample: 0,
344            sample_format: SampleFormat::Unknown,
345        }
346    }
347
348    /// * Get the concrete sample type
349    pub fn get_sample_type(&self) -> WaveSampleType {
350        get_sample_type(self.bits_per_sample, self.sample_format)
351    }
352
353    /// * Guess the channel mask
354    pub fn guess_channel_mask(&self) -> Result<u32, AudioError> {
355        Ok(speaker_positions::guess_channel_mask(self.channels)?)
356    }
357
358    /// * Break down a channel mask to the speaker positions.
359    pub fn channel_mask_to_speaker_positions(&self) -> Vec<u32> {
360        speaker_positions::channel_mask_to_speaker_positions(self.channel_mask)
361    }
362
363    /// * Break down a channel mask to the speaker position description strings.
364    pub fn channel_mask_to_speaker_positions_descs(&self) -> Vec<&'static str> {
365        speaker_positions::channel_mask_to_speaker_positions_descs(self.channel_mask)
366    }
367
368    /// * Check if this spec is good for encoding PCM format.
369    pub fn verify_for_pcm(&self) -> Result<(), AudioError> {
370        self.guess_channel_mask()?;
371        if self.get_sample_type() == WaveSampleType::Unknown {
372            Err(AudioError::InvalidArguments(format!(
373                "PCM doesn't support {} bits per sample {:?}",
374                self.bits_per_sample, self.sample_format
375            )))
376        } else {
377            Ok(())
378        }
379    }
380
381    /// * Check if the channel mask matches the channel number.
382    pub fn is_channel_mask_valid(&self) -> bool {
383        speaker_positions::is_channel_mask_valid(self.channels, self.channel_mask)
384    }
385}
386
387/// * The WAV chunk writer used by `WaveWriter`
388/// * It remembers the chunk header field positions.
389/// * When it gets out of the scope or to be dropped, it updates the field of the chunk header.
390pub struct ChunkWriter<'a> {
391    pub writer: &'a mut dyn Writer,
392    pub flag: [u8; 4],
393    pub pos_of_chunk_len: u64, // Byte position where the chunk size field is written (to be backfilled later)
394    pub chunk_start: u64,      // File offset marking the start of this chunk's payload data
395    ended: bool,
396}
397
398impl Debug for ChunkWriter<'_> {
399    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
400        fmt.debug_struct("ChunkWriter")
401            .field("writer", &self.writer)
402            .field(
403                "flag",
404                &format_args!("{}", String::from_utf8_lossy(&self.flag)),
405            )
406            .field("pos_of_chunk_len", &format_args!("0x{:x}", self.pos_of_chunk_len))
407            .field("chunk_start", &format_args!("0x{:x}", self.chunk_start))
408            .field("ended", &self.ended)
409            .finish()
410    }
411}
412
413impl<'a> ChunkWriter<'a> {
414    // Starts writing a chunk by first writing the chunk Flag, then recording the positions
415    // for both the chunk size and the chunk data.
416    pub fn begin(writer: &'a mut dyn Writer, flag: &[u8; 4]) -> Result<Self, AudioWriteError> {
417        writer.write_all(flag)?;
418        let pos_of_chunk_len = writer.stream_position()?;
419        0u32.write_le(writer)?;
420        let chunk_start = writer.stream_position()?;
421        Ok(Self {
422            writer,
423            flag: *flag,
424            pos_of_chunk_len,
425            chunk_start,
426            ended: false,
427        })
428    }
429
430    /// * At the end of the chunk, the chunk size will be updated since the ownership of `self` moved there, and `drop()` will be called.
431    pub fn end(self) {}
432
433    fn on_drop(&mut self) -> Result<(), AudioWriteError> {
434        if self.ended {
435            return Ok(());
436        }
437        // Chunk size handling constraints:
438        // ---------------------------------------------------------------
439        // 1. u32 Overflow Handling:
440        //    - RIFF/RF64/data chunks: If size exceeds u32::MAX (0xFFFFFFFF),
441        //      write 0xFFFFFFFF and store the actual u64 size in the ds64 chunk.
442        //    - Other chunks: Size must not exceed u32::MAX. If violated, returns an error.
443        //
444        // 2. ds64 Table Limitations:
445        //    - The ds64 chunk's table can store u64 sizes for non-RIFF/RF64/data chunks,
446        //      but adding entries increases the ds64 chunk's own size.
447        //    - Current implementation does NOT pre-allocate space in the ds64 chunk.
448        //
449        // 3. Enforcement:
450        //    - Non-RIFF/RF64/data chunks exceeding 0xFFFFFFFF bytes will fail encoding.
451        //    - Callers must ensure chunks (except RIFF/RF64/data) stay within u32 limits.
452        let mut chunk_size = self.get_chunk_data_size()?;
453        if chunk_size >= 0xFFFFFFFFu64 {
454            match &self.flag {
455                b"RIFF" | b"data" => {
456                    chunk_size = 0xFFFFFFFF;
457                }
458                other => {
459                    let chunk_flag = String::from_utf8_lossy(other);
460                    return Err(AudioWriteError::ChunkSizeTooBig(format!(
461                        "{} is 0x{:x} bytes long.",
462                        chunk_flag, chunk_size
463                    )));
464                }
465            }
466        }
467        self.end_and_write_size(chunk_size as u32)
468    }
469
470    fn end_and_write_size(&mut self, chunk_size_to_write: u32) -> Result<(), AudioWriteError> {
471        let end_of_chunk = self.writer.stream_position()?;
472        self.writer.seek(SeekFrom::Start(self.pos_of_chunk_len))?;
473        chunk_size_to_write.write_le(self.writer)?;
474        self.writer.seek(SeekFrom::Start(end_of_chunk))?;
475        if end_of_chunk & 1 > 0 {
476            0u8.write_le(self.writer)?;
477        } // Alignment
478        self.ended = true;
479        Ok(())
480    }
481
482    /// * For you to write data from the start of the chunk, here's a method for you to get the offset.
483    pub fn get_chunk_start_pos(&self) -> u64 {
484        self.chunk_start
485    }
486
487    /// * A method to calculate currently how big the chunk is.
488    pub fn get_chunk_data_size(&mut self) -> Result<u64, AudioWriteError> {
489        Ok(self.writer.stream_position()? - self.get_chunk_start_pos())
490    }
491}
492
493impl Drop for ChunkWriter<'_> {
494    fn drop(&mut self) {
495        self.on_drop().unwrap()
496    }
497}
498
499/// * This thing is for reading a chunk
500#[derive(Clone, Copy)]
501pub struct ChunkHeader {
502    /// * The 4-byte identifier stored in the file (e.g., "RIFF", "fmt ")
503    pub flag: [u8; 4],
504
505    /// * The chunk size stored in the file header (may be 0xFFFFFFFF if actual size is in ds64 chunk)
506    pub size: u32,
507
508    /// * File offset of the chunk's payload (excludes the 8-byte header)
509    pub chunk_start_pos: u64,
510}
511
512impl ChunkHeader {
513    pub fn new() -> Self {
514        Self {
515            flag: [0u8; 4],
516            size: 0,
517            chunk_start_pos: 0,
518        }
519    }
520
521    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
522        let mut ret = Self::new();
523        reader.read_exact(&mut ret.flag)?;
524        ret.size = u32::read_le(reader)?;
525        ret.chunk_start_pos = reader.stream_position()?;
526        Ok(ret)
527    }
528
529    pub fn read_unseekable(
530        reader: &mut impl Reader,
531        cur_pos: &mut u64,
532    ) -> Result<Self, AudioReadError> {
533        let mut ret = Self::new();
534        reader.read_exact(&mut ret.flag)?;
535        ret.size = u32::read_le(reader)?;
536        *cur_pos += 8;
537        ret.chunk_start_pos = *cur_pos;
538        Ok(ret)
539    }
540
541    /// * Calculate alignment
542    pub fn align(addr: u64) -> u64 {
543        addr + (addr & 1)
544    }
545
546    /// * Calculate the position of the next chunk
547    pub fn next_chunk_pos(&self) -> u64 {
548        Self::align(self.chunk_start_pos + self.size as u64)
549    }
550
551    /// * Seek to the next chunk (with alignment)
552    pub fn seek_to_next_chunk(&self, reader: &mut impl Reader) -> Result<u64, AudioReadError> {
553        Ok(reader.seek(SeekFrom::Start(self.next_chunk_pos()))?)
554    }
555
556    /// * If can't seek, do some dummy reads to the next chunk (with alignment)
557    pub fn goto_next_chunk_unseekable(
558        &self,
559        reader: &mut impl Reader,
560        cur_pos: &mut u64,
561    ) -> Result<u64, AudioReadError> {
562        Ok(io_utils::goto_offset_without_seek(
563            reader,
564            cur_pos,
565            self.next_chunk_pos(),
566        )?)
567    }
568}
569
570impl Debug for ChunkHeader {
571    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
572        f.debug_struct("ChunkHeader")
573        .field("flag", &String::from_utf8_lossy(&self.flag).into_owned())
574        .field("size", &format_args!("0x{:x}", self.size))
575        .field("chunk_start_pos", &format_args!("0x{:x}", self.chunk_start_pos))
576        .finish()
577    }
578}
579
580impl Default for ChunkHeader {
581    fn default() -> Self {
582        Self::new()
583    }
584}
585
586/// * The `fmt ` chunk for the WAV file.
587#[derive(Debug, Clone)]
588pub struct FmtChunk {
589    /// * See <https://github.com/tpn/winsdk-10/blob/master/Include/10.0.14393.0/shared/mmreg.h>
590    pub format_tag: u16,
591
592    /// * Num channels
593    pub channels: u16,
594
595    /// * Sample rate. It's actually `frame_rate` because it's the rate of how frequently the audio frames are played.
596    /// * Each audio frame contains samples for each channel. For example, your audio file has two samples, which means an audio frame is two samples for each channel.
597    /// * For another example, if the sample rate is 44100, but the channels are 2, it plays 88200 samples per second.
598    pub sample_rate: u32,
599
600    /// * How many bytes are to be played in a second. This field is important because lots of audio players use this field to calculate the playback progress.
601    pub byte_rate: u32,
602
603    /// * Block size. For PCM, it's sample size in bytes times to channel number. For non-PCM, it's the block size for the audio blocks.
604    /// * The block size field is for quickly `seek()` the audio file.
605    pub block_align: u16,
606
607    /// * For PCM, this indicates how many bits are in a sample. For non-PCM, this field is either zero or some other meaningful value for the encoded format.
608    pub bits_per_sample: u16,
609
610    /// * The extension block for the `fmt ` chunk, its type depends on the `format_tag` value.
611    pub extension: Option<FmtExtension>,
612}
613
614/// * The `fmt ` chunk extension block
615#[derive(Debug, Clone)]
616pub struct FmtExtension {
617    /// * Extension block size
618    pub ext_len: u16,
619
620    /// * Extension block data
621    pub data: ExtensionData,
622}
623
624/// * Extension block data
625#[derive(Debug, Clone)]
626pub enum ExtensionData {
627    /// * If the extension block size is zero, here we have `Nodata` for it.
628    Nodata,
629
630    /// * ADPCM-MS specified extension data. Anyway, the decoder can generate the data if the format is ADPCM-MS and there's no data for it.
631    AdpcmMs(AdpcmMsData),
632
633    /// * ADPCM-IMA specified extension data. Kind of useless.
634    AdpcmIma(AdpcmImaData),
635
636    /// * MP3 specified extension data.
637    Mp3(Mp3Data),
638
639    /// * Naked Vorbis header data
640    Vorbis(VorbisHeaderData),
641
642    /// * OggVorbis specified extension data.
643    OggVorbis(OggVorbisData),
644
645    /// * Another OggVorbis specified extension data.
646    OggVorbisWithHeader(OggVorbisWithHeaderData),
647
648    /// * Extensible data, it has channel mask, GUID for formats, etc, dedicated for multi-channel PCM format.
649    Extensible(ExtensibleData),
650}
651
652#[derive(Debug, Clone, Copy)]
653pub struct AdpcmCoeffSet {
654    pub coeff1: i16,
655    pub coeff2: i16,
656}
657
658impl AdpcmCoeffSet {
659    pub fn new() -> Self {
660        Self {
661            coeff1: 0,
662            coeff2: 0,
663        }
664    }
665
666    pub fn get(&self, index: usize) -> i16 {
667        match index {
668            1 => self.coeff1,
669            2 => self.coeff2,
670            o => panic!("Index must be 1 or 2, not {o}"),
671        }
672    }
673}
674
675/// * The extension data for ADPCM-MS
676#[derive(Debug, Clone, Copy)]
677pub struct AdpcmMsData {
678    pub samples_per_block: u16,
679    pub num_coeff: u16,
680    pub coeffs: [AdpcmCoeffSet; 7],
681}
682
683/// * The extension data for ADPCM-IMA
684#[derive(Debug, Clone, Copy)]
685pub struct AdpcmImaData {
686    pub samples_per_block: u16,
687}
688
689/// * The extension data for MP3
690#[derive(Debug, Clone, Copy)]
691pub struct Mp3Data {
692    pub id: u16,
693    pub flags: u32,
694    pub block_size: u16,
695    pub frames_per_block: u16,
696    pub codec_delay: u16,
697}
698
699/// * The extension data for Naked vorbis audio without Ogg stream encapsulation
700#[derive(Default, Clone)]
701pub struct VorbisHeaderData {
702    /// The header for the Vorbis audio
703    pub header: Vec<u8>,
704}
705
706impl VorbisHeaderData {
707    pub fn new(header: &[u8]) -> Self {
708        Self {
709            header: header.to_vec(),
710        }
711    }
712
713    pub fn sizeof(&self) -> usize {
714        self.header.len()
715    }
716
717    pub fn read(reader: &mut impl Reader, ext_len: u16) -> Result<Self, AudioReadError> {
718        let mut buf = vec![0u8; ext_len as usize];
719        reader.read_exact(&mut buf)?;
720        Ok(Self::new(&buf))
721    }
722
723    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
724        writer.write_all(&self.header)?;
725        Ok(())
726    }
727}
728
729impl Debug for VorbisHeaderData {
730    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
731        f.debug_struct("VorbisHeaderData")
732        .field("header", &format_args!("[u8; {}]", self.header.len()))
733        .finish()
734    }
735}
736
737/// * The extension data for OggVorbis
738#[derive(Clone, Copy)]
739pub struct OggVorbisData {
740    /// * The codec version. I'm coding this thing at 2025/5/6, so this filed for our encoded WAV file should be 0x20250506
741    pub codec_version: u32,
742
743    /// * The `libvorbis` version, our `rustwav` depends on `vorbis_rs 0.5.5`, which uses `vorbis-sys`, which uses `libvorbis 1.3.7 20200704`
744    /// * So this field must be 0x20200704 for our encoded WAV file.
745    pub vorbis_version: u32,
746}
747
748impl Debug for OggVorbisData {
749    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
750        f.debug_struct("OggVorbisWithHeaderData")
751        .field("codec_version", &format_args!("{:x}/{:x}/{:x}", self.codec_version >> 16, (self.codec_version >> 8) & 0xFF, self.codec_version & 0xFF))
752        .field("vorbis_version", &format_args!("{:x}/{:x}/{:x}", self.vorbis_version >> 16, (self.vorbis_version >> 8) & 0xFF, self.vorbis_version & 0xFF))
753        .finish()
754    }
755}
756
757/// * The another extension data for OggVorbis
758#[derive(Clone)]
759pub struct OggVorbisWithHeaderData {
760    /// * The codec version. I'm coding this thing at 2025/5/6, so this filed for our encoded WAV file should be 0x20250506
761    pub codec_version: u32,
762
763    /// * The `libvorbis` version, our `rustwav` depends on `vorbis_rs 0.5.5`, which uses `vorbis-sys`, which uses `libvorbis 1.3.7 20200704`
764    /// * So this field must be 0x20200704 for our encoded WAV file.
765    pub vorbis_version: u32,
766
767    /// * The OggVorbis header data
768    pub header: Vec<u8>,
769}
770
771impl Debug for OggVorbisWithHeaderData {
772    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
773        f.debug_struct("OggVorbisWithHeaderData")
774        .field("codec_version", &format_args!("{:x}/{:x}/{:x}", self.codec_version >> 16, (self.codec_version >> 8) & 0xFF, self.codec_version & 0xFF))
775        .field("vorbis_version", &format_args!("{:x}/{:x}/{:x}", self.vorbis_version >> 16, (self.vorbis_version >> 8) & 0xFF, self.vorbis_version & 0xFF))
776        .field("header", &format_args!("[u8; {}]", self.header.len()))
777        .finish()
778    }
779}
780
781/// * The extension data for extensible.
782#[derive(Debug, Clone, Copy)]
783pub struct ExtensibleData {
784    /// * Valid bits per sample
785    pub valid_bits_per_sample: u16,
786
787    /// * This is for multi-channel speaker position masks, see `struct Spec`
788    pub channel_mask: u32,
789
790    /// * This field indicates the exact format for the PCM samples.
791    pub sub_format: GUID,
792}
793
794impl FmtChunk {
795    pub fn new() -> Self {
796        Self {
797            format_tag: 0,
798            channels: 0,
799            sample_rate: 0,
800            byte_rate: 0,
801            block_align: 0,
802            bits_per_sample: 0,
803            extension: None,
804        }
805    }
806
807    pub fn read(reader: &mut impl Reader, chunk_size: u32) -> Result<Self, AudioReadError> {
808        let mut ret = FmtChunk {
809            format_tag: u16::read_le(reader)?,
810            channels: u16::read_le(reader)?,
811            sample_rate: u32::read_le(reader)?,
812            byte_rate: u32::read_le(reader)?,
813            block_align: u16::read_le(reader)?,
814            bits_per_sample: u16::read_le(reader)?,
815            extension: None,
816        };
817        if chunk_size > 16 {
818            ret.extension = Some(FmtExtension::read(reader, &ret)?);
819        }
820        Ok(ret)
821    }
822
823    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
824        self.format_tag.write_le(writer)?;
825        self.channels.write_le(writer)?;
826        self.sample_rate.write_le(writer)?;
827        self.byte_rate.write_le(writer)?;
828        self.block_align.write_le(writer)?;
829        self.bits_per_sample.write_le(writer)?;
830        if let Some(extension) = &self.extension {
831            extension.write(writer)?;
832        }
833        Ok(())
834    }
835
836    pub fn get_sample_format(&self) -> SampleFormat {
837        use SampleFormat::{Float, Int, UInt, Unknown};
838        match (self.format_tag, self.bits_per_sample) {
839            (1, 8) => UInt,
840            (1, 16) => Int,
841            (1, 24) => Int,
842            (1, 32) => Int,
843            (1, 64) => Int,
844            (0xFFFE, 8) => UInt,
845            (0xFFFE, 16) => Int,
846            (0xFFFE, 24) => Int,
847            (0xFFFE, 32) | (0xFFFE, 64) => {
848                if let Some(extension) = &self.extension {
849                    match &extension.data {
850                        ExtensionData::Extensible(extensible) => {
851                            match extensible.sub_format {
852                                GUID_PCM_FORMAT => Int,
853                                GUID_IEEE_FLOAT_FORMAT => Float,
854                                _ => Unknown, // Let the decoders to decide
855                            }
856                        }
857                        other => {
858                            panic!("Unexpected extension data in the `fmt ` chunk: {:?}", other)
859                        }
860                    }
861                } else {
862                    Int
863                }
864            }
865            (3, 32) => Float,
866            (3, 64) => Float,
867            (_, _) => Unknown, // Let the decoders to decide
868        }
869    }
870
871    pub fn get_sample_type(&self) -> WaveSampleType {
872        get_sample_type(self.bits_per_sample, self.get_sample_format())
873    }
874}
875
876impl Default for FmtChunk {
877    fn default() -> Self {
878        Self::new()
879    }
880}
881
882impl FmtExtension {
883    pub fn new_adpcm_ms(adpcm_ms: AdpcmMsData) -> Self {
884        Self {
885            ext_len: AdpcmMsData::sizeof() as u16,
886            data: ExtensionData::AdpcmMs(adpcm_ms),
887        }
888    }
889
890    pub fn new_adpcm_ima(adpcm_ima: AdpcmImaData) -> Self {
891        Self {
892            ext_len: AdpcmImaData::sizeof() as u16,
893            data: ExtensionData::AdpcmIma(adpcm_ima),
894        }
895    }
896
897    pub fn new_mp3(mp3: Mp3Data) -> Self {
898        Self {
899            ext_len: Mp3Data::sizeof() as u16,
900            data: ExtensionData::Mp3(mp3),
901        }
902    }
903
904    pub fn new_vorbis(vorbis: VorbisHeaderData) -> Self {
905        Self {
906            ext_len: vorbis.sizeof() as u16,
907            data: ExtensionData::Vorbis(vorbis),
908        }
909    }
910
911    pub fn new_oggvorbis(oggvorbis: OggVorbisData) -> Self {
912        Self {
913            ext_len: OggVorbisData::sizeof() as u16,
914            data: ExtensionData::OggVorbis(oggvorbis),
915        }
916    }
917
918    pub fn new_oggvorbis_with_header(oggvorbis_with_header: OggVorbisWithHeaderData) -> Self {
919        Self {
920            ext_len: oggvorbis_with_header.sizeof() as u16,
921            data: ExtensionData::OggVorbisWithHeader(oggvorbis_with_header),
922        }
923    }
924
925    pub fn new_extensible(extensible: ExtensibleData) -> Self {
926        Self {
927            ext_len: ExtensibleData::sizeof() as u16,
928            data: ExtensionData::Extensible(extensible),
929        }
930    }
931
932    pub fn get_length(&self) -> u16 {
933        self.ext_len
934    }
935
936    pub fn read(reader: &mut impl Reader, fmt_chunk: &FmtChunk) -> Result<Self, AudioReadError> {
937        let ext_len = u16::read_le(reader)?;
938        Ok(Self {
939            ext_len,
940            data: match fmt_chunk.format_tag {
941                FORMAT_TAG_ADPCM_MS => {
942                    if ext_len as usize >= AdpcmMsData::sizeof() {
943                        Ok(ExtensionData::AdpcmMs(AdpcmMsData::read(reader)?))
944                    } else {
945                        Err(AudioReadError::IncompleteData(format!(
946                            "The extension data for ADPCM-MS should be bigger than {}, got {ext_len}",
947                            AdpcmMsData::sizeof()
948                        )))
949                    }
950                }
951                FORMAT_TAG_ADPCM_IMA => {
952                    if ext_len as usize >= AdpcmImaData::sizeof() {
953                        Ok(ExtensionData::AdpcmIma(AdpcmImaData::read(reader)?))
954                    } else {
955                        Err(AudioReadError::IncompleteData(format!(
956                            "The extension data for ADPCM-IMA should be bigger than {}, got {ext_len}",
957                            AdpcmImaData::sizeof()
958                        )))
959                    }
960                }
961                FORMAT_TAG_MP3 => {
962                    if ext_len as usize >= Mp3Data::sizeof() {
963                        Ok(ExtensionData::Mp3(Mp3Data::read(reader)?))
964                    } else {
965                        Err(AudioReadError::IncompleteData(format!(
966                            "The extension data for Mpeg Layer III should be bigger than {}, got {ext_len}",
967                            Mp3Data::sizeof()
968                        )))
969                    }
970                }
971                FORMAT_TAG_VORBIS => {
972                    Ok(ExtensionData::Vorbis(VorbisHeaderData::read(reader, ext_len)?))
973                }
974                FORMAT_TAG_OGG_VORBIS2 | FORMAT_TAG_OGG_VORBIS2P => {
975                    if ext_len as usize >= OggVorbisWithHeaderData::sizeof_min() {
976                        Ok(ExtensionData::OggVorbisWithHeader(OggVorbisWithHeaderData::read(reader, ext_len)?))
977                    } else {
978                        Err(AudioReadError::IncompleteData(format!(
979                            "The extension data for OggVorbis should be bigger than {}, got {ext_len}",
980                            OggVorbisWithHeaderData::sizeof_min()
981                        )))
982                    }
983                }
984                FORMAT_TAG_OGG_VORBIS1 | FORMAT_TAG_OGG_VORBIS3 | FORMAT_TAG_OGG_VORBIS1P | FORMAT_TAG_OGG_VORBIS3P => {
985                    if ext_len as usize >= OggVorbisData::sizeof() {
986                        Ok(ExtensionData::OggVorbis(OggVorbisData::read(reader)?))
987                    } else {
988                        Err(AudioReadError::IncompleteData(format!(
989                            "The extension data for OggVorbis should be bigger than {}, got {ext_len}",
990                            OggVorbisData::sizeof()
991                        )))
992                    }
993                }
994                FORMAT_TAG_EXTENSIBLE => {
995                    if ext_len as usize >= ExtensibleData::sizeof() {
996                        Ok(ExtensionData::Extensible(ExtensibleData::read(reader)?))
997                    } else if ext_len == 0 {
998                        Ok(ExtensionData::Extensible(ExtensibleData::new(fmt_chunk)?))
999                    } else {
1000                        Err(AudioReadError::IncompleteData(format!(
1001                            "The extension data for EXTENSIBLE should be bigger than {}, got {ext_len}",
1002                            ExtensibleData::sizeof()
1003                        )))
1004                    }
1005                }
1006                _ => Ok(ExtensionData::Nodata),
1007            }?,
1008        })
1009    }
1010
1011    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1012        self.ext_len.write_le(writer)?;
1013        if self.ext_len != 0 {
1014            match &self.data {
1015                ExtensionData::Nodata => Err(AudioWriteError::InvalidArguments(format!(
1016                    "There should be data in {} bytes to be written, but the data is `Nodata`.",
1017                    self.ext_len
1018                ))),
1019                ExtensionData::AdpcmMs(data) => Ok(data.write(writer)?),
1020                ExtensionData::AdpcmIma(data) => Ok(data.write(writer)?),
1021                ExtensionData::Mp3(data) => Ok(data.write(writer)?),
1022                ExtensionData::Vorbis(data) => Ok(data.write(writer)?),
1023                ExtensionData::OggVorbis(data) => Ok(data.write(writer)?),
1024                ExtensionData::OggVorbisWithHeader(data) => Ok(data.write(writer)?),
1025                ExtensionData::Extensible(data) => Ok(data.write(writer)?),
1026            }
1027        } else {
1028            Ok(())
1029        }
1030    }
1031}
1032
1033impl AdpcmMsData {
1034    pub fn new() -> Self {
1035        Self {
1036            samples_per_block: 0,
1037            num_coeff: 7,
1038            coeffs: [
1039                AdpcmCoeffSet{coeff1: 256, coeff2: 0   },
1040                AdpcmCoeffSet{coeff1: 512, coeff2: -256},
1041                AdpcmCoeffSet{coeff1: 0  , coeff2: 0   },
1042                AdpcmCoeffSet{coeff1: 192, coeff2: 64  },
1043                AdpcmCoeffSet{coeff1: 240, coeff2: 0   },
1044                AdpcmCoeffSet{coeff1: 460, coeff2: -208},
1045                AdpcmCoeffSet{coeff1: 392, coeff2: -232},
1046            ],
1047        }
1048    }
1049
1050    pub fn sizeof() -> usize {
1051        32
1052    }
1053
1054    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1055        Ok(Self {
1056            samples_per_block: u16::read_le(reader)?,
1057            num_coeff: u16::read_le(reader)?,
1058            coeffs: [
1059                AdpcmCoeffSet{coeff1: i16::read_le(reader)?, coeff2: i16::read_le(reader)?},
1060                AdpcmCoeffSet{coeff1: i16::read_le(reader)?, coeff2: i16::read_le(reader)?},
1061                AdpcmCoeffSet{coeff1: i16::read_le(reader)?, coeff2: i16::read_le(reader)?},
1062                AdpcmCoeffSet{coeff1: i16::read_le(reader)?, coeff2: i16::read_le(reader)?},
1063                AdpcmCoeffSet{coeff1: i16::read_le(reader)?, coeff2: i16::read_le(reader)?},
1064                AdpcmCoeffSet{coeff1: i16::read_le(reader)?, coeff2: i16::read_le(reader)?},
1065                AdpcmCoeffSet{coeff1: i16::read_le(reader)?, coeff2: i16::read_le(reader)?},
1066            ],
1067        })
1068    }
1069
1070    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1071        self.samples_per_block.write_le(writer)?;
1072        self.num_coeff.write_le(writer)?;
1073        for coeff in self.coeffs {
1074            coeff.coeff1.write_le(writer)?;
1075            coeff.coeff2.write_le(writer)?;
1076        }
1077        Ok(())
1078    }
1079}
1080
1081impl Default for AdpcmMsData {
1082    fn default() -> Self {
1083        Self::new()
1084    }
1085}
1086
1087impl AdpcmImaData {
1088    pub fn new(samples_per_block: u16) -> Self {
1089        Self { samples_per_block }
1090    }
1091
1092    pub fn sizeof() -> usize {
1093        2
1094    }
1095
1096    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1097        Ok(Self {
1098            samples_per_block: u16::read_le(reader)?,
1099        })
1100    }
1101
1102    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1103        self.samples_per_block.write_le(writer)?;
1104        Ok(())
1105    }
1106}
1107
1108impl Mp3Data {
1109    pub const MPEGLAYER3_FLAG_PADDING_ISO: u32 = 0x00000000;
1110    pub const MPEGLAYER3_FLAG_PADDING_ON : u32 = 0x00000001;
1111    pub const MPEGLAYER3_FLAG_PADDING_OFF: u32 = 0x00000002;
1112
1113    pub fn new(bitrate: u32, sample_rate: u32) -> Self {
1114        Self {
1115            id: 1,
1116            flags: Self::MPEGLAYER3_FLAG_PADDING_OFF,
1117            block_size: (144 * bitrate / sample_rate) as u16,
1118            frames_per_block: 1,
1119            codec_delay: 0,
1120        }
1121    }
1122
1123    pub fn sizeof() -> usize {
1124        12
1125    }
1126
1127    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1128        Ok(Self {
1129            id: u16::read_le(reader)?,
1130            flags: u32::read_le(reader)?,
1131            block_size: u16::read_le(reader)?,
1132            frames_per_block: u16::read_le(reader)?,
1133            codec_delay: u16::read_le(reader)?,
1134        })
1135    }
1136
1137    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1138        self.id.write_le(writer)?;
1139        self.flags.write_le(writer)?;
1140        self.block_size.write_le(writer)?;
1141        self.frames_per_block.write_le(writer)?;
1142        self.codec_delay.write_le(writer)?;
1143        Ok(())
1144    }
1145}
1146
1147impl OggVorbisData {
1148    pub fn new() -> Self {
1149        Self {
1150            codec_version: 0x20250506,
1151            vorbis_version: 0x20110424,
1152        }
1153    }
1154
1155    pub fn sizeof() -> usize {
1156        8
1157    }
1158
1159    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1160        Ok(Self {
1161            codec_version: u32::read_le(reader)?,
1162            vorbis_version: u32::read_le(reader)?,
1163        })
1164    }
1165
1166    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1167        self.codec_version.write_le(writer)?;
1168        self.vorbis_version.write_le(writer)?;
1169        Ok(())
1170    }
1171}
1172
1173impl Default for OggVorbisData {
1174    fn default() -> Self {
1175        Self::new()
1176    }
1177}
1178
1179impl OggVorbisWithHeaderData {
1180    pub fn new(header: &[u8]) -> Self {
1181        Self {
1182            codec_version: 0x20250506,
1183            vorbis_version: 0x20200704,
1184            header: header.to_vec(),
1185        }
1186    }
1187
1188    pub fn sizeof_min() -> usize {
1189        8
1190    }
1191
1192    pub fn sizeof(&self) -> usize {
1193        Self::sizeof_min() + self.header.len()
1194    }
1195
1196    pub fn read(reader: &mut impl Reader, ext_len: u16) -> Result<Self, AudioReadError> {
1197        let mut ret = Self {
1198            codec_version: u32::read_le(reader)?,
1199            vorbis_version: u32::read_le(reader)?,
1200            header: vec![0u8; ext_len as usize - Self::sizeof_min()],
1201        };
1202        reader.read_exact(&mut ret.header)?;
1203        Ok(ret)
1204    }
1205
1206    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1207        self.codec_version.write_le(writer)?;
1208        self.vorbis_version.write_le(writer)?;
1209        writer.write_all(&self.header)?;
1210        Ok(())
1211    }
1212}
1213
1214
1215impl ExtensibleData {
1216    pub fn new(fmt_chunk: &FmtChunk) -> Result<Self, AudioReadError> {
1217        Ok(Self {
1218            valid_bits_per_sample: fmt_chunk.bits_per_sample,
1219            channel_mask: {
1220                let spec = Spec {
1221                    channels: fmt_chunk.channels,
1222                    channel_mask: 0,
1223                    sample_rate: fmt_chunk.sample_rate,
1224                    bits_per_sample: fmt_chunk.bits_per_sample,
1225                    sample_format: SampleFormat::Unknown,
1226                };
1227                spec.guess_channel_mask()?
1228            },
1229            sub_format: GUID_PCM_FORMAT,
1230        })
1231    }
1232
1233    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1234        Ok(Self {
1235            valid_bits_per_sample: u16::read_le(reader)?,
1236            channel_mask: u32::read_le(reader)?,
1237            sub_format: GUID::read(reader)?,
1238        })
1239    }
1240
1241    pub fn sizeof() -> usize {
1242        22
1243    }
1244
1245    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1246        self.valid_bits_per_sample.write_le(writer)?;
1247        self.channel_mask.write_le(writer)?;
1248        self.sub_format.write(writer)?;
1249        Ok(())
1250    }
1251}
1252
1253/// See <https://www.recordingblogs.com/wiki/silent-chunk-of-a-wave-file>
1254#[derive(Debug, Clone, Copy, Default)]
1255pub struct SlntChunk {
1256    /// * The number of samples through which playback should be silent
1257    data: u32,
1258}
1259
1260impl SlntChunk {
1261    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1262        Ok(Self {
1263            data: u32::read_le(reader)?,
1264        })
1265    }
1266
1267    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1268        let cw = ChunkWriter::begin(writer, b"bext")?;
1269        self.data.write_le(cw.writer)?;
1270        Ok(())
1271    }
1272}
1273
1274#[derive(Clone)]
1275pub struct BextChunk {
1276    pub description: String,
1277    pub originator: String,
1278    pub originator_ref: String,
1279    pub origination_date: String,
1280    pub origination_time: String,
1281    pub time_ref: u64,
1282    pub version: u16,
1283    pub umid: [u8; 64],
1284    pub reserved: [u8; 190],
1285    pub coding_history: [u8; 1],
1286}
1287
1288impl BextChunk {
1289    pub fn read(
1290        reader: &mut impl Reader,
1291        text_encoding: &StringCodecMaps,
1292    ) -> Result<Self, AudioReadError> {
1293        let description = read_str(reader, 256, text_encoding)?;
1294        let originator = read_str(reader, 32, text_encoding)?;
1295        let originator_ref = read_str(reader, 32, text_encoding)?;
1296        let origination_date = read_str(reader, 10, text_encoding)?;
1297        let origination_time = read_str(reader, 8, text_encoding)?;
1298        let time_ref = u64::read_le(reader)?;
1299        let version = u16::read_le(reader)?;
1300        let mut umid = [0u8; 64];
1301        let mut reserved = [0u8; 190];
1302        let mut coding_history = [0u8; 1];
1303        reader.read_exact(&mut umid)?;
1304        reader.read_exact(&mut reserved)?;
1305        reader.read_exact(&mut coding_history)?;
1306        Ok(Self {
1307            description,
1308            originator,
1309            originator_ref,
1310            origination_date,
1311            origination_time,
1312            time_ref,
1313            version,
1314            umid,
1315            reserved,
1316            coding_history,
1317        })
1318    }
1319
1320    pub fn write(
1321        &self,
1322        writer: &mut dyn Writer,
1323        text_encoding: &StringCodecMaps,
1324    ) -> Result<(), AudioWriteError> {
1325        let cw = ChunkWriter::begin(writer, b"bext")?;
1326        write_str_sized(cw.writer, &self.description, 256, text_encoding)?;
1327        write_str_sized(cw.writer, &self.originator, 32, text_encoding)?;
1328        write_str_sized(cw.writer, &self.originator_ref, 32, text_encoding)?;
1329        write_str_sized(cw.writer, &self.origination_date, 10, text_encoding)?;
1330        write_str_sized(cw.writer, &self.origination_time, 8, text_encoding)?;
1331        self.time_ref.write_le(cw.writer)?;
1332        self.version.write_le(cw.writer)?;
1333        cw.writer.write_all(&self.umid)?;
1334        cw.writer.write_all(&self.reserved)?;
1335        cw.writer.write_all(&self.coding_history)?;
1336        Ok(())
1337    }
1338}
1339
1340impl Debug for BextChunk {
1341    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
1342        fmt.debug_struct("BextChunk")
1343            .field("description", &self.description)
1344            .field("originator", &self.originator)
1345            .field("originator_ref", &self.originator_ref)
1346            .field("origination_date", &self.origination_date)
1347            .field("origination_time", &self.origination_time)
1348            .field("time_ref", &self.time_ref)
1349            .field("version", &self.version)
1350            .field(
1351                "umid",
1352                &format_args!(
1353                    "[{}]",
1354                    self.umid
1355                        .iter()
1356                        .map(|byte| { format!("0x{:02x}", byte) })
1357                        .collect::<Vec<String>>()
1358                        .join(",")
1359                ),
1360            )
1361            .field("reserved", &format_args!("[u8; {}]", self.reserved.len()))
1362            .field("coding_history", &self.coding_history)
1363            .finish()
1364    }
1365}
1366
1367impl Default for BextChunk {
1368    fn default() -> Self {
1369        Self {
1370            description: String::new(),
1371            originator: String::new(),
1372            originator_ref: String::new(),
1373            origination_date: String::new(),
1374            origination_time: String::new(),
1375            time_ref: 0,
1376            version: 0,
1377            umid: [0u8; 64],
1378            reserved: [0u8; 190],
1379            coding_history: [0u8; 1],
1380        }
1381    }
1382}
1383
1384#[derive(Debug, Clone, Default)]
1385pub struct SmplChunk {
1386    pub manufacturer: u32,
1387    pub product: u32,
1388    pub sample_period: u32,
1389    pub midi_unity_note: u32,
1390    pub midi_pitch_fraction: u32,
1391    pub smpte_format: u32,
1392    pub smpte_offset: u32,
1393    pub num_sample_loops: u32,
1394    pub sampler_data: u32,
1395    pub loops: Vec<SmplSampleLoop>,
1396}
1397
1398#[derive(Debug, Clone, Copy, Default)]
1399pub struct SmplSampleLoop {
1400    pub identifier: u32,
1401    pub type_: u32,
1402    pub start: u32,
1403    pub end: u32,
1404    pub fraction: u32,
1405    pub play_count: u32,
1406}
1407
1408impl SmplChunk {
1409    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1410        let mut ret = Self {
1411            manufacturer: u32::read_le(reader)?,
1412            product: u32::read_le(reader)?,
1413            sample_period: u32::read_le(reader)?,
1414            midi_unity_note: u32::read_le(reader)?,
1415            midi_pitch_fraction: u32::read_le(reader)?,
1416            smpte_format: u32::read_le(reader)?,
1417            smpte_offset: u32::read_le(reader)?,
1418            num_sample_loops: u32::read_le(reader)?,
1419            sampler_data: u32::read_le(reader)?,
1420            loops: Vec::<SmplSampleLoop>::new(),
1421        };
1422        for _ in 0..ret.num_sample_loops {
1423            ret.loops.push(SmplSampleLoop::read(reader)?);
1424        }
1425        Ok(ret)
1426    }
1427
1428    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1429        let cw = ChunkWriter::begin(writer, b"smpl")?;
1430        self.manufacturer.write_le(cw.writer)?;
1431        self.product.write_le(cw.writer)?;
1432        self.sample_period.write_le(cw.writer)?;
1433        self.midi_unity_note.write_le(cw.writer)?;
1434        self.midi_pitch_fraction.write_le(cw.writer)?;
1435        self.smpte_format.write_le(cw.writer)?;
1436        self.smpte_offset.write_le(cw.writer)?;
1437        self.num_sample_loops.write_le(cw.writer)?;
1438        self.sampler_data.write_le(cw.writer)?;
1439        for l in self.loops.iter() {
1440            l.write(cw.writer)?;
1441        }
1442        Ok(())
1443    }
1444}
1445
1446impl SmplSampleLoop {
1447    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1448        Ok(Self {
1449            identifier: u32::read_le(reader)?,
1450            type_: u32::read_le(reader)?,
1451            start: u32::read_le(reader)?,
1452            end: u32::read_le(reader)?,
1453            fraction: u32::read_le(reader)?,
1454            play_count: u32::read_le(reader)?,
1455        })
1456    }
1457
1458    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1459        self.identifier.write_le(writer)?;
1460        self.type_.write_le(writer)?;
1461        self.start.write_le(writer)?;
1462        self.end.write_le(writer)?;
1463        self.fraction.write_le(writer)?;
1464        self.play_count.write_le(writer)?;
1465        Ok(())
1466    }
1467}
1468
1469#[derive(Debug, Clone, Copy, Default)]
1470pub struct InstChunk {
1471    pub base_note: u8,
1472    pub detune: u8,
1473    pub gain: u8,
1474    pub low_note: u8,
1475    pub high_note: u8,
1476    pub low_velocity: u8,
1477    pub high_velocity: u8,
1478}
1479
1480impl InstChunk {
1481    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1482        Ok(Self {
1483            base_note: u8::read_le(reader)?,
1484            detune: u8::read_le(reader)?,
1485            gain: u8::read_le(reader)?,
1486            low_note: u8::read_le(reader)?,
1487            high_note: u8::read_le(reader)?,
1488            low_velocity: u8::read_le(reader)?,
1489            high_velocity: u8::read_le(reader)?,
1490        })
1491    }
1492
1493    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1494        let cw = ChunkWriter::begin(writer, b"INST")?;
1495        self.base_note.write_le(cw.writer)?;
1496        self.detune.write_le(cw.writer)?;
1497        self.gain.write_le(cw.writer)?;
1498        self.low_note.write_le(cw.writer)?;
1499        self.high_note.write_le(cw.writer)?;
1500        self.low_velocity.write_le(cw.writer)?;
1501        self.high_velocity.write_le(cw.writer)?;
1502        Ok(())
1503    }
1504}
1505
1506/// * See <https://www.recordingblogs.com/wiki/playlist-chunk-of-a-wave-file>
1507#[derive(Debug, Clone, Default)]
1508pub struct PlstChunk {
1509    pub playlist_len: u32,
1510    pub data: Vec<Plst>,
1511}
1512
1513#[derive(Debug, Clone, Copy, Default)]
1514pub struct Plst {
1515    pub cue_point_id: u32,
1516    pub num_samples: u32,
1517    pub repeats: u32,
1518}
1519
1520impl PlstChunk {
1521    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1522        let playlist_len = u32::read_le(reader)?;
1523        Ok(Self {
1524            playlist_len,
1525            data: (0..playlist_len)
1526                .map(|_| -> Result<Plst, AudioReadError> { Plst::read(reader) })
1527                .collect::<Result<Vec<Plst>, AudioReadError>>()?,
1528        })
1529    }
1530
1531    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1532        let cw = ChunkWriter::begin(writer, b"plst")?;
1533        self.playlist_len.write_le(cw.writer)?;
1534        for data in self.data.iter() {
1535            data.write(cw.writer)?;
1536        }
1537        Ok(())
1538    }
1539
1540    pub fn build_map(&self) -> BTreeMap<u32, Plst> {
1541        self.data
1542            .iter()
1543            .map(|plst| (plst.cue_point_id, *plst))
1544            .collect()
1545    }
1546}
1547
1548impl Plst {
1549    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1550        Ok(Self {
1551            cue_point_id: u32::read_le(reader)?,
1552            num_samples: u32::read_le(reader)?,
1553            repeats: u32::read_le(reader)?,
1554        })
1555    }
1556
1557    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1558        self.cue_point_id.write_le(writer)?;
1559        self.num_samples.write_le(writer)?;
1560        self.repeats.write_le(writer)?;
1561        Ok(())
1562    }
1563}
1564
1565/// See <https://www.recordingblogs.com/wiki/cue-chunk-of-a-wave-file>
1566/// See <https://wavref.til.cafe/chunk/cue/>
1567#[derive(Debug, Clone, Default)]
1568pub struct CueChunk {
1569    pub num_cues: u32,
1570    pub cue_points: Vec<CuePoint>,
1571}
1572
1573#[derive(Debug, Clone, Copy, Default)]
1574pub struct CuePoint {
1575    pub cue_point_id: u32,
1576    pub position: u32,
1577    pub data_chunk_id: [u8; 4],
1578    pub chunk_start: u32,
1579    pub block_start: u32,
1580    pub offset: u32,
1581}
1582
1583impl CueChunk {
1584    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1585        let num_cues = u32::read_le(reader)?;
1586        Ok(Self {
1587            num_cues,
1588            cue_points: (0..num_cues)
1589                .map(|_| -> Result<CuePoint, AudioReadError> { CuePoint::read(reader) })
1590                .collect::<Result<Vec<CuePoint>, AudioReadError>>()?,
1591        })
1592    }
1593
1594    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1595        let cw = ChunkWriter::begin(writer, b"cue ")?;
1596        self.num_cues.write_le(cw.writer)?;
1597        for cue_point in self.cue_points.iter() {
1598            cue_point.write(cw.writer)?;
1599        }
1600        Ok(())
1601    }
1602
1603    pub fn build_map(&self) -> BTreeMap<u32, &CuePoint> {
1604        self.cue_points
1605            .iter()
1606            .map(|cue| (cue.cue_point_id, cue))
1607            .collect()
1608    }
1609}
1610
1611impl CuePoint {
1612    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
1613        let mut data_chunk_id = [0u8; 4];
1614        reader.read_exact(&mut data_chunk_id)?;
1615        Ok(Self {
1616            cue_point_id: u32::read_le(reader)?,
1617            position: u32::read_le(reader)?,
1618            data_chunk_id,
1619            chunk_start: u32::read_le(reader)?,
1620            block_start: u32::read_le(reader)?,
1621            offset: u32::read_le(reader)?,
1622        })
1623    }
1624
1625    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
1626        self.cue_point_id.write_le(writer)?;
1627        self.position.write_le(writer)?;
1628        writer.write_all(&self.data_chunk_id)?;
1629        self.chunk_start.write_le(writer)?;
1630        self.block_start.write_le(writer)?;
1631        self.offset.write_le(writer)?;
1632        Ok(())
1633    }
1634}
1635
1636#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
1637pub enum ListChunk {
1638    Info(BTreeMap<String, String>),
1639    Adtl(BTreeMap<u32, AdtlChunk>),
1640}
1641
1642impl Default for ListChunk {
1643    fn default() -> Self {
1644        Self::Info(BTreeMap::<String, String>::new())
1645    }
1646}
1647
1648/// See <https://wavref.til.cafe/chunk/adtl/>
1649#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
1650pub enum AdtlChunk {
1651    Labl(LablChunk),
1652    Note(NoteChunk),
1653    Ltxt(LtxtChunk),
1654    File(FileChunk),
1655}
1656
1657impl Default for AdtlChunk {
1658    fn default() -> Self {
1659        Self::Labl(LablChunk::default())
1660    }
1661}
1662
1663#[derive(Debug, Clone, Default, PartialOrd, Ord, PartialEq, Eq)]
1664pub struct LablChunk {
1665    pub cue_point_id: u32,
1666    pub data: String,
1667}
1668
1669#[derive(Debug, Clone, Default, PartialOrd, Ord, PartialEq, Eq)]
1670pub struct NoteChunk {
1671    pub cue_point_id: u32,
1672    pub data: String,
1673}
1674
1675#[derive(Debug, Clone, Default, PartialOrd, Ord, PartialEq, Eq)]
1676pub struct LtxtChunk {
1677    pub cue_point_id: u32,
1678    pub sample_length: u32,
1679    pub purpose_id: String,
1680    pub country: u16,
1681    pub language: u16,
1682    pub dialect: u16,
1683    pub code_page: u16,
1684    pub data: String,
1685}
1686
1687#[derive(Clone, Default, PartialOrd, Ord, PartialEq, Eq)]
1688pub struct FileChunk {
1689    pub cue_point_id: u32,
1690    pub media_type: u32,
1691    pub file_data: Vec<u8>,
1692}
1693
1694impl Debug for FileChunk {
1695    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
1696        fmt.debug_struct("FileChunk")
1697            .field("cue_point_id", &self.cue_point_id)
1698            .field("media_type", &self.media_type)
1699            .field("file_data", &format_args!("[u8; {}]", self.file_data.len()))
1700            .finish()
1701    }
1702}
1703
1704impl AdtlChunk {
1705    pub fn read(
1706        reader: &mut impl Reader,
1707        text_encoding: &StringCodecMaps,
1708    ) -> Result<Self, AudioReadError> {
1709        let sub_chunk = ChunkHeader::read(reader)?;
1710        let ret = match &sub_chunk.flag {
1711            b"labl" => Self::Labl(LablChunk {
1712                cue_point_id: u32::read_le(reader)?,
1713                data: read_str(reader, (sub_chunk.size - 4) as usize, text_encoding)?,
1714            }),
1715            b"note" => Self::Note(NoteChunk {
1716                cue_point_id: u32::read_le(reader)?,
1717                data: read_str(reader, (sub_chunk.size - 4) as usize, text_encoding)?,
1718            }),
1719            b"ltxt" => {
1720                let mut ltxt = LtxtChunk {
1721                    cue_point_id: u32::read_le(reader)?,
1722                    sample_length: u32::read_le(reader)?,
1723                    purpose_id: read_str(reader, 4, text_encoding)?,
1724                    country: u16::read_le(reader)?,
1725                    language: u16::read_le(reader)?,
1726                    dialect: u16::read_le(reader)?,
1727                    code_page: u16::read_le(reader)?,
1728                    data: String::new(),
1729                };
1730                ltxt.data = read_str_by_code_page(
1731                    reader,
1732                    (sub_chunk.size - 20) as usize,
1733                    text_encoding,
1734                    ltxt.code_page as u32,
1735                )?;
1736                Self::Ltxt(ltxt)
1737            }
1738            b"file" => Self::File(FileChunk {
1739                cue_point_id: u32::read_le(reader)?,
1740                media_type: u32::read_le(reader)?,
1741                file_data: read_bytes(reader, (sub_chunk.size - 8) as usize)?,
1742            }),
1743            other => {
1744                return Err(AudioReadError::UnexpectedFlag(
1745                    "labl/note/ltxt".to_owned(),
1746                    String::from_utf8_lossy(other).to_string(),
1747                ));
1748            }
1749        };
1750        sub_chunk.seek_to_next_chunk(reader)?;
1751        Ok(ret)
1752    }
1753
1754    pub fn write(
1755        &self,
1756        writer: &mut dyn Writer,
1757        text_encoding: &StringCodecMaps,
1758    ) -> Result<(), AudioWriteError> {
1759        fn to_sz(s: &str) -> String {
1760            if !s.is_empty() {
1761                let mut s = s.to_owned();
1762                if !s.ends_with('\0') {
1763                    s.push('\0');
1764                }
1765                s
1766            } else {
1767                "\0".to_owned()
1768            }
1769        }
1770        match self {
1771            Self::Labl(labl) => {
1772                let cw = ChunkWriter::begin(writer, b"labl")?;
1773                labl.cue_point_id.write_le(cw.writer)?;
1774                write_str(cw.writer, &to_sz(&labl.data), text_encoding)?;
1775            }
1776            Self::Note(note) => {
1777                let cw = ChunkWriter::begin(writer, b"note")?;
1778                note.cue_point_id.write_le(cw.writer)?;
1779                write_str(cw.writer, &to_sz(&note.data), text_encoding)?;
1780            }
1781            Self::Ltxt(ltxt) => {
1782                let cw = ChunkWriter::begin(writer, b"ltxt")?;
1783                ltxt.cue_point_id.write_le(cw.writer)?;
1784                ltxt.sample_length.write_le(cw.writer)?;
1785                write_str_sized(cw.writer, &ltxt.purpose_id, 4, text_encoding)?;
1786                ltxt.country.write_le(cw.writer)?;
1787                ltxt.language.write_le(cw.writer)?;
1788                ltxt.dialect.write_le(cw.writer)?;
1789                ltxt.code_page.write_le(cw.writer)?;
1790                write_str(cw.writer, &to_sz(&ltxt.data), text_encoding)?;
1791            }
1792            Self::File(file) => {
1793                let cw = ChunkWriter::begin(writer, b"file")?;
1794                file.cue_point_id.write_le(cw.writer)?;
1795                file.media_type.write_le(cw.writer)?;
1796                cw.writer.write_all(&file.file_data)?;
1797            }
1798        }
1799        Ok(())
1800    }
1801
1802    pub fn get_cue_point_id(&self) -> u32 {
1803        match self {
1804            Self::Labl(labl) => labl.cue_point_id,
1805            Self::Note(lote) => lote.cue_point_id,
1806            Self::Ltxt(ltxt) => ltxt.cue_point_id,
1807            Self::File(lile) => lile.cue_point_id,
1808        }
1809    }
1810}
1811
1812impl ListChunk {
1813    pub fn read(
1814        reader: &mut impl Reader,
1815        chunk_size: u64,
1816        text_encoding: &StringCodecMaps,
1817    ) -> Result<Self, AudioReadError> {
1818        let end_of_chunk = ChunkHeader::align(reader.stream_position()? + chunk_size);
1819        let mut flag = [0u8; 4];
1820        reader.read_exact(&mut flag)?;
1821        match &flag {
1822            b"info" | b"INFO" => {
1823                let dict = Self::read_dict(reader, end_of_chunk, text_encoding)?;
1824                Ok(Self::Info(dict))
1825            }
1826            b"adtl" => {
1827                let mut adtl_map = BTreeMap::<u32, AdtlChunk>::new();
1828                while reader.stream_position()? < end_of_chunk {
1829                    let adtl = AdtlChunk::read(reader, text_encoding)?;
1830                    let cue_point_id = adtl.get_cue_point_id();
1831                    if let Some(dup) = adtl_map.insert(cue_point_id, adtl.clone()) {
1832                        // If the chunk point ID duplicates,  the new one will be used to overwrite the old one.
1833                        eprintln!(
1834                            "Duplicated chunk point ID {cue_point_id} for the `Adtl` data: old is {:?}, and will be overwritten by the new one: {:?}",
1835                            dup, adtl
1836                        );
1837                    }
1838                }
1839                Ok(Self::Adtl(adtl_map))
1840            }
1841            other => Err(AudioReadError::Unimplemented(format!(
1842                "Unknown indentifier in LIST chunk: {}",
1843                text_encoding.decode_flags(other)
1844            ))),
1845        }
1846    }
1847
1848    pub fn write(
1849        &self,
1850        writer: &mut dyn Writer,
1851        text_encoding: &StringCodecMaps,
1852    ) -> Result<(), AudioWriteError> {
1853        let mut cw = ChunkWriter::begin(writer, b"LIST")?;
1854        match self {
1855            Self::Info(dict) => {
1856                cw.writer.write_all(b"INFO")?;
1857                Self::write_dict(&mut cw.writer, dict, text_encoding)?;
1858            }
1859            Self::Adtl(adtls) => {
1860                cw.writer.write_all(b"adtl")?;
1861                for (_cue_point_id, adtl) in adtls.iter() {
1862                    adtl.write(&mut cw.writer, text_encoding)?;
1863                }
1864            }
1865        };
1866        Ok(())
1867    }
1868
1869    fn read_dict(
1870        reader: &mut impl Reader,
1871        end_of_chunk: u64,
1872        text_encoding: &StringCodecMaps,
1873    ) -> Result<BTreeMap<String, String>, AudioReadError> {
1874        // The INFO chunk consists of multiple key-value pairs for song metadata.
1875        // Within its byte size constraints, read all key-value entries.
1876        let mut dict = BTreeMap::<String, String>::new();
1877        while reader.stream_position()? < end_of_chunk {
1878            let key_chunk = ChunkHeader::read(reader)?; // Every chunk's name is a key, its content is the value.
1879            let value_str = read_str(reader, key_chunk.size as usize, text_encoding)?;
1880            let key_str = text_encoding.decode(&key_chunk.flag);
1881            dict.insert(key_str, value_str);
1882            key_chunk.seek_to_next_chunk(reader)?;
1883        }
1884        // Let's try to store the key in uppercase form, if the INFO chunk provides both uppercase or lowercase, we store both of them.
1885        let mut to_be_added = Vec::<(String, String)>::new();
1886        for (key, val) in dict.iter() {
1887            let key_uppercase = key.to_uppercase();
1888            if key_uppercase == *key {
1889                // It is uppercase originally.
1890                continue;
1891            }
1892            if dict.contains_key(&key_uppercase) {
1893                // The LIST INFO chunk provided the uppercase key, and its value may be different from the lowercase key value, better not to overwrite it.
1894                continue;
1895            }
1896            to_be_added.push((key_uppercase, val.clone()));
1897        }
1898        for (key_uppercase, val) in to_be_added.into_iter() {
1899            dict.insert(key_uppercase, val);
1900        }
1901        Ok(dict)
1902    }
1903
1904    fn write_dict(
1905        writer: &mut dyn Writer,
1906        dict: &BTreeMap<String, String>,
1907        text_encoding: &StringCodecMaps,
1908    ) -> Result<(), AudioWriteError> {
1909        for (key, val) in dict.iter() {
1910            if key.len() != 4 {
1911                return Err(AudioWriteError::InvalidArguments(
1912                    "flag must be 4 bytes".to_owned(),
1913                ));
1914            }
1915            let bytes = key.as_bytes();
1916            let flag = [bytes[0], bytes[1], bytes[2], bytes[3]];
1917            let cw = ChunkWriter::begin(writer, &flag)?;
1918            let mut val = val.clone();
1919            val.push('\0');
1920            write_str(cw.writer, &val, text_encoding)?;
1921        }
1922        Ok(())
1923    }
1924}
1925
1926/// See <https://www.recordingblogs.com/wiki/list-chunk-of-a-wave-file>
1927pub fn get_list_info_map() -> BTreeMap<&'static str, &'static str> {
1928    [
1929        ("IARL", "The location where the subject of the file is archived"),
1930        ("IART", "The artist of the original subject of the file"),
1931        ("ICMS", "The name of the person or organization that commissioned the original subject of the file"),
1932        ("ICMT", "General comments about the file or its subject"),
1933        ("ICOP", "Copyright information about the file (e.g., \"Copyright Some Company 2011\")"),
1934        ("ICRD", "The date the subject of the file was created (creation date) (e.g., \"2022-12-31\")"),
1935        ("ICRP", "Whether and how an image was cropped"),
1936        ("IDIM", "The dimensions of the original subject of the file"),
1937        ("IDPI", "Dots per inch settings used to digitize the file"),
1938        ("IENG", "The name of the engineer who worked on the file"),
1939        ("IGNR", "The genre of the subject"),
1940        ("IKEY", "A list of keywords for the file or its subject"),
1941        ("ILGT", "Lightness settings used to digitize the file"),
1942        ("IMED", "Medium for the original subject of the file"),
1943        ("INAM", "Title of the subject of the file (name)"),
1944        ("IPLT", "The number of colors in the color palette used to digitize the file"),
1945        ("IPRD", "Name of the title the subject was originally intended for"),
1946        ("ISBJ", "Description of the contents of the file (subject)"),
1947        ("ISFT", "Name of the software package used to create the file"),
1948        ("ISRC", "The name of the person or organization that supplied the original subject of the file"),
1949        ("ISRF", "The original form of the material that was digitized (source form)"),
1950        ("ITCH", "The name of the technician who digitized the subject file"),
1951        ("ITRK", "The track number of the file")
1952    ].iter().copied().collect()
1953}
1954
1955/// * A trait for conveniently get the metadata fields.
1956pub trait ListInfo {
1957    fn get_is_list_info(&self) -> bool;
1958    fn get(&self, key: &str) -> Option<&String>;
1959    fn set(&mut self, key: &str, value: &str) -> Result<Option<String>, AudioError>;
1960
1961    fn get_archive(&self) -> Option<&String> {self.get("IARL")}
1962    fn get_artist(&self) -> Option<&String> {self.get("IART")}
1963    fn get_comment(&self) -> Option<&String> {self.get("ICMT")}
1964    fn get_producer(&self) -> Option<&String> {self.get("ICMS")}
1965    fn get_copyright(&self) -> Option<&String> {self.get("ICOP")}
1966    fn get_create_date(&self) -> Option<&String> {self.get("ICRD")}
1967    fn get_engineer(&self) -> Option<&String> {self.get("IENG")}
1968    fn get_genre(&self) -> Option<&String> {self.get("IGNR")}
1969    fn get_keywords(&self) -> Option<&String> {self.get("IKEY")}
1970    fn get_lightness(&self) -> Option<&String> {self.get("ILGT")}
1971    fn get_medium(&self) -> Option<&String> {self.get("IMED")}
1972    fn get_name(&self) -> Option<&String> {self.get("INAM")}
1973    fn get_album(&self) -> Option<&String> {self.get("IPRD")}
1974    fn get_description(&self) -> Option<&String> {self.get("ISBJ")}
1975    fn get_software(&self) -> Option<&String> {self.get("ISFT")}
1976    fn get_source(&self) -> Option<&String> {self.get("ISRC")}
1977    fn get_orig_form(&self) -> Option<&String> {self.get("ISRF")}
1978    fn get_technician(&self) -> Option<&String> {self.get("ITCH")}
1979    fn get_track_no(&self) -> Option<&String> {self.get("ITRK")}
1980
1981    fn get_track_no_as_number(&self) -> Result<u32, AudioError> {
1982        if let Some(track_no) = self.get_track_no() {
1983            match track_no.parse::<u32>() {
1984                Ok(track_no) => Ok(track_no),
1985                Err(_) => Err(AudioError::Unparseable(track_no.clone())),
1986            }
1987        } else {
1988            Err(AudioError::NoSuchData("ITRK".to_owned()))
1989        }
1990    }
1991
1992    fn set_archive(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("IARL", value)}
1993    fn set_artist(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("IART", value)}
1994    fn set_comment(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("ICMT", value)}
1995    fn set_producer(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("ICMS", value)}
1996    fn set_copyright(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("ICOP", value)}
1997    fn set_create_date(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("ICRD", value)}
1998    fn set_engineer(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("IENG", value)}
1999    fn set_genre(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("IGNR", value)}
2000    fn set_keywords(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("IKEY", value)}
2001    fn set_lightness(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("ILGT", value)}
2002    fn set_medium(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("IMED", value)}
2003    fn set_name(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("INAM", value)}
2004    fn set_album(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("IPRD", value)}
2005    fn set_description(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("ISBJ", value)}
2006    fn set_software(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("ISFT", value)}
2007    fn set_source(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("ISRC", value)}
2008    fn set_orig_form(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("ISRF", value)}
2009    fn set_technician(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("ITCH", value)}
2010    fn set_track_no(&mut self, value: &str) -> Result<Option<String>, AudioError> {self.set("ITRK", value)}
2011    fn set_track_no_as_number(&mut self, track_no: u32) -> Result<u32, AudioError> {
2012        match self.set_track_no(&format!("{track_no}")) {
2013            Err(e) => Err(e),
2014            Ok(track_no) => {
2015                if let Some(track_no) = track_no {
2016                    match track_no.parse::<u32>() {
2017                        Ok(track_no) => Ok(track_no),
2018                        Err(_) => Err(AudioError::Unparseable(track_no.clone())),
2019                    }
2020                } else {
2021                    Ok(0)
2022                }
2023            }
2024        }
2025    }
2026}
2027
2028impl ListInfo for ListChunk {
2029    fn get_is_list_info(&self) -> bool {
2030        matches!(self, Self::Info(_))
2031    }
2032
2033    fn get(&self, key: &str) -> Option<&String> {
2034        if let Self::Info(dict) = self {
2035            dict.get(key)
2036        } else {
2037            None
2038        }
2039    }
2040
2041    fn set(&mut self, key: &str, value: &str) -> Result<Option<String>, AudioError> {
2042        if let Self::Info(dict) = self {
2043            Ok(dict.insert(key.to_owned(), value.to_string()))
2044        } else {
2045            Err(AudioError::InvalidArguments("The type of the `LIST` chunk is `adtl`, not `INFO`, so can not set the metadata values.".to_owned()))
2046        }
2047    }
2048}
2049
2050/// See <https://wavref.til.cafe/chunk/cset/>
2051#[allow(clippy::zero_prefixed_literal)]
2052pub fn get_country_code_map() -> HashMap<u16, &'static str> {
2053    [
2054        (000, "None (assume 001 = USA)"),
2055        (001, "USA"),
2056        (002, "Canada"),
2057        (003, "Latin America"),
2058        (030, "Greece"),
2059        (031, "Netherlands"),
2060        (032, "Belgium"),
2061        (033, "France"),
2062        (034, "Spain"),
2063        (039, "Italy"),
2064        (041, "Switzerland"),
2065        (043, "Austria"),
2066        (044, "United Kingdom"),
2067        (045, "Denmark"),
2068        (046, "Sweden"),
2069        (047, "Norway"),
2070        (049, "West Germany"),
2071        (052, "Mexico"),
2072        (055, "Brazil"),
2073        (061, "Australia"),
2074        (064, "New Zealand"),
2075        (081, "Japan"),
2076        (082, "Korea"),
2077        (086, "People’s Republic of China"),
2078        (088, "Taiwan"),
2079        (090, "Turkey"),
2080        (351, "Portugal"),
2081        (352, "Luxembourg"),
2082        (354, "Iceland"),
2083        (358, "Finland"),
2084    ]
2085    .iter()
2086    .copied()
2087    .collect()
2088}
2089
2090#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
2091pub struct LanguageDialect {
2092    lang: u16,
2093    dial: u16,
2094}
2095
2096#[derive(Debug, Clone, Copy)]
2097pub struct LanguageSpecification {
2098    lang: &'static str,
2099    spec: &'static str,
2100}
2101
2102/// See <https://wavref.til.cafe/chunk/cset/>
2103pub fn get_language_dialect_code_map() -> HashMap<LanguageDialect, LanguageSpecification> {
2104    [
2105        (LanguageDialect{lang: 0 ,  dial: 0}, LanguageSpecification{lang: "None (assume 9,1 = US English)", spec: "RIFF1991"}),
2106        (LanguageDialect{lang: 1 ,  dial: 1}, LanguageSpecification{lang: "Arabic", spec: "RIFF1991"}),
2107        (LanguageDialect{lang: 2 ,  dial: 1}, LanguageSpecification{lang: "Bulgarian", spec: "RIFF1991"}),
2108        (LanguageDialect{lang: 3 ,  dial: 1}, LanguageSpecification{lang: "Catalan", spec: "RIFF1991"}),
2109        (LanguageDialect{lang: 4 ,  dial: 1}, LanguageSpecification{lang: "Traditional Chinese", spec: "RIFF1991"}),
2110        (LanguageDialect{lang: 4 ,  dial: 2}, LanguageSpecification{lang: "Simplified Chinese", spec: "RIFF1991"}),
2111        (LanguageDialect{lang: 5 ,  dial: 1}, LanguageSpecification{lang: "Czech", spec: "RIFF1991"}),
2112        (LanguageDialect{lang: 6 ,  dial: 1}, LanguageSpecification{lang: "Danish", spec: "RIFF1991"}),
2113        (LanguageDialect{lang: 7 ,  dial: 1}, LanguageSpecification{lang: "German", spec: "RIFF1991"}),
2114        (LanguageDialect{lang: 7 ,  dial: 2}, LanguageSpecification{lang: "Swiss German", spec: "RIFF1991"}),
2115        (LanguageDialect{lang: 8 ,  dial: 1}, LanguageSpecification{lang: "Greek", spec: "RIFF1991"}),
2116        (LanguageDialect{lang: 9 ,  dial: 1}, LanguageSpecification{lang: "US English", spec: "RIFF1991"}),
2117        (LanguageDialect{lang: 9 ,  dial: 2}, LanguageSpecification{lang: "UK English", spec: "RIFF1991"}),
2118        (LanguageDialect{lang: 10,  dial: 1}, LanguageSpecification{lang: "Spanish", spec: "RIFF1991"}),
2119        (LanguageDialect{lang: 10,  dial: 2}, LanguageSpecification{lang: "Spanish", spec: "Mexican RIFF1991"}),
2120        (LanguageDialect{lang: 11,  dial: 1}, LanguageSpecification{lang: "Finnish", spec: "RIFF1991"}),
2121        (LanguageDialect{lang: 12,  dial: 1}, LanguageSpecification{lang: "French", spec: "RIFF1991"}),
2122        (LanguageDialect{lang: 12,  dial: 2}, LanguageSpecification{lang: "Belgian French", spec: "RIFF1991"}),
2123        (LanguageDialect{lang: 12,  dial: 3}, LanguageSpecification{lang: "Canadian French", spec: "RIFF1991"}),
2124        (LanguageDialect{lang: 12,  dial: 4}, LanguageSpecification{lang: "Swiss French", spec: "RIFF1991"}),
2125        (LanguageDialect{lang: 13,  dial: 1}, LanguageSpecification{lang: "Hebrew", spec: "RIFF1991"}),
2126        (LanguageDialect{lang: 14,  dial: 1}, LanguageSpecification{lang: "Hungarian", spec: "RIFF1994"}),
2127        (LanguageDialect{lang: 15,  dial: 1}, LanguageSpecification{lang: "Icelandic", spec: "RIFF1994"}),
2128        (LanguageDialect{lang: 16,  dial: 1}, LanguageSpecification{lang: "Italian", spec: "RIFF1994"}),
2129        (LanguageDialect{lang: 16,  dial: 2}, LanguageSpecification{lang: "Swiss Italian", spec: "RIFF1994"}),
2130        (LanguageDialect{lang: 17,  dial: 1}, LanguageSpecification{lang: "Japanese", spec: "RIFF1994"}),
2131        (LanguageDialect{lang: 18,  dial: 1}, LanguageSpecification{lang: "Korean", spec: "RIFF1994"}),
2132        (LanguageDialect{lang: 19,  dial: 1}, LanguageSpecification{lang: "Dutch", spec: "RIFF1994"}),
2133        (LanguageDialect{lang: 19,  dial: 2}, LanguageSpecification{lang: "Belgian Dutch", spec: "RIFF1994"}),
2134        (LanguageDialect{lang: 20,  dial: 1}, LanguageSpecification{lang: "Norwegian - Bokmal", spec: "RIFF1994"}),
2135        (LanguageDialect{lang: 20,  dial: 2}, LanguageSpecification{lang: "Norwegian - Nynorsk", spec: "RIFF1994"}),
2136        (LanguageDialect{lang: 21,  dial: 1}, LanguageSpecification{lang: "Polish", spec: "RIFF1994"}),
2137        (LanguageDialect{lang: 22,  dial: 1}, LanguageSpecification{lang: "Brazilian Portuguese", spec: "RIFF1994"}),
2138        (LanguageDialect{lang: 22,  dial: 2}, LanguageSpecification{lang: "Portuguese", spec: "RIFF1994"}),
2139        (LanguageDialect{lang: 23,  dial: 1}, LanguageSpecification{lang: "Rhaeto-Romanic", spec: "RIFF1994"}),
2140        (LanguageDialect{lang: 24,  dial: 1}, LanguageSpecification{lang: "Romanian", spec: "RIFF1994"}),
2141        (LanguageDialect{lang: 25,  dial: 1}, LanguageSpecification{lang: "Russian", spec: "RIFF1994"}),
2142        (LanguageDialect{lang: 26,  dial: 1}, LanguageSpecification{lang: "Serbo-Croatian (Latin)", spec: "RIFF1994"}),
2143        (LanguageDialect{lang: 26,  dial: 2}, LanguageSpecification{lang: "Serbo-Croatian (Cyrillic)", spec: "RIFF1994"}),
2144        (LanguageDialect{lang: 27,  dial: 1}, LanguageSpecification{lang: "Slovak", spec: "RIFF1994"}),
2145        (LanguageDialect{lang: 28,  dial: 1}, LanguageSpecification{lang: "Albanian", spec: "RIFF1994"}),
2146        (LanguageDialect{lang: 29,  dial: 1}, LanguageSpecification{lang: "Swedish", spec: "RIFF1994"}),
2147        (LanguageDialect{lang: 30,  dial: 1}, LanguageSpecification{lang: "Thai", spec: "RIFF1994"}),
2148        (LanguageDialect{lang: 31,  dial: 1}, LanguageSpecification{lang: "Turkish", spec: "RIFF1994"}),
2149        (LanguageDialect{lang: 32,  dial: 1}, LanguageSpecification{lang: "Urdu", spec: "RIFF1994"}),
2150        (LanguageDialect{lang: 33,  dial: 1}, LanguageSpecification{lang: "Bahasa", spec: "RIFF1994"}),
2151    ].iter().copied().collect()
2152}
2153
2154/// * The fully assembled cue point data from various of chunks in the WAV file.
2155#[derive(Debug, Clone, Default)]
2156pub struct FullInfoCuePoint {
2157    pub data_chunk_id: [u8; 4],
2158
2159    /// * The label of the cue point
2160    pub label: String,
2161
2162    /// * The notes for the cue point
2163    pub note: String,
2164
2165    /// * How many samples in the cue point
2166    pub sample_length: u32,
2167
2168    /// * What is the purpose of the cue point
2169    pub purpose_id: String,
2170
2171    /// * Country name
2172    pub country: String,
2173
2174    /// * Language name
2175    pub language: String,
2176
2177    /// * Some text data
2178    pub text_data: String,
2179
2180    /// * The media type
2181    pub media_type: u32,
2182
2183    /// * The file data
2184    pub file_data: Vec<u8>,
2185
2186    /// * Start sample
2187    pub start_sample: u32,
2188
2189    /// * Num samples
2190    pub num_samples: u32,
2191
2192    /// * repeats for playback
2193    pub repeats: u32,
2194}
2195
2196impl FullInfoCuePoint {
2197    pub fn new(
2198        cue_point_id: u32,
2199        cue_point: &CuePoint,
2200        adtl_chunks: &BTreeMap<u32, AdtlChunk>,
2201        plst: &Option<&Plst>,
2202        country_code_map: &HashMap<u16, &'static str>,
2203        dialect_code_map: &HashMap<LanguageDialect, LanguageSpecification>,
2204    ) -> Result<Self, AudioError> {
2205        let mut ret = Self {
2206            data_chunk_id: cue_point.data_chunk_id,
2207            label: String::new(),
2208            note: String::new(),
2209            sample_length: 0,
2210            purpose_id: String::new(),
2211            country: String::new(),
2212            language: String::new(),
2213            text_data: String::new(),
2214            media_type: 0,
2215            file_data: Vec::<u8>::new(),
2216            start_sample: cue_point.position,
2217            num_samples: 0,
2218            repeats: 0,
2219        };
2220        if let Some(plst) = plst {
2221            ret.num_samples = plst.num_samples;
2222            ret.repeats = plst.repeats;
2223        } else {
2224            eprintln!(
2225                "Lack of `plst` chunk, `num_samples` should be calculated by yourself, and `repeats` remains zero."
2226            );
2227        }
2228        if let Some(adtl) = adtl_chunks.get(&cue_point_id) {
2229            match adtl {
2230                AdtlChunk::Labl(labl) => ret.label = labl.data.clone(),
2231                AdtlChunk::Note(note) => ret.note = note.data.clone(),
2232                AdtlChunk::Ltxt(ltxt) => {
2233                    let lang_dial = LanguageDialect {
2234                        lang: ltxt.language,
2235                        dial: ltxt.dialect,
2236                    };
2237                    let unknown_lang_spec = LanguageSpecification {
2238                        lang: "Unknown",
2239                        spec: "UKNW1994",
2240                    };
2241                    let country = if let Some(country) = country_code_map.get(&ltxt.country) {
2242                        country.to_string()
2243                    } else {
2244                        format!("Unknown country code {}", ltxt.country)
2245                    };
2246                    let language = if let Some(lang_dial) = dialect_code_map.get(&lang_dial) {
2247                        *lang_dial
2248                    } else {
2249                        unknown_lang_spec
2250                    }
2251                    .lang
2252                    .to_owned();
2253                    ret.sample_length = ltxt.sample_length;
2254                    ret.purpose_id = ltxt.purpose_id.clone();
2255                    ret.country = country;
2256                    ret.language = language;
2257                    ret.text_data = ltxt.data.clone();
2258                }
2259                AdtlChunk::File(file) => {
2260                    ret.media_type = file.media_type;
2261                    ret.file_data = file.file_data.clone();
2262                }
2263            }
2264            Ok(ret)
2265        } else {
2266            Err(AudioError::NoSuchData(format!(
2267                "ADTL data for cue point ID: {cue_point_id}"
2268            )))
2269        }
2270    }
2271}
2272
2273/// * Create a fully assembled cue point data from various of chunks in the WAV file.
2274pub fn create_full_info_cue_data(
2275    cue_chunk: &CueChunk,
2276    adtl_chunks: &BTreeMap<u32, AdtlChunk>,
2277    plstchunk: &Option<PlstChunk>,
2278) -> Result<BTreeMap<u32, FullInfoCuePoint>, AudioError> {
2279    let country_code_map = get_country_code_map();
2280    let dialect_code_map = get_language_dialect_code_map();
2281    let plstmap = if let Some(plstchunk) = plstchunk {
2282        plstchunk.build_map()
2283    } else {
2284        BTreeMap::<u32, Plst>::new()
2285    };
2286    cue_chunk
2287        .cue_points
2288        .iter()
2289        .map(|cue| -> Result<(u32, FullInfoCuePoint), AudioError> {
2290            match FullInfoCuePoint::new(
2291                cue.cue_point_id,
2292                cue,
2293                adtl_chunks,
2294                &plstmap.get(&cue.cue_point_id),
2295                &country_code_map,
2296                &dialect_code_map,
2297            ) {
2298                Ok(full_info_cue_data) => Ok((cue.cue_point_id, full_info_cue_data)),
2299                Err(e) => Err(e),
2300            }
2301        })
2302        .collect::<Result<BTreeMap<u32, FullInfoCuePoint>, AudioError>>()
2303}
2304
2305#[derive(Debug, Clone, Default)]
2306pub struct AcidChunk {
2307    pub flags: u32,
2308    pub root_node: u16,
2309    pub reserved1: u16,
2310    pub reserved2: f32,
2311    pub num_beats: u32,
2312    pub meter_denominator: u16,
2313    pub meter_numerator: u16,
2314    pub tempo: f32,
2315}
2316
2317impl AcidChunk {
2318    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
2319        Ok(Self {
2320            flags: u32::read_le(reader)?,
2321            root_node: u16::read_le(reader)?,
2322            reserved1: u16::read_le(reader)?,
2323            reserved2: f32::read_le(reader)?,
2324            num_beats: u32::read_le(reader)?,
2325            meter_denominator: u16::read_le(reader)?,
2326            meter_numerator: u16::read_le(reader)?,
2327            tempo: f32::read_le(reader)?,
2328        })
2329    }
2330
2331    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
2332        let cw = ChunkWriter::begin(writer, b"acid")?;
2333        self.flags.write_le(cw.writer)?;
2334        self.root_node.write_le(cw.writer)?;
2335        self.reserved1.write_le(cw.writer)?;
2336        self.reserved2.write_le(cw.writer)?;
2337        self.num_beats.write_le(cw.writer)?;
2338        self.meter_denominator.write_le(cw.writer)?;
2339        self.meter_numerator.write_le(cw.writer)?;
2340        self.tempo.write_le(cw.writer)?;
2341        Ok(())
2342    }
2343}
2344
2345#[derive(Debug, Clone, Copy, Default)]
2346pub struct TrknChunk {
2347    pub track_no: u16,
2348    pub total_tracks: u16,
2349}
2350
2351impl TrknChunk {
2352    pub fn read(reader: &mut impl Reader) -> Result<Self, AudioReadError> {
2353        Ok(Self {
2354            track_no: u16::read_le(reader)?,
2355            total_tracks: u16::read_le(reader)?,
2356        })
2357    }
2358
2359    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
2360        let cw = ChunkWriter::begin(writer, b"Trkn")?;
2361        self.track_no.write_le(cw.writer)?;
2362        self.total_tracks.write_le(cw.writer)?;
2363        Ok(())
2364    }
2365}
2366
2367#[derive(Clone, PartialOrd, PartialEq, Ord, Eq)]
2368pub enum JunkChunk {
2369    FullZero(u64),
2370    SomeData(Vec<u8>),
2371}
2372
2373impl JunkChunk {
2374    pub fn from(data: Vec<u8>) -> Self {
2375        let mut is_all_zero = true;
2376        for i in data.iter() {
2377            if *i != 0 {
2378                is_all_zero = false;
2379                break;
2380            }
2381        }
2382        if is_all_zero {
2383            Self::FullZero(data.len() as u64)
2384        } else {
2385            Self::SomeData(data)
2386        }
2387    }
2388
2389    pub fn write(&self, writer: &mut dyn Writer) -> Result<(), AudioWriteError> {
2390        let cw = ChunkWriter::begin(writer, b"JUNK")?;
2391        match self {
2392            Self::FullZero(size) => cw.writer.write_all(&vec![0u8; *size as usize])?,
2393            Self::SomeData(data) => cw.writer.write_all(data)?,
2394        }
2395        Ok(())
2396    }
2397}
2398
2399impl Debug for JunkChunk {
2400    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
2401        match self {
2402            Self::FullZero(size) => write!(f, "[0u8; {size}]"),
2403            Self::SomeData(data) => write!(
2404                f,
2405                "[{}]",
2406                data.iter()
2407                    .map(|byte| { format!("0x{:02}", byte) })
2408                    .collect::<Vec<String>>()
2409                    .join(", ")
2410            ),
2411        }
2412    }
2413}
2414
2415impl Default for JunkChunk {
2416    fn default() -> Self {
2417        Self::FullZero(0)
2418    }
2419}
2420
2421/// * If the `id3` feature is enabled, use it to read ID3 data.
2422#[cfg(feature = "id3")]
2423#[allow(non_snake_case)]
2424pub mod Id3 {
2425    use crate::errors::{AudioReadError, AudioWriteError, IOErrorInfo};
2426    use std::io::{Read, Seek, Write};
2427    pub type Tag = id3::Tag;
2428
2429    pub fn id3_read<R>(reader: &mut R, _size: usize) -> Result<Tag, AudioReadError>
2430    where
2431        R: Read + Seek + ?Sized,
2432    {
2433        Ok(Tag::read_from2(reader)?)
2434    }
2435
2436    pub fn id3_write<W>(tag: &Tag, writer: &mut W) -> Result<(), AudioWriteError>
2437    where
2438        W: Write + ?Sized,
2439    {
2440        Ok(tag.write_to(writer, tag.version())?)
2441    }
2442
2443    impl From<id3::Error> for AudioReadError {
2444        fn from(err: id3::Error) -> Self {
2445            match err.kind {
2446                id3::ErrorKind::Io(ioerr) => AudioReadError::IOError(IOErrorInfo {
2447                    kind: ioerr.kind(),
2448                    message: ioerr.to_string(),
2449                }),
2450                id3::ErrorKind::StringDecoding(bytes) => AudioReadError::StringDecodeError(bytes),
2451                id3::ErrorKind::NoTag => AudioReadError::FormatError(err.description),
2452                id3::ErrorKind::Parsing => AudioReadError::InvalidData(err.description),
2453                id3::ErrorKind::InvalidInput => AudioReadError::InvalidData(err.description),
2454                id3::ErrorKind::UnsupportedFeature => AudioReadError::Unsupported(err.description),
2455            }
2456        }
2457    }
2458
2459    impl From<id3::Error> for AudioWriteError {
2460        fn from(err: id3::Error) -> Self {
2461            match err.kind {
2462                id3::ErrorKind::Io(ioerr) => AudioWriteError::IOError(IOErrorInfo {
2463                    kind: ioerr.kind(),
2464                    message: ioerr.to_string(),
2465                }),
2466                id3::ErrorKind::StringDecoding(bytes) => AudioWriteError::StringDecodeError(bytes),
2467                id3::ErrorKind::NoTag => AudioWriteError::OtherReason(err.description),
2468                id3::ErrorKind::Parsing => AudioWriteError::OtherReason(err.description),
2469                id3::ErrorKind::InvalidInput => AudioWriteError::OtherReason(err.description),
2470                id3::ErrorKind::UnsupportedFeature => AudioWriteError::Unsupported(err.description),
2471            }
2472        }
2473    }
2474}
2475
2476/// * If the `id3` feature is disabled, read the raw bytes.
2477#[cfg(not(feature = "id3"))]
2478#[allow(non_snake_case)]
2479pub mod Id3 {
2480    use std::error::Error;
2481    use std::io::Read;
2482    use std::vec::Vec;
2483    #[derive(Clone)]
2484    pub struct Tag {
2485        pub data: Vec<u8>,
2486    }
2487    impl Tag {
2488        fn new(data: Vec<u8>) -> Self {
2489            Self { data }
2490        }
2491    }
2492
2493    pub fn id3_read<R>(reader: &mut R, size: usize) -> Result<Tag, AudioReadError>
2494    where
2495        R: Read + Seek + ?Sized,
2496    {
2497        #[cfg(debug_assertions)]
2498        println!(
2499            "Feature \"id3\" was not enabled, consider compile with \"cargo build --features id3\""
2500        );
2501        Ok(Tag::new(super::read_bytes(reader, size)?))
2502    }
2503
2504    pub fn id3_write<W>(tag: &Tag, writer: &mut W) -> Result<(), AudioWriteError>
2505    where
2506        W: Write + ?Sized,
2507    {
2508        #[cfg(debug_assertions)]
2509        println!(
2510            "Feature \"id3\" was not enabled, the saved id3 binary bytes may not correct, consider compile with \"cargo build --features id3\""
2511        );
2512        Ok(writer.write_all(&tag.data))
2513    }
2514
2515    impl std::fmt::Debug for Tag {
2516        fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
2517            fmt.debug_struct("Tag").finish_non_exhaustive()
2518        }
2519    }
2520}
2521
2522impl From<DownmixerError> for AudioError {
2523    fn from(err: DownmixerError) -> AudioError {
2524        match err {
2525            DownmixerError::InvalidInput(info) => AudioError::InvalidArguments(info),
2526        }
2527    }
2528}
2529
2530pub mod mp3 {
2531    /// * MP3 supports two channels in multiple ways.
2532    #[derive(Debug, Clone, Copy, PartialEq)]
2533    #[repr(u8)]
2534    pub enum Mp3Channels {
2535        /// * Mono audio
2536        Mono = 3,
2537
2538        /// * Stereo audio (not telling how it is)
2539        Stereo = 0,
2540
2541        /// * Joint stereo (most commonly used, for better compression)
2542        JointStereo = 1,
2543
2544        /// * Dual channel stereo (for better audio quality)
2545        DualChannel = 2,
2546
2547        /// * Not set
2548        NotSet = 4,
2549    }
2550
2551    /// * MP3 quality. Affects the speed of encoding.
2552    #[derive(Debug, Clone, Copy, PartialEq)]
2553    #[repr(u8)]
2554    pub enum Mp3Quality {
2555        Best = 0,
2556        SecondBest = 1,
2557        NearBest = 2,
2558        VeryNice = 3,
2559        Nice = 4,
2560        Good = 5,
2561        Decent = 6,
2562        Ok = 7,
2563        SecondWorst = 8,
2564        Worst = 9,
2565    }
2566
2567    /// * The tier 1 factor for MP3 audio quality, bigger bitrate means better audio quality.
2568    /// * Most of the music website provides 128 kbps music for free, and 320 kbps music for the purchased subscribed members.
2569    #[derive(Debug, Clone, Copy, PartialEq)]
2570    #[repr(u16)]
2571    pub enum Mp3Bitrate {
2572        Kbps8 = 8,
2573        Kbps16 = 16,
2574        Kbps24 = 24,
2575        Kbps32 = 32,
2576        Kbps40 = 40,
2577        Kbps48 = 48,
2578
2579        /// * The bitrate for audio chatting.
2580        Kbps64 = 64,
2581        Kbps80 = 80,
2582        Kbps96 = 96,
2583        Kbps112 = 112,
2584
2585        /// * The bitrate for free users.
2586        Kbps128 = 128,
2587        Kbps160 = 160,
2588        Kbps192 = 192,
2589        Kbps224 = 224,
2590
2591        /// * Laboratories uses this bitrate.
2592        Kbps256 = 256,
2593
2594        /// * The bitrate for VIP users who pay for it.
2595        Kbps320 = 320,
2596    }
2597
2598    /// * The VBR mode for MP3. If you turn VBR on, the audio quality for MP3 will be a little bit worse.
2599    #[derive(Debug, Clone, Copy, PartialEq)]
2600    #[repr(u8)]
2601    pub enum Mp3VbrMode {
2602        /// * This option disables the VBR mode.
2603        Off = 0,
2604
2605        Mt = 1,
2606        Rh = 2,
2607        Abr = 3,
2608
2609        /// * This option is used most commonly.
2610        Mtrh = 4,
2611    }
2612
2613    const ID3_FIELD_LENGTH: usize = 250;
2614
2615    #[derive(Debug, Clone, PartialEq)]
2616    pub struct Mp3Id3Tag {
2617        pub title: [u8; ID3_FIELD_LENGTH],
2618        pub artist: [u8; ID3_FIELD_LENGTH],
2619        pub album: [u8; ID3_FIELD_LENGTH],
2620        pub album_art: [u8; ID3_FIELD_LENGTH],
2621        pub year: [u8; ID3_FIELD_LENGTH],
2622        pub comment: [u8; ID3_FIELD_LENGTH],
2623    }
2624
2625    /// * The encoder options for MP3
2626    #[derive(Debug, Clone, PartialEq)]
2627    pub struct Mp3EncoderOptions {
2628        /// * MP3 channels, not just mono and stereo. MP3 supports two channels in multiple ways.
2629        pub channels: Mp3Channels,
2630
2631        /// * MP3 quality. Affects the speed of encoding.
2632        pub quality: Mp3Quality,
2633
2634        /// * MP3 bitrate. Affects the audio quality and file size, bigger is better.
2635        pub bitrate: Mp3Bitrate,
2636
2637        /// * VBR mode for better compression, turn it off to get better audio quality.
2638        pub vbr_mode: Mp3VbrMode,
2639
2640        /// * ID3 tags, if you have, fill this field.
2641        pub id3tag: Option<Mp3Id3Tag>,
2642    }
2643
2644    impl Mp3EncoderOptions {
2645        pub fn new() -> Self {
2646            Self {
2647                channels: Mp3Channels::NotSet,
2648                quality: Mp3Quality::Best,
2649                bitrate: Mp3Bitrate::Kbps320,
2650                vbr_mode: Mp3VbrMode::Off,
2651                id3tag: None,
2652            }
2653        }
2654
2655        pub fn new_mono() -> Self {
2656            Self {
2657                channels: Mp3Channels::Mono,
2658                quality: Mp3Quality::Best,
2659                bitrate: Mp3Bitrate::Kbps320,
2660                vbr_mode: Mp3VbrMode::Off,
2661                id3tag: None,
2662            }
2663        }
2664
2665        pub fn new_stereo() -> Self {
2666            Self {
2667                channels: Mp3Channels::JointStereo,
2668                quality: Mp3Quality::Best,
2669                bitrate: Mp3Bitrate::Kbps320,
2670                vbr_mode: Mp3VbrMode::Off,
2671                id3tag: None,
2672            }
2673        }
2674
2675        pub fn get_channels(&self) -> u16 {
2676            match self.channels {
2677                Mp3Channels::Mono => 1,
2678                Mp3Channels::Stereo | Mp3Channels::DualChannel | Mp3Channels::JointStereo => 2,
2679                Mp3Channels::NotSet => 0,
2680            }
2681        }
2682
2683        pub fn get_bitrate(&self) -> u32 {
2684            self.bitrate as u32 * 1000
2685        }
2686    }
2687
2688    impl Default for Mp3EncoderOptions {
2689        fn default() -> Self {
2690            Self::new()
2691        }
2692    }
2693}
2694
2695pub mod opus {
2696    pub const OPUS_ALLOWED_SAMPLE_RATES: [u32; 5] = [8000, 12000, 16000, 24000, 48000];
2697    pub const OPUS_MIN_SAMPLE_RATE: u32 = 8000;
2698    pub const OPUS_MAX_SAMPLE_RATE: u32 = 48000;
2699
2700    /// * The opus encoder only eats these durations of the samples to encode.
2701    /// * Longer duration means better quality and compression.
2702    /// * If longer than or equal to 10ms, the compression algorithm could be able to use some advanced technology.
2703    #[derive(Debug, Clone, Copy, PartialEq)]
2704    #[repr(u32)]
2705    pub enum OpusEncoderSampleDuration {
2706        MilliSec2_5,
2707        MilliSec5,
2708        MilliSec10,
2709        MilliSec20,
2710        MilliSec40,
2711        MilliSec60,
2712    }
2713
2714    /// * The bitrate option for the Opus encoder, the higher the better for audio quality.
2715    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
2716    pub enum OpusBitrate {
2717        Bits(i32),
2718        Max,
2719        Auto,
2720    }
2721
2722    impl OpusEncoderSampleDuration {
2723        pub fn get_num_samples(&self, channels: u16, sample_rate: u32) -> usize {
2724            let ms_m_10 = match self {
2725                Self::MilliSec2_5 => 25,
2726                Self::MilliSec5 => 50,
2727                Self::MilliSec10 => 100,
2728                Self::MilliSec20 => 200,
2729                Self::MilliSec40 => 400,
2730                Self::MilliSec60 => 600,
2731            };
2732            (sample_rate as usize * ms_m_10 as usize) * channels as usize / 10000
2733        }
2734    }
2735
2736    /// * The encoder options for Opus
2737    #[derive(Debug, Clone, Copy, PartialEq)]
2738    pub struct OpusEncoderOptions {
2739        /// * The tier 1 factor for Opus audio quality, bigger bitrate means better audio quality.
2740        pub bitrate: OpusBitrate,
2741
2742        /// * VBR mode for better compression, turn it off to get better audio quality.
2743        pub encode_vbr: bool,
2744
2745        /// * The opus encoder only eats these durations of the samples to encode.
2746        /// * Longer duration means better quality and compression.
2747        pub samples_cache_duration: OpusEncoderSampleDuration,
2748    }
2749
2750    impl OpusEncoderOptions {
2751        pub fn new() -> Self {
2752            Self {
2753                bitrate: OpusBitrate::Max,
2754                encode_vbr: false,
2755                samples_cache_duration: OpusEncoderSampleDuration::MilliSec60,
2756            }
2757        }
2758
2759        pub fn get_allowed_sample_rates(&self) -> [u32; OPUS_ALLOWED_SAMPLE_RATES.len()] {
2760            OPUS_ALLOWED_SAMPLE_RATES
2761        }
2762
2763        pub fn get_rounded_up_sample_rate(&self, sample_rate: u32) -> u32 {
2764            if sample_rate <= OPUS_MIN_SAMPLE_RATE {
2765                OPUS_MIN_SAMPLE_RATE
2766            } else if sample_rate >= OPUS_MAX_SAMPLE_RATE {
2767                OPUS_MAX_SAMPLE_RATE
2768            } else {
2769                for (l, h) in OPUS_ALLOWED_SAMPLE_RATES[..OPUS_ALLOWED_SAMPLE_RATES.len() - 1]
2770                    .iter()
2771                    .zip(OPUS_ALLOWED_SAMPLE_RATES[1..].iter())
2772                {
2773                    if sample_rate > *l && sample_rate <= *h {
2774                        return *h;
2775                    }
2776                }
2777                OPUS_MAX_SAMPLE_RATE
2778            }
2779        }
2780    }
2781
2782    impl Default for OpusEncoderOptions {
2783        fn default() -> Self {
2784            Self::new()
2785        }
2786    }
2787}
2788
2789pub mod flac {
2790    use std::collections::BTreeMap;
2791
2792    /// * The compression level of the FLAC file
2793    /// A higher number means less file size. Default compression level is 5
2794    #[derive(Debug, Clone, Copy, PartialEq)]
2795    pub enum FlacCompression {
2796        /// Almost no compression
2797        Level0 = 0,
2798        Level1 = 1,
2799        Level2 = 2,
2800        Level3 = 3,
2801        Level4 = 4,
2802        Level5 = 5,
2803        Level6 = 6,
2804        Level7 = 7,
2805
2806        /// Maximum compression
2807        Level8 = 8,
2808    }
2809
2810    #[derive(Debug, Clone, Copy, PartialEq)]
2811    pub struct FlacEncoderParams {
2812        /// * If set to true, the FLAC encoder will send the encoded data to a decoder to verify if the encoding is successful, and the encoding process will be slower.
2813        pub verify_decoded: bool,
2814
2815        /// * The compression level of the FLAC file, a higher number means less file size.
2816        pub compression: FlacCompression,
2817
2818        /// * Num channels of the audio file, max channels is 8.
2819        pub channels: u16,
2820
2821        /// * The sample rate of the audio file. Every FLAC frame contains this value.
2822        pub sample_rate: u32,
2823
2824        /// * How many bits in an `i32` are valid for a sample, for example, if this value is 16, your `i32` sample should be between -32768 to +32767.
2825        ///   Because the FLAC encoder **only eats `[i32]`** , and you can't just pass `[i16]` to it.
2826        ///   It seems like 8, 12, 16, 20, 24, 32 are valid values for this field.
2827        pub bits_per_sample: u32,
2828
2829        /// * How many samples you will put into the encoder, set to zero if you don't know.
2830        pub total_samples_estimate: u64,
2831    }
2832
2833    pub fn get_listinfo_flacmeta() -> &'static BTreeMap<&'static str, &'static str> {
2834        use std::sync::OnceLock;
2835        static LISTINFO_FLACMETA: OnceLock<BTreeMap<&'static str, &'static str>> = OnceLock::new();
2836        LISTINFO_FLACMETA.get_or_init(|| {
2837            [
2838                ("ITRK", "TRACKNUMBER"),
2839                ("IART", "ARTIST"),
2840                ("INAM", "TITLE"),
2841                ("IPRD", "ALBUM"),
2842                ("ICMT", "COMMENT"),
2843                ("ICOP", "COPYRIGHT"),
2844                ("ICRD", "DATE"),
2845                ("IGNR", "GENRE"),
2846                ("ISRC", "ISRC"),
2847                ("ISFT", "ENCODER"),
2848                ("ISMP", "TIMECODE"),
2849                ("ILNG", "LANGUAGE"),
2850                ("ICMS", "PRODUCER"),
2851            ]
2852            .iter()
2853            .copied()
2854            .collect()
2855        })
2856    }
2857}
2858
2859pub mod oggvorbis {
2860    /// * OggVorbis encoder mode
2861    #[derive(Debug, Clone, Copy, Default, PartialEq)]
2862    pub enum OggVorbisMode {
2863        /// * Please use this mode, it works well because it just uses the WAV `data` chunk to encapsulate the whole Ogg Vorbis audio stream.
2864        #[default]
2865        OriginalStreamCompatible = 1,
2866
2867        /// * This mode works on some players. It separates the Ogg Vorbis header into the `fmt ` chunk extension data.
2868        /// * Some players with the Ogg Vorbis decoder for the WAV file may fail because the header is separated.
2869        HaveIndependentHeader = 2,
2870
2871        /// * Please don't use this mode. The encoder strips the Ogg Vorbis header in this mode, and to decode it, the decoder should use another encoder to create an Ogg Vorbis header for the audio stream to be decoded.
2872        /// * What if the decoder has a different version of `libvorbis`, the header info is misaligned to the audio body, then BAM, it's unable to play.
2873        /// * I'm still wondering why the Japanese developer invented this mode. to reduce the audio file size? Or to use the `fmt ` chunk info to create the header?
2874        /// * The result is that you can't control the bitrate, thus the file would be very large at full bitrate settings by default.
2875        HaveNoCodebookHeader = 3,
2876
2877        /// * Another mode that exists but doesn't work.
2878        /// * The naked Vorbis audio without Ogg encapsulation, invented by the author of FFmpeg? I guess.
2879        /// * Without the Ogg packet header `granule position` field, `libvorbis` is unable to decode it correctly.
2880        /// * The decoder will try to fake the Ogg encapsulation, and if you are lucky enough, it still has some chance to decode correctly.
2881        /// * BTW. FFmpeg can encode audio into this format, but can't decode it correctly.
2882        NakedVorbis = 4
2883    }
2884
2885    /// * OggVorbis encoder parameters, NOTE: Most of the comments or documents were copied from `vorbis_rs`
2886    #[derive(Debug, Clone, Copy, Default, PartialEq)]
2887    pub struct OggVorbisEncoderParams {
2888        /// OggVorbis encoder mode
2889        pub mode: OggVorbisMode,
2890
2891        /// Num channels
2892        pub channels: u16,
2893
2894        /// Sample rate
2895        pub sample_rate: u32,
2896
2897        /// The serials for the generated OggVorbis streams will be randomly generated, as dictated by the Ogg specification. If this behavior is not desirable, set this field to `Some(your_serial_number)`.
2898        pub stream_serial: Option<i32>,
2899
2900        /// OggVorbis bitrate strategy represents a bitrate management strategy that a OggVorbis encoder can use.
2901        pub bitrate: Option<OggVorbisBitrateStrategy>,
2902
2903        /// * Specifies the minimum size of OggVorbis stream data to put into each Ogg page, except for some header pages,
2904        /// * which have to be cut short to conform to the OggVorbis specification.
2905        /// * This value controls the tradeoff between Ogg encapsulation overhead and ease of seeking and packet loss concealment.
2906        /// * By default, it is set to None, which lets the encoder decide.
2907        pub minimum_page_data_size: Option<u16>,
2908    }
2909
2910    /// * OggVorbis bitrate strategy represents a bitrate management strategy that a OggVorbis encoder can use.
2911    #[derive(Debug, Clone, Copy, PartialEq)]
2912    pub enum OggVorbisBitrateStrategy {
2913        /// * Pure VBR quality mode, selected by a target bitrate (in bit/s).
2914        /// * The bitrate management engine is not enabled.
2915        /// * The average bitrate will usually be close to the target, but there are no guarantees.
2916        /// * Easier or harder than expected to encode audio may be encoded at a significantly different bitrate.
2917        Vbr(u32),
2918
2919        /// * Similar to `Vbr`, this encoding strategy fixes the output subjective quality level,
2920        /// * but lets OggVorbis vary the target bitrate depending on the qualities of the input signal.
2921        /// * An upside of this approach is that OggVorbis can automatically increase or decrease the target bitrate according to how difficult the signal is to encode,
2922        /// * which guarantees perceptually-consistent results while using an optimal bitrate.
2923        /// * Another upside is that there always is some mode to encode audio at a given quality level.
2924        /// * The downside is that the output bitrate is harder to predict across different types of audio signals.
2925        QualityVbr(f32),
2926
2927        /// * ABR mode, selected by an average bitrate (in bit/s).
2928        /// * The bitrate management engine is enabled to ensure that the instantaneous bitrate does not divert significantly from the specified average over time,
2929        /// * but no hard bitrate limits are imposed. Any bitrate fluctuations are guaranteed to be minor and short.
2930        Abr(u32),
2931
2932        /// * Constrained ABR mode, selected by a hard maximum bitrate (in bit/s).
2933        /// * The bitrate management engine is enabled to ensure that the instantaneous bitrate never exceeds the specified maximum bitrate,
2934        /// * which is a hard limit. Internally, the encoder will target an average bitrate that  s slightly lower than the specified maximum bitrate.
2935        /// * The stream is guaranteed to never go above the specified bitrate, at the cost of a lower bitrate,
2936        /// * and thus lower audio quality, on average.
2937        ConstrainedAbr(u32),
2938    }
2939}
2940