use crate::diagnostics::codes::ValidationCode;
use crate::diagnostics::{Category, Severity};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IabCode {
CodecForbidden,
ElectrospatialFormulationForbidden,
QuantizationBitsMissing,
QuantizationBitsInvalid,
ContainerFormatMissing,
EssenceContainerInvalid,
AudioSamplingRateMissing,
AudioSamplingRateInvalid,
SoundCompressionMissing,
SoundCompressionInvalid,
ChannelCountNotZero,
SubDescriptorMissing,
MCATagSymbolMissing,
MCATagSymbolInvalid,
MCATagNameMissing,
MCATagNameInvalid,
MCALabelDictionaryIDMissing,
MCALabelDictionaryIDInvalid,
MainAudioMissing,
IABSequenceNoResources,
IABSequenceSourceEncodingInvalid,
}
macro_rules! define_iab_enum {
($name:ident, $prefix:literal) => {
define_iab_enum!(@inner $name, $prefix, None);
};
($name:ident, $prefix:literal, $previous:literal) => {
define_iab_enum!(@inner $name, $prefix, Some($previous));
};
(@inner $name:ident, $prefix:literal, $previous:expr) => {
#[doc = $prefix]
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
pub enum $name {
CodecForbidden,
ElectrospatialFormulationForbidden,
QuantizationBitsMissing,
QuantizationBitsInvalid,
ContainerFormatMissing,
EssenceContainerInvalid,
AudioSamplingRateMissing,
AudioSamplingRateInvalid,
SoundCompressionMissing,
SoundCompressionInvalid,
ChannelCountNotZero,
SubDescriptorMissing,
MCATagSymbolMissing,
MCATagSymbolInvalid,
MCATagNameMissing,
MCATagNameInvalid,
MCALabelDictionaryIDMissing,
MCALabelDictionaryIDInvalid,
MainAudioMissing,
IABSequenceNoResources,
IABSequenceSourceEncodingInvalid,
}
impl ValidationCode for $name {
fn code(&self) -> &'static str {
match self {
Self::CodecForbidden => concat!($prefix, ":5.9/CodecForbidden"),
Self::ElectrospatialFormulationForbidden => concat!($prefix, ":5.9/ElectrospatialFormulationForbidden"),
Self::QuantizationBitsMissing => concat!($prefix, ":5.9/QuantizationBitsMissing"),
Self::QuantizationBitsInvalid => concat!($prefix, ":5.9/QuantizationBitsInvalid"),
Self::ContainerFormatMissing => concat!($prefix, ":5.3/ContainerFormatMissing"),
Self::EssenceContainerInvalid => concat!($prefix, ":5.3/EssenceContainerInvalid"),
Self::AudioSamplingRateMissing => concat!($prefix, ":5.9/AudioSamplingRateMissing"),
Self::AudioSamplingRateInvalid => concat!($prefix, ":5.9/AudioSamplingRateInvalid"),
Self::SoundCompressionMissing => concat!($prefix, ":5.9/SoundCompressionMissing"),
Self::SoundCompressionInvalid => concat!($prefix, ":5.9/SoundCompressionInvalid"),
Self::ChannelCountNotZero => concat!($prefix, ":5.9/ChannelCountNotZero"),
Self::SubDescriptorMissing => concat!($prefix, ":5.9/SubDescriptorMissing"),
Self::MCATagSymbolMissing => concat!($prefix, ":5.9/MCATagSymbolMissing"),
Self::MCATagSymbolInvalid => concat!($prefix, ":5.9/MCATagSymbolInvalid"),
Self::MCATagNameMissing => concat!($prefix, ":5.9/MCATagNameMissing"),
Self::MCATagNameInvalid => concat!($prefix, ":5.9/MCATagNameInvalid"),
Self::MCALabelDictionaryIDMissing => concat!($prefix, ":5.9/MCALabelDictionaryIDMissing"),
Self::MCALabelDictionaryIDInvalid => concat!($prefix, ":5.9/MCALabelDictionaryIDInvalid"),
Self::MainAudioMissing => concat!($prefix, ":6.2/MainAudioMissing"),
Self::IABSequenceNoResources => concat!($prefix, ":6.2/IABSequenceNoResources"),
Self::IABSequenceSourceEncodingInvalid => concat!($prefix, ":6.2/IABSequenceSourceEncodingInvalid"),
}
}
fn description(&self) -> &'static str {
match self {
Self::CodecForbidden => "IABEssenceDescriptor: Codec item shall not be present (§5.9).",
Self::ElectrospatialFormulationForbidden => "IABEssenceDescriptor: ElectrospatialFormulation shall not be present (§5.9).",
Self::QuantizationBitsMissing => "IABEssenceDescriptor: QuantizationBits is missing; shall be 24.",
Self::QuantizationBitsInvalid => "IABEssenceDescriptor: QuantizationBits shall be 24.",
Self::ContainerFormatMissing => "IABEssenceDescriptor: ContainerFormat is missing.",
Self::EssenceContainerInvalid => "IABEssenceDescriptor: ContainerFormat is not the required IAB container UL.",
Self::AudioSamplingRateMissing => "IABEssenceDescriptor: AudioSampleRate is missing; shall be 48000/1.",
Self::AudioSamplingRateInvalid => "IABEssenceDescriptor: AudioSampleRate shall be 48000/1.",
Self::SoundCompressionMissing => "IABEssenceDescriptor: SoundCompression is missing.",
Self::SoundCompressionInvalid => "IABEssenceDescriptor: SoundCompression is not the required IAB compression UL.",
Self::ChannelCountNotZero => "IABEssenceDescriptor: ChannelCount shall be the distinguished value 0 (2019 edition).",
Self::SubDescriptorMissing => "IABEssenceDescriptor: IABSoundfieldLabelSubDescriptor shall be present.",
Self::MCATagSymbolMissing => "IABSoundfieldLabelSubDescriptor: MCATagSymbol is missing; shall be \"IAB\".",
Self::MCATagSymbolInvalid => "IABSoundfieldLabelSubDescriptor: MCATagSymbol shall be \"IAB\".",
Self::MCATagNameMissing => "IABSoundfieldLabelSubDescriptor: MCATagName is missing; shall be \"IAB\".",
Self::MCATagNameInvalid => "IABSoundfieldLabelSubDescriptor: MCATagName shall be \"IAB\".",
Self::MCALabelDictionaryIDMissing => "IABSoundfieldLabelSubDescriptor: MCALabelDictionaryID is missing.",
Self::MCALabelDictionaryIDInvalid => "IABSoundfieldLabelSubDescriptor: MCALabelDictionaryID is not the required IAB label UL.",
Self::MainAudioMissing => "Segment has IABSequence but no MainAudioSequence (§6.2).",
Self::IABSequenceNoResources => "IABSequence shall contain at least one Resource (§6.2).",
Self::IABSequenceSourceEncodingInvalid => "IABSequence Resource.SourceEncoding does not reference an IABEssenceDescriptor (§6.2).",
}
}
fn default_severity(&self) -> Severity {
match self {
Self::QuantizationBitsMissing
| Self::ContainerFormatMissing
| Self::AudioSamplingRateMissing
| Self::SoundCompressionMissing => Severity::Warning,
_ => Severity::Error,
}
}
fn category(&self) -> Category {
Category::Audio
}
fn previous_identical_edition(&self) -> Option<&'static str> {
$previous
}
fn example(&self) -> Option<&'static str> {
Some(match self {
Self::CodecForbidden =>
"IABEssenceDescriptor with a non-empty Codec UL — ST 2067-201 forbids the item entirely.",
Self::ElectrospatialFormulationForbidden =>
"IABEssenceDescriptor sets ElectrospatialFormulation; the item shall be absent.",
Self::QuantizationBitsMissing =>
"IABEssenceDescriptor with no QuantizationBits item.",
Self::QuantizationBitsInvalid =>
"IABEssenceDescriptor with QuantizationBits = 16 instead of 24.",
Self::ContainerFormatMissing =>
"IABEssenceDescriptor with no ContainerFormat (EssenceContainer) UL.",
Self::EssenceContainerInvalid =>
"IABEssenceDescriptor with ContainerFormat = `0d010301.020c0900` (WAV) instead of the IAB frame-wrapped UL.",
Self::AudioSamplingRateMissing =>
"IABEssenceDescriptor with no AudioSampleRate.",
Self::AudioSamplingRateInvalid =>
"IABEssenceDescriptor with AudioSampleRate = 96000/1 instead of 48000/1.",
Self::SoundCompressionMissing =>
"IABEssenceDescriptor with no SoundCompression UL.",
Self::SoundCompressionInvalid =>
"IABEssenceDescriptor with SoundCompression = uncompressed PCM UL instead of the IAB compression UL.",
Self::ChannelCountNotZero =>
"IABEssenceDescriptor with ChannelCount = 2 — the 2019 edition mandates the distinguished value 0.",
Self::SubDescriptorMissing =>
"IABEssenceDescriptor with no IABSoundfieldLabelSubDescriptor in its SubDescriptors strong-ref list.",
Self::MCATagSymbolMissing =>
"IABSoundfieldLabelSubDescriptor with no MCATagSymbol item.",
Self::MCATagSymbolInvalid =>
"IABSoundfieldLabelSubDescriptor with MCATagSymbol = \"71\" instead of \"IAB\".",
Self::MCATagNameMissing =>
"IABSoundfieldLabelSubDescriptor with no MCATagName item.",
Self::MCATagNameInvalid =>
"IABSoundfieldLabelSubDescriptor with MCATagName = \"Immersive Audio Bitstream\" instead of \"IAB\".",
Self::MCALabelDictionaryIDMissing =>
"IABSoundfieldLabelSubDescriptor with no MCALabelDictionaryID UL.",
Self::MCALabelDictionaryIDInvalid =>
"IABSoundfieldLabelSubDescriptor with MCALabelDictionaryID set to a 5.1 surround UL instead of the IAB label UL.",
Self::MainAudioMissing =>
"Segment contains an IABSequence but no MainAudioSequence — required pairing per §6.2.",
Self::IABSequenceNoResources =>
"An IABSequence with an empty `<ResourceList>`.",
Self::IABSequenceSourceEncodingInvalid =>
"An IABSequence Resource whose SourceEncoding references a WAVEPCMDescriptor instead of an IABEssenceDescriptor.",
})
}
}
impl $name {
pub const ALL: &'static [Self] = &[
Self::CodecForbidden,
Self::ElectrospatialFormulationForbidden,
Self::QuantizationBitsMissing,
Self::QuantizationBitsInvalid,
Self::ContainerFormatMissing,
Self::EssenceContainerInvalid,
Self::AudioSamplingRateMissing,
Self::AudioSamplingRateInvalid,
Self::SoundCompressionMissing,
Self::SoundCompressionInvalid,
Self::ChannelCountNotZero,
Self::SubDescriptorMissing,
Self::MCATagSymbolMissing,
Self::MCATagSymbolInvalid,
Self::MCATagNameMissing,
Self::MCATagNameInvalid,
Self::MCALabelDictionaryIDMissing,
Self::MCALabelDictionaryIDInvalid,
Self::MainAudioMissing,
Self::IABSequenceNoResources,
Self::IABSequenceSourceEncodingInvalid,
];
pub fn for_code(r: IabCode) -> &'static str {
match r {
IabCode::CodecForbidden => concat!($prefix, ":5.9/CodecForbidden"),
IabCode::ElectrospatialFormulationForbidden => concat!($prefix, ":5.9/ElectrospatialFormulationForbidden"),
IabCode::QuantizationBitsMissing => concat!($prefix, ":5.9/QuantizationBitsMissing"),
IabCode::QuantizationBitsInvalid => concat!($prefix, ":5.9/QuantizationBitsInvalid"),
IabCode::ContainerFormatMissing => concat!($prefix, ":5.3/ContainerFormatMissing"),
IabCode::EssenceContainerInvalid => concat!($prefix, ":5.3/EssenceContainerInvalid"),
IabCode::AudioSamplingRateMissing => concat!($prefix, ":5.9/AudioSamplingRateMissing"),
IabCode::AudioSamplingRateInvalid => concat!($prefix, ":5.9/AudioSamplingRateInvalid"),
IabCode::SoundCompressionMissing => concat!($prefix, ":5.9/SoundCompressionMissing"),
IabCode::SoundCompressionInvalid => concat!($prefix, ":5.9/SoundCompressionInvalid"),
IabCode::ChannelCountNotZero => concat!($prefix, ":5.9/ChannelCountNotZero"),
IabCode::SubDescriptorMissing => concat!($prefix, ":5.9/SubDescriptorMissing"),
IabCode::MCATagSymbolMissing => concat!($prefix, ":5.9/MCATagSymbolMissing"),
IabCode::MCATagSymbolInvalid => concat!($prefix, ":5.9/MCATagSymbolInvalid"),
IabCode::MCATagNameMissing => concat!($prefix, ":5.9/MCATagNameMissing"),
IabCode::MCATagNameInvalid => concat!($prefix, ":5.9/MCATagNameInvalid"),
IabCode::MCALabelDictionaryIDMissing => concat!($prefix, ":5.9/MCALabelDictionaryIDMissing"),
IabCode::MCALabelDictionaryIDInvalid => concat!($prefix, ":5.9/MCALabelDictionaryIDInvalid"),
IabCode::MainAudioMissing => concat!($prefix, ":6.2/MainAudioMissing"),
IabCode::IABSequenceNoResources => concat!($prefix, ":6.2/IABSequenceNoResources"),
IabCode::IABSequenceSourceEncodingInvalid => concat!($prefix, ":6.2/IABSequenceSourceEncodingInvalid"),
}
}
}
impl From<$name> for String {
fn from(c: $name) -> String {
c.code().to_string()
}
}
};
}
define_iab_enum!(St2067_201_2019, "ST2067-201:2019");
define_iab_enum!(St2067_201_2021, "ST2067-201:2021", "ST2067-201:2019");
#[derive(Debug, Clone, Copy, PartialEq, Eq, strum::EnumIter)]
pub enum St2067_201_2026Delta {
IabChannelSubDescriptorRecommended,
}
impl crate::diagnostics::codes::ValidationCode for St2067_201_2026Delta {
fn code(&self) -> &'static str {
match self {
Self::IabChannelSubDescriptorRecommended => {
"ST2067-201:2026:Annex-E/IabChannelSubDescriptorRecommended"
}
}
}
fn description(&self) -> &'static str {
match self {
Self::IabChannelSubDescriptorRecommended =>
"Track File should carry an IABChannelSubDescriptor for each channel of each BedDefinition (Annex E).",
}
}
fn default_severity(&self) -> crate::diagnostics::Severity {
crate::diagnostics::Severity::Warning
}
fn category(&self) -> crate::diagnostics::Category {
crate::diagnostics::Category::Audio
}
fn example(&self) -> Option<&'static str> {
Some(
"<IABEssenceDescriptor><SubDescriptors><IABSoundfieldLabelSubDescriptor/></SubDescriptors></IABEssenceDescriptor> \
<!-- no <IABChannelSubDescriptor> entries -->",
)
}
}
impl St2067_201_2026Delta {
pub const ALL: &'static [Self] = &[Self::IabChannelSubDescriptorRecommended];
}
impl From<St2067_201_2026Delta> for String {
fn from(c: St2067_201_2026Delta) -> String {
use crate::diagnostics::codes::ValidationCode;
c.code().to_string()
}
}