piaf/capabilities/cea861/
audio.rs1use crate::model::prelude::Vec;
2
3bitflags::bitflags! {
4 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
9 pub struct AudioSampleRates: u8 {
10 const HZ_32000 = 0x01;
12 const HZ_44100 = 0x02;
14 const HZ_48000 = 0x04;
16 const HZ_88200 = 0x08;
18 const HZ_96000 = 0x10;
20 const HZ_176400 = 0x20;
22 const HZ_192000 = 0x40;
24 }
25}
26
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum AudioFormat {
31 Lpcm,
33 Ac3,
35 Mpeg1,
37 Mp3,
39 Mpeg2Multichannel,
41 AacLc,
43 Dts,
45 Atrac,
47 OneBitAudio,
49 EnhancedAc3,
51 DtsHd,
53 MlpTrueHd,
55 Dst,
57 WmaPro,
59 Extended(u8),
61 Reserved(u8),
63}
64
65#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub enum AudioFormatInfo {
69 Lpcm {
71 depth_16: bool,
73 depth_20: bool,
75 depth_24: bool,
77 },
78 MaxBitrateKbps(u16),
80 Raw(u8),
82}
83
84#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct ShortAudioDescriptor {
88 pub format: AudioFormat,
90 pub max_channels: u8,
92 pub sample_rates: AudioSampleRates,
94 pub format_info: AudioFormatInfo,
96}
97
98pub(super) fn parse_audio_data_block(block_data: &[u8]) -> Vec<ShortAudioDescriptor> {
103 let mut out = Vec::new();
104 let mut j = 0;
105 while j + 3 <= block_data.len() {
106 let b0 = block_data[j];
107 let b1 = block_data[j + 1];
108 let b2 = block_data[j + 2];
109
110 let afc = (b0 >> 3) & 0x0F;
111 let max_channels = (b0 & 0x07) + 1;
112 let sample_rates = AudioSampleRates::from_bits_truncate(b1 & 0x7F);
113
114 let format = match afc {
115 1 => AudioFormat::Lpcm,
116 2 => AudioFormat::Ac3,
117 3 => AudioFormat::Mpeg1,
118 4 => AudioFormat::Mp3,
119 5 => AudioFormat::Mpeg2Multichannel,
120 6 => AudioFormat::AacLc,
121 7 => AudioFormat::Dts,
122 8 => AudioFormat::Atrac,
123 9 => AudioFormat::OneBitAudio,
124 10 => AudioFormat::EnhancedAc3,
125 11 => AudioFormat::DtsHd,
126 12 => AudioFormat::MlpTrueHd,
127 13 => AudioFormat::Dst,
128 14 => AudioFormat::WmaPro,
129 15 => AudioFormat::Extended((b2 >> 3) & 0x1F),
130 _ => AudioFormat::Reserved(afc),
131 };
132
133 let format_info = match afc {
134 1 => AudioFormatInfo::Lpcm {
135 depth_16: b2 & 0x01 != 0,
136 depth_20: b2 & 0x02 != 0,
137 depth_24: b2 & 0x04 != 0,
138 },
139 2..=8 => AudioFormatInfo::MaxBitrateKbps((b2 as u16) * 8),
140 _ => AudioFormatInfo::Raw(b2),
141 };
142
143 out.push(ShortAudioDescriptor {
144 format,
145 max_channels,
146 sample_rates,
147 format_info,
148 });
149 j += 3;
150 }
151 out
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_lpcm_2ch_stereo() {
160 let sads = parse_audio_data_block(&[0x09, 0x07, 0x07]);
165 assert_eq!(sads.len(), 1);
166 let sad = &sads[0];
167 assert_eq!(sad.format, AudioFormat::Lpcm);
168 assert_eq!(sad.max_channels, 2);
169 assert!(sad.sample_rates.contains(AudioSampleRates::HZ_32000));
170 assert!(sad.sample_rates.contains(AudioSampleRates::HZ_44100));
171 assert!(sad.sample_rates.contains(AudioSampleRates::HZ_48000));
172 assert!(!sad.sample_rates.contains(AudioSampleRates::HZ_96000));
173 assert_eq!(
174 sad.format_info,
175 AudioFormatInfo::Lpcm {
176 depth_16: true,
177 depth_20: true,
178 depth_24: true
179 }
180 );
181 }
182
183 #[test]
184 fn test_ac3_bitrate() {
185 let sads = parse_audio_data_block(&[0x15, 0x04, 80]);
190 assert_eq!(sads.len(), 1);
191 let sad = &sads[0];
192 assert_eq!(sad.format, AudioFormat::Ac3);
193 assert_eq!(sad.max_channels, 6);
194 assert_eq!(sad.format_info, AudioFormatInfo::MaxBitrateKbps(640));
195 }
196
197 #[test]
198 fn test_truehd_raw_byte3() {
199 let sads = parse_audio_data_block(&[0x67, 0x40, 0x00]);
204 assert_eq!(sads.len(), 1);
205 let sad = &sads[0];
206 assert_eq!(sad.format, AudioFormat::MlpTrueHd);
207 assert_eq!(sad.max_channels, 8);
208 assert!(sad.sample_rates.contains(AudioSampleRates::HZ_192000));
209 assert_eq!(sad.format_info, AudioFormatInfo::Raw(0x00));
210 }
211
212 #[test]
213 fn test_partial_trailing_bytes_ignored() {
214 let sads = parse_audio_data_block(&[0x09, 0x07, 0x07, 0xFF]);
216 assert_eq!(sads.len(), 1);
217 }
218
219 #[test]
220 fn test_multiple_sads() {
221 let sads = parse_audio_data_block(&[
223 0x09, 0x07, 0x07, 0x15, 0x04, 80, ]);
226 assert_eq!(sads.len(), 2);
227 assert_eq!(sads[0].format, AudioFormat::Lpcm);
228 assert_eq!(sads[1].format, AudioFormat::Ac3);
229 }
230}