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#[derive(Debug, Clone, PartialEq)]
23#[allow(clippy::large_enum_variant)]
24pub enum DataFormat {
25 Unspecified,
27
28 Pcm,
30
31 Adpcm(AdpcmSubFormat),
33
34 PcmALaw,
37
38 PcmMuLaw,
40
41 Mp3(Mp3EncoderOptions),
45
46 Opus(OpusEncoderOptions),
51
52 Flac(FlacEncoderParams),
56
57 OggVorbis(OggVorbisEncoderParams),
60}
61
62#[derive(Debug, Clone, Copy, PartialEq)]
65#[repr(u16)]
66pub enum AdpcmSubFormat {
67 Ms = 0x0002,
69
70 Ima = 0x0011,
72
73 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#[derive(Debug, Clone, Copy)]
136pub enum SampleFormat {
137 Unknown,
138
139 Float,
141
142 UInt,
144
145 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#[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#[derive(Debug, Clone, Copy)]
296pub struct Spec {
297 pub channels: u16,
299
300 pub channel_mask: u32,
302
303 pub sample_rate: u32,
305
306 pub bits_per_sample: u16,
308
309 pub sample_format: SampleFormat,
311}
312
313impl Default for Spec {
314 fn default() -> Self {
315 Self::new()
316 }
317}
318
319#[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 (_, _) => 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 pub fn get_sample_type(&self) -> WaveSampleType {
350 get_sample_type(self.bits_per_sample, self.sample_format)
351 }
352
353 pub fn guess_channel_mask(&self) -> Result<u32, AudioError> {
355 Ok(speaker_positions::guess_channel_mask(self.channels)?)
356 }
357
358 pub fn channel_mask_to_speaker_positions(&self) -> Vec<u32> {
360 speaker_positions::channel_mask_to_speaker_positions(self.channel_mask)
361 }
362
363 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 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 pub fn is_channel_mask_valid(&self) -> bool {
383 speaker_positions::is_channel_mask_valid(self.channels, self.channel_mask)
384 }
385}
386
387pub struct ChunkWriter<'a> {
391 pub writer: &'a mut dyn Writer,
392 pub flag: [u8; 4],
393 pub pos_of_chunk_len: u64, pub chunk_start: u64, 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 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 pub fn end(self) {}
432
433 fn on_drop(&mut self) -> Result<(), AudioWriteError> {
434 if self.ended {
435 return Ok(());
436 }
437 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 } self.ended = true;
479 Ok(())
480 }
481
482 pub fn get_chunk_start_pos(&self) -> u64 {
484 self.chunk_start
485 }
486
487 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#[derive(Clone, Copy)]
501pub struct ChunkHeader {
502 pub flag: [u8; 4],
504
505 pub size: u32,
507
508 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 pub fn align(addr: u64) -> u64 {
543 addr + (addr & 1)
544 }
545
546 pub fn next_chunk_pos(&self) -> u64 {
548 Self::align(self.chunk_start_pos + self.size as u64)
549 }
550
551 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 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#[derive(Debug, Clone)]
588pub struct FmtChunk {
589 pub format_tag: u16,
591
592 pub channels: u16,
594
595 pub sample_rate: u32,
599
600 pub byte_rate: u32,
602
603 pub block_align: u16,
606
607 pub bits_per_sample: u16,
609
610 pub extension: Option<FmtExtension>,
612}
613
614#[derive(Debug, Clone)]
616pub struct FmtExtension {
617 pub ext_len: u16,
619
620 pub data: ExtensionData,
622}
623
624#[derive(Debug, Clone)]
626pub enum ExtensionData {
627 Nodata,
629
630 AdpcmMs(AdpcmMsData),
632
633 AdpcmIma(AdpcmImaData),
635
636 Mp3(Mp3Data),
638
639 Vorbis(VorbisHeaderData),
641
642 OggVorbis(OggVorbisData),
644
645 OggVorbisWithHeader(OggVorbisWithHeaderData),
647
648 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#[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#[derive(Debug, Clone, Copy)]
685pub struct AdpcmImaData {
686 pub samples_per_block: u16,
687}
688
689#[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#[derive(Default, Clone)]
701pub struct VorbisHeaderData {
702 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#[derive(Clone, Copy)]
739pub struct OggVorbisData {
740 pub codec_version: u32,
742
743 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#[derive(Clone)]
759pub struct OggVorbisWithHeaderData {
760 pub codec_version: u32,
762
763 pub vorbis_version: u32,
766
767 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#[derive(Debug, Clone, Copy)]
783pub struct ExtensibleData {
784 pub valid_bits_per_sample: u16,
786
787 pub channel_mask: u32,
789
790 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, }
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, }
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#[derive(Debug, Clone, Copy, Default)]
1255pub struct SlntChunk {
1256 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#[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#[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#[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(¬e.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, <xt.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(<xt.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 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 let mut dict = BTreeMap::<String, String>::new();
1877 while reader.stream_position()? < end_of_chunk {
1878 let key_chunk = ChunkHeader::read(reader)?; 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 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 continue;
1891 }
1892 if dict.contains_key(&key_uppercase) {
1893 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
1926pub 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
1955pub 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#[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
2102pub 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#[derive(Debug, Clone, Default)]
2156pub struct FullInfoCuePoint {
2157 pub data_chunk_id: [u8; 4],
2158
2159 pub label: String,
2161
2162 pub note: String,
2164
2165 pub sample_length: u32,
2167
2168 pub purpose_id: String,
2170
2171 pub country: String,
2173
2174 pub language: String,
2176
2177 pub text_data: String,
2179
2180 pub media_type: u32,
2182
2183 pub file_data: Vec<u8>,
2185
2186 pub start_sample: u32,
2188
2189 pub num_samples: u32,
2191
2192 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(<xt.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
2273pub 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#[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#[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 #[derive(Debug, Clone, Copy, PartialEq)]
2533 #[repr(u8)]
2534 pub enum Mp3Channels {
2535 Mono = 3,
2537
2538 Stereo = 0,
2540
2541 JointStereo = 1,
2543
2544 DualChannel = 2,
2546
2547 NotSet = 4,
2549 }
2550
2551 #[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 #[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 Kbps64 = 64,
2581 Kbps80 = 80,
2582 Kbps96 = 96,
2583 Kbps112 = 112,
2584
2585 Kbps128 = 128,
2587 Kbps160 = 160,
2588 Kbps192 = 192,
2589 Kbps224 = 224,
2590
2591 Kbps256 = 256,
2593
2594 Kbps320 = 320,
2596 }
2597
2598 #[derive(Debug, Clone, Copy, PartialEq)]
2600 #[repr(u8)]
2601 pub enum Mp3VbrMode {
2602 Off = 0,
2604
2605 Mt = 1,
2606 Rh = 2,
2607 Abr = 3,
2608
2609 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 #[derive(Debug, Clone, PartialEq)]
2627 pub struct Mp3EncoderOptions {
2628 pub channels: Mp3Channels,
2630
2631 pub quality: Mp3Quality,
2633
2634 pub bitrate: Mp3Bitrate,
2636
2637 pub vbr_mode: Mp3VbrMode,
2639
2640 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 #[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 #[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 #[derive(Debug, Clone, Copy, PartialEq)]
2738 pub struct OpusEncoderOptions {
2739 pub bitrate: OpusBitrate,
2741
2742 pub encode_vbr: bool,
2744
2745 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 #[derive(Debug, Clone, Copy, PartialEq)]
2795 pub enum FlacCompression {
2796 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 Level8 = 8,
2808 }
2809
2810 #[derive(Debug, Clone, Copy, PartialEq)]
2811 pub struct FlacEncoderParams {
2812 pub verify_decoded: bool,
2814
2815 pub compression: FlacCompression,
2817
2818 pub channels: u16,
2820
2821 pub sample_rate: u32,
2823
2824 pub bits_per_sample: u32,
2828
2829 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 #[derive(Debug, Clone, Copy, Default, PartialEq)]
2862 pub enum OggVorbisMode {
2863 #[default]
2865 OriginalStreamCompatible = 1,
2866
2867 HaveIndependentHeader = 2,
2870
2871 HaveNoCodebookHeader = 3,
2876
2877 NakedVorbis = 4
2883 }
2884
2885 #[derive(Debug, Clone, Copy, Default, PartialEq)]
2887 pub struct OggVorbisEncoderParams {
2888 pub mode: OggVorbisMode,
2890
2891 pub channels: u16,
2893
2894 pub sample_rate: u32,
2896
2897 pub stream_serial: Option<i32>,
2899
2900 pub bitrate: Option<OggVorbisBitrateStrategy>,
2902
2903 pub minimum_page_data_size: Option<u16>,
2908 }
2909
2910 #[derive(Debug, Clone, Copy, PartialEq)]
2912 pub enum OggVorbisBitrateStrategy {
2913 Vbr(u32),
2918
2919 QualityVbr(f32),
2926
2927 Abr(u32),
2931
2932 ConstrainedAbr(u32),
2938 }
2939}
2940