1use crate::diagnostics::codes::ValidationCode;
10use crate::diagnostics::{Category, Severity};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum IabCode {
22 CodecForbidden,
24 ElectrospatialFormulationForbidden,
26 QuantizationBitsMissing,
28 QuantizationBitsInvalid,
30 ContainerFormatMissing,
32 EssenceContainerInvalid,
34 AudioSamplingRateMissing,
36 AudioSamplingRateInvalid,
38 SoundCompressionMissing,
40 SoundCompressionInvalid,
42 ChannelCountNotZero,
44 SubDescriptorMissing,
46 MCATagSymbolMissing,
48 MCATagSymbolInvalid,
50 MCATagNameMissing,
52 MCATagNameInvalid,
54 MCALabelDictionaryIDMissing,
56 MCALabelDictionaryIDInvalid,
58 MainAudioMissing,
60 IABSequenceNoResources,
62 IABSequenceSourceEncodingInvalid,
64}
65
66macro_rules! define_iab_enum {
71 ($name:ident, $prefix:literal) => {
73 define_iab_enum!(@inner $name, $prefix, None);
74 };
75 ($name:ident, $prefix:literal, $previous:literal) => {
78 define_iab_enum!(@inner $name, $prefix, Some($previous));
79 };
80 (@inner $name:ident, $prefix:literal, $previous:expr) => {
81 #[doc = $prefix]
83 #[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
84 pub enum $name {
85 CodecForbidden,
87 ElectrospatialFormulationForbidden,
89 QuantizationBitsMissing,
91 QuantizationBitsInvalid,
93 ContainerFormatMissing,
95 EssenceContainerInvalid,
97 AudioSamplingRateMissing,
99 AudioSamplingRateInvalid,
101 SoundCompressionMissing,
103 SoundCompressionInvalid,
105 ChannelCountNotZero,
107 SubDescriptorMissing,
109 MCATagSymbolMissing,
111 MCATagSymbolInvalid,
113 MCATagNameMissing,
115 MCATagNameInvalid,
117 MCALabelDictionaryIDMissing,
119 MCALabelDictionaryIDInvalid,
121 MainAudioMissing,
123 IABSequenceNoResources,
125 IABSequenceSourceEncodingInvalid,
127 }
128
129 impl ValidationCode for $name {
130 fn code(&self) -> &'static str {
131 match self {
132 Self::CodecForbidden => concat!($prefix, ":5.9/CodecForbidden"),
133 Self::ElectrospatialFormulationForbidden => concat!($prefix, ":5.9/ElectrospatialFormulationForbidden"),
134 Self::QuantizationBitsMissing => concat!($prefix, ":5.9/QuantizationBitsMissing"),
135 Self::QuantizationBitsInvalid => concat!($prefix, ":5.9/QuantizationBitsInvalid"),
136 Self::ContainerFormatMissing => concat!($prefix, ":5.3/ContainerFormatMissing"),
137 Self::EssenceContainerInvalid => concat!($prefix, ":5.3/EssenceContainerInvalid"),
138 Self::AudioSamplingRateMissing => concat!($prefix, ":5.9/AudioSamplingRateMissing"),
139 Self::AudioSamplingRateInvalid => concat!($prefix, ":5.9/AudioSamplingRateInvalid"),
140 Self::SoundCompressionMissing => concat!($prefix, ":5.9/SoundCompressionMissing"),
141 Self::SoundCompressionInvalid => concat!($prefix, ":5.9/SoundCompressionInvalid"),
142 Self::ChannelCountNotZero => concat!($prefix, ":5.9/ChannelCountNotZero"),
143 Self::SubDescriptorMissing => concat!($prefix, ":5.9/SubDescriptorMissing"),
144 Self::MCATagSymbolMissing => concat!($prefix, ":5.9/MCATagSymbolMissing"),
145 Self::MCATagSymbolInvalid => concat!($prefix, ":5.9/MCATagSymbolInvalid"),
146 Self::MCATagNameMissing => concat!($prefix, ":5.9/MCATagNameMissing"),
147 Self::MCATagNameInvalid => concat!($prefix, ":5.9/MCATagNameInvalid"),
148 Self::MCALabelDictionaryIDMissing => concat!($prefix, ":5.9/MCALabelDictionaryIDMissing"),
149 Self::MCALabelDictionaryIDInvalid => concat!($prefix, ":5.9/MCALabelDictionaryIDInvalid"),
150 Self::MainAudioMissing => concat!($prefix, ":6.2/MainAudioMissing"),
151 Self::IABSequenceNoResources => concat!($prefix, ":6.2/IABSequenceNoResources"),
152 Self::IABSequenceSourceEncodingInvalid => concat!($prefix, ":6.2/IABSequenceSourceEncodingInvalid"),
153 }
154 }
155 fn description(&self) -> &'static str {
156 match self {
157 Self::CodecForbidden => "IABEssenceDescriptor: Codec item shall not be present (§5.9).",
158 Self::ElectrospatialFormulationForbidden => "IABEssenceDescriptor: ElectrospatialFormulation shall not be present (§5.9).",
159 Self::QuantizationBitsMissing => "IABEssenceDescriptor: QuantizationBits is missing; shall be 24.",
160 Self::QuantizationBitsInvalid => "IABEssenceDescriptor: QuantizationBits shall be 24.",
161 Self::ContainerFormatMissing => "IABEssenceDescriptor: ContainerFormat is missing.",
162 Self::EssenceContainerInvalid => "IABEssenceDescriptor: ContainerFormat is not the required IAB container UL.",
163 Self::AudioSamplingRateMissing => "IABEssenceDescriptor: AudioSampleRate is missing; shall be 48000/1.",
164 Self::AudioSamplingRateInvalid => "IABEssenceDescriptor: AudioSampleRate shall be 48000/1.",
165 Self::SoundCompressionMissing => "IABEssenceDescriptor: SoundCompression is missing.",
166 Self::SoundCompressionInvalid => "IABEssenceDescriptor: SoundCompression is not the required IAB compression UL.",
167 Self::ChannelCountNotZero => "IABEssenceDescriptor: ChannelCount shall be the distinguished value 0 (2019 edition).",
168 Self::SubDescriptorMissing => "IABEssenceDescriptor: IABSoundfieldLabelSubDescriptor shall be present.",
169 Self::MCATagSymbolMissing => "IABSoundfieldLabelSubDescriptor: MCATagSymbol is missing; shall be \"IAB\".",
170 Self::MCATagSymbolInvalid => "IABSoundfieldLabelSubDescriptor: MCATagSymbol shall be \"IAB\".",
171 Self::MCATagNameMissing => "IABSoundfieldLabelSubDescriptor: MCATagName is missing; shall be \"IAB\".",
172 Self::MCATagNameInvalid => "IABSoundfieldLabelSubDescriptor: MCATagName shall be \"IAB\".",
173 Self::MCALabelDictionaryIDMissing => "IABSoundfieldLabelSubDescriptor: MCALabelDictionaryID is missing.",
174 Self::MCALabelDictionaryIDInvalid => "IABSoundfieldLabelSubDescriptor: MCALabelDictionaryID is not the required IAB label UL.",
175 Self::MainAudioMissing => "Segment has IABSequence but no MainAudioSequence (§6.2).",
176 Self::IABSequenceNoResources => "IABSequence shall contain at least one Resource (§6.2).",
177 Self::IABSequenceSourceEncodingInvalid => "IABSequence Resource.SourceEncoding does not reference an IABEssenceDescriptor (§6.2).",
178 }
179 }
180 fn default_severity(&self) -> Severity {
181 match self {
182 Self::QuantizationBitsMissing
183 | Self::ContainerFormatMissing
184 | Self::AudioSamplingRateMissing
185 | Self::SoundCompressionMissing => Severity::Warning,
186 _ => Severity::Error,
187 }
188 }
189 fn category(&self) -> Category {
190 Category::Audio
191 }
192 fn previous_identical_edition(&self) -> Option<&'static str> {
193 $previous
194 }
195 fn example(&self) -> Option<&'static str> {
196 Some(match self {
197 Self::CodecForbidden =>
198 "IABEssenceDescriptor with a non-empty Codec UL — ST 2067-201 forbids the item entirely.",
199 Self::ElectrospatialFormulationForbidden =>
200 "IABEssenceDescriptor sets ElectrospatialFormulation; the item shall be absent.",
201 Self::QuantizationBitsMissing =>
202 "IABEssenceDescriptor with no QuantizationBits item.",
203 Self::QuantizationBitsInvalid =>
204 "IABEssenceDescriptor with QuantizationBits = 16 instead of 24.",
205 Self::ContainerFormatMissing =>
206 "IABEssenceDescriptor with no ContainerFormat (EssenceContainer) UL.",
207 Self::EssenceContainerInvalid =>
208 "IABEssenceDescriptor with ContainerFormat = `0d010301.020c0900` (WAV) instead of the IAB frame-wrapped UL.",
209 Self::AudioSamplingRateMissing =>
210 "IABEssenceDescriptor with no AudioSampleRate.",
211 Self::AudioSamplingRateInvalid =>
212 "IABEssenceDescriptor with AudioSampleRate = 96000/1 instead of 48000/1.",
213 Self::SoundCompressionMissing =>
214 "IABEssenceDescriptor with no SoundCompression UL.",
215 Self::SoundCompressionInvalid =>
216 "IABEssenceDescriptor with SoundCompression = uncompressed PCM UL instead of the IAB compression UL.",
217 Self::ChannelCountNotZero =>
218 "IABEssenceDescriptor with ChannelCount = 2 — the 2019 edition mandates the distinguished value 0.",
219 Self::SubDescriptorMissing =>
220 "IABEssenceDescriptor with no IABSoundfieldLabelSubDescriptor in its SubDescriptors strong-ref list.",
221 Self::MCATagSymbolMissing =>
222 "IABSoundfieldLabelSubDescriptor with no MCATagSymbol item.",
223 Self::MCATagSymbolInvalid =>
224 "IABSoundfieldLabelSubDescriptor with MCATagSymbol = \"71\" instead of \"IAB\".",
225 Self::MCATagNameMissing =>
226 "IABSoundfieldLabelSubDescriptor with no MCATagName item.",
227 Self::MCATagNameInvalid =>
228 "IABSoundfieldLabelSubDescriptor with MCATagName = \"Immersive Audio Bitstream\" instead of \"IAB\".",
229 Self::MCALabelDictionaryIDMissing =>
230 "IABSoundfieldLabelSubDescriptor with no MCALabelDictionaryID UL.",
231 Self::MCALabelDictionaryIDInvalid =>
232 "IABSoundfieldLabelSubDescriptor with MCALabelDictionaryID set to a 5.1 surround UL instead of the IAB label UL.",
233 Self::MainAudioMissing =>
234 "Segment contains an IABSequence but no MainAudioSequence — required pairing per §6.2.",
235 Self::IABSequenceNoResources =>
236 "An IABSequence with an empty `<ResourceList>`.",
237 Self::IABSequenceSourceEncodingInvalid =>
238 "An IABSequence Resource whose SourceEncoding references a WAVEPCMDescriptor instead of an IABEssenceDescriptor.",
239 })
240 }
241 }
242
243 impl $name {
244 pub const ALL: &'static [Self] = &[
245 Self::CodecForbidden,
246 Self::ElectrospatialFormulationForbidden,
247 Self::QuantizationBitsMissing,
248 Self::QuantizationBitsInvalid,
249 Self::ContainerFormatMissing,
250 Self::EssenceContainerInvalid,
251 Self::AudioSamplingRateMissing,
252 Self::AudioSamplingRateInvalid,
253 Self::SoundCompressionMissing,
254 Self::SoundCompressionInvalid,
255 Self::ChannelCountNotZero,
256 Self::SubDescriptorMissing,
257 Self::MCATagSymbolMissing,
258 Self::MCATagSymbolInvalid,
259 Self::MCATagNameMissing,
260 Self::MCATagNameInvalid,
261 Self::MCALabelDictionaryIDMissing,
262 Self::MCALabelDictionaryIDInvalid,
263 Self::MainAudioMissing,
264 Self::IABSequenceNoResources,
265 Self::IABSequenceSourceEncodingInvalid,
266 ];
267
268 pub fn for_code(r: IabCode) -> &'static str {
271 match r {
272 IabCode::CodecForbidden => concat!($prefix, ":5.9/CodecForbidden"),
273 IabCode::ElectrospatialFormulationForbidden => concat!($prefix, ":5.9/ElectrospatialFormulationForbidden"),
274 IabCode::QuantizationBitsMissing => concat!($prefix, ":5.9/QuantizationBitsMissing"),
275 IabCode::QuantizationBitsInvalid => concat!($prefix, ":5.9/QuantizationBitsInvalid"),
276 IabCode::ContainerFormatMissing => concat!($prefix, ":5.3/ContainerFormatMissing"),
277 IabCode::EssenceContainerInvalid => concat!($prefix, ":5.3/EssenceContainerInvalid"),
278 IabCode::AudioSamplingRateMissing => concat!($prefix, ":5.9/AudioSamplingRateMissing"),
279 IabCode::AudioSamplingRateInvalid => concat!($prefix, ":5.9/AudioSamplingRateInvalid"),
280 IabCode::SoundCompressionMissing => concat!($prefix, ":5.9/SoundCompressionMissing"),
281 IabCode::SoundCompressionInvalid => concat!($prefix, ":5.9/SoundCompressionInvalid"),
282 IabCode::ChannelCountNotZero => concat!($prefix, ":5.9/ChannelCountNotZero"),
283 IabCode::SubDescriptorMissing => concat!($prefix, ":5.9/SubDescriptorMissing"),
284 IabCode::MCATagSymbolMissing => concat!($prefix, ":5.9/MCATagSymbolMissing"),
285 IabCode::MCATagSymbolInvalid => concat!($prefix, ":5.9/MCATagSymbolInvalid"),
286 IabCode::MCATagNameMissing => concat!($prefix, ":5.9/MCATagNameMissing"),
287 IabCode::MCATagNameInvalid => concat!($prefix, ":5.9/MCATagNameInvalid"),
288 IabCode::MCALabelDictionaryIDMissing => concat!($prefix, ":5.9/MCALabelDictionaryIDMissing"),
289 IabCode::MCALabelDictionaryIDInvalid => concat!($prefix, ":5.9/MCALabelDictionaryIDInvalid"),
290 IabCode::MainAudioMissing => concat!($prefix, ":6.2/MainAudioMissing"),
291 IabCode::IABSequenceNoResources => concat!($prefix, ":6.2/IABSequenceNoResources"),
292 IabCode::IABSequenceSourceEncodingInvalid => concat!($prefix, ":6.2/IABSequenceSourceEncodingInvalid"),
293 }
294 }
295 }
296
297 impl From<$name> for String {
298 fn from(c: $name) -> String {
299 c.code().to_string()
300 }
301 }
302 };
303}
304
305define_iab_enum!(St2067_201_2019, "ST2067-201:2019");
306define_iab_enum!(St2067_201_2021, "ST2067-201:2021", "ST2067-201:2019");
308
309#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
325pub enum St2067_201_2026Delta {
326 IabChannelSubDescriptorRecommended,
330}
331
332impl crate::diagnostics::codes::ValidationCode for St2067_201_2026Delta {
333 fn code(&self) -> &'static str {
334 match self {
335 Self::IabChannelSubDescriptorRecommended => {
336 "ST2067-201:2026:Annex-E/IabChannelSubDescriptorRecommended"
337 }
338 }
339 }
340 fn description(&self) -> &'static str {
341 match self {
342 Self::IabChannelSubDescriptorRecommended =>
343 "Track File should carry an IABChannelSubDescriptor for each channel of each BedDefinition (Annex E).",
344 }
345 }
346 fn default_severity(&self) -> crate::diagnostics::Severity {
347 crate::diagnostics::Severity::Warning
349 }
350 fn category(&self) -> crate::diagnostics::Category {
351 crate::diagnostics::Category::Audio
352 }
353 fn example(&self) -> Option<&'static str> {
354 Some(
355 "<IABEssenceDescriptor><SubDescriptors><IABSoundfieldLabelSubDescriptor/></SubDescriptors></IABEssenceDescriptor> \
356 <!-- no <IABChannelSubDescriptor> entries -->",
357 )
358 }
359}
360
361impl St2067_201_2026Delta {
362 pub const ALL: &'static [Self] = &[Self::IabChannelSubDescriptorRecommended];
363}
364
365impl From<St2067_201_2026Delta> for String {
366 fn from(c: St2067_201_2026Delta) -> String {
367 use crate::diagnostics::codes::ValidationCode;
368 c.code().to_string()
369 }
370}