use crate::{AudioChannels, PcmSample, PcmSpec, PcmWav, PcmWavError, is};
#[doc = crate::_tags!(audio parser)]
#[doc = crate::_doc_meta!{location("media/audio"), test_size_of(PcmWavFmt = 28|224)}]
#[must_use]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct PcmWavFmt {
pub format_tag: u16,
pub subformat_tag: u16,
pub valid_bits_per_sample: u16,
pub channel_mask: u32,
pub channels: u16,
pub sample_rate: u32,
pub byte_rate: u32,
pub block_align: u16,
pub bits_per_sample: u16,
pub extra_len: u16,
}
impl PcmWavFmt {
pub const fn from_spec(spec: PcmSpec) -> Result<Self, PcmWavError> {
let format_tag = if spec.sample.is_float() {
PcmWav::FORMAT_IEEE_FLOAT
} else if spec.sample.is_int() {
PcmWav::FORMAT_PCM
} else {
return Err(PcmWavError::UnsupportedBitsPerSample(spec.sample.bits() as u16));
};
Self::new(
format_tag,
spec.channel_count() as u16,
spec.sample_rate,
spec.sample.bits() as u16,
0, )
}
pub const fn new(
format_tag: u16,
channels: u16,
sample_rate: u32,
bits_per_sample: u16,
extra_len: u16,
) -> Result<Self, PcmWavError> {
match format_tag {
PcmWav::FORMAT_PCM | PcmWav::FORMAT_IEEE_FLOAT => {}
PcmWav::FORMAT_EXTENSIBLE => return Err(PcmWavError::InvalidExtensibleFormat),
other => return Err(PcmWavError::UnsupportedFormat(other)),
}
is! { channels == 0, return Err(PcmWavError::UnsupportedChannelCount(channels)) }
let bytes_per_sample = match PcmWav::bytes_per_sample(format_tag, bits_per_sample) {
Ok(bytes) => bytes,
Err(err) => return Err(err),
};
let Some(block_align) = channels.checked_mul(bytes_per_sample) else {
return Err(PcmWavError::InvalidBlockAlign);
};
let Some(byte_rate) = sample_rate.checked_mul(block_align as u32) else {
return Err(PcmWavError::InvalidByteRate);
};
Ok(Self {
format_tag,
subformat_tag: format_tag,
channels,
sample_rate,
byte_rate,
block_align,
bits_per_sample,
valid_bits_per_sample: bits_per_sample,
channel_mask: 0,
extra_len,
})
}
pub const fn new_extensible(
subformat_tag: u16,
channels: u16,
sample_rate: u32,
bits_per_sample: u16,
valid_bits_per_sample: u16,
channel_mask: u32,
) -> Result<Self, PcmWavError> {
match subformat_tag {
PcmWav::FORMAT_PCM | PcmWav::FORMAT_IEEE_FLOAT => {}
other => return Err(PcmWavError::UnsupportedFormat(other)),
}
if valid_bits_per_sample == 0 || valid_bits_per_sample > bits_per_sample {
return Err(PcmWavError::InvalidExtensibleFormat);
}
let mut fmt = match Self::new(
subformat_tag,
channels,
sample_rate,
bits_per_sample,
PcmWav::EXTENSIBLE_EXTRA_LEN,
) {
Ok(fmt) => fmt,
Err(err) => return Err(err),
};
fmt.format_tag = PcmWav::FORMAT_EXTENSIBLE;
fmt.subformat_tag = subformat_tag;
fmt.valid_bits_per_sample = valid_bits_per_sample;
fmt.channel_mask = channel_mask;
fmt.extra_len = PcmWav::EXTENSIBLE_EXTRA_LEN;
Ok(fmt)
}
pub const fn encoded_len(self) -> usize {
if self.format_tag == PcmWav::FORMAT_EXTENSIBLE {
PcmWav::EXTENSIBLE_LEN
} else if self.format_tag == PcmWav::FORMAT_PCM && self.extra_len == 0 {
Self::BASE_LEN
} else {
Self::WAVEFORMATEX_EMPTY_LEN + self.extra_len as usize
}
}
#[must_use]
pub const fn needs_fact(self) -> bool {
self.subformat_tag != PcmWav::FORMAT_PCM
}
pub const fn frames_for_data_len(self, data_len: usize) -> Result<usize, PcmWavError> {
let spec = match self.spec() {
Ok(spec) => spec,
Err(err) => return Err(err),
};
match spec.frames_for_data_len(data_len) {
Some(frames) => Ok(frames),
None => Err(PcmWavError::InvalidDataLength),
}
}
pub const fn validate(self) -> Result<Self, PcmWavError> {
is![self.channels == 0, return Err(PcmWavError::UnsupportedChannelCount(self.channels))];
match self.format_tag {
PcmWav::FORMAT_PCM | PcmWav::FORMAT_IEEE_FLOAT => {
if self.subformat_tag != self.format_tag {
return Err(PcmWavError::InvalidExtensibleFormat);
}
if self.valid_bits_per_sample != self.bits_per_sample {
return Err(PcmWavError::InvalidExtensibleFormat);
}
}
PcmWav::FORMAT_EXTENSIBLE => {
if self.extra_len < PcmWav::EXTENSIBLE_EXTRA_LEN {
return Err(PcmWavError::InvalidExtensibleFormat);
}
if self.valid_bits_per_sample == 0
|| self.valid_bits_per_sample > self.bits_per_sample
{
return Err(PcmWavError::InvalidExtensibleFormat);
}
}
other => return Err(PcmWavError::UnsupportedFormat(other)),
}
let bytes_per_sample =
match PcmWav::bytes_per_sample(self.subformat_tag, self.bits_per_sample) {
Ok(bytes) => bytes,
Err(err) => return Err(err),
};
let Some(expected_align) = self.channels.checked_mul(bytes_per_sample) else {
return Err(PcmWavError::InvalidBlockAlign);
};
if self.block_align != expected_align {
return Err(PcmWavError::InvalidBlockAlign);
}
let Some(expected_rate) = self.sample_rate.checked_mul(self.block_align as u32) else {
return Err(PcmWavError::InvalidByteRate);
};
is![self.byte_rate != expected_rate, return Err(PcmWavError::InvalidByteRate)];
Ok(self)
}
pub const fn spec(self) -> Result<PcmSpec, PcmWavError> {
let sample = match (self.subformat_tag, self.bits_per_sample) {
(PcmWav::FORMAT_PCM, 8) => PcmSample::U8,
(PcmWav::FORMAT_PCM, 16) => PcmSample::I16,
(PcmWav::FORMAT_PCM, 24) => PcmSample::I24,
(PcmWav::FORMAT_PCM, 32) => PcmSample::I32,
(PcmWav::FORMAT_IEEE_FLOAT, 32) => PcmSample::F32,
(PcmWav::FORMAT_IEEE_FLOAT, 64) => PcmSample::F64,
(_, bits) => return Err(PcmWavError::UnsupportedBitsPerSample(bits)),
};
let channels = match self.channels {
1 => AudioChannels::Mono,
2 => AudioChannels::Stereo,
other => return Err(PcmWavError::UnsupportedChannelCount(other)),
};
Ok(PcmSpec::new(sample, channels, self.sample_rate))
}
}
impl PcmWavFmt {
pub const BASE_LEN: usize = 16;
pub const WAVEFORMATEX_EMPTY_LEN: usize = 18;
}