use crate::error::{Error, Result};
pub const FORMAT_PCM: u16 = 1;
pub const FORMAT_IEEE_FLOAT: u16 = 3;
pub const FORMAT_EXTENSIBLE: u16 = 0xFFFE;
pub const HEADER_LEN: usize = 44;
pub(super) const EXTENSIBLE_HEADER_LEN: usize = 68;
pub(super) const RIFF_SIZE_OFFSET: usize = 4;
pub(super) const DATA_SIZE_OFFSET: usize = HEADER_LEN - 4;
pub(super) struct FmtFields {
pub(super) format_tag: u16,
pub(super) channels: u16,
pub(super) sample_rate: u32,
pub(super) byte_rate: u32,
pub(super) block_align: u16,
pub(super) bits_per_sample: u16,
pub(super) channel_mask: u32,
}
pub(super) fn canonical_header(fmt: &FmtFields, riff_size: u32, data_size: u32) -> Vec<u8> {
let mut out = Vec::with_capacity(HEADER_LEN);
out.extend_from_slice(b"RIFF");
out.extend_from_slice(&riff_size.to_le_bytes());
out.extend_from_slice(b"WAVE");
out.extend_from_slice(b"fmt ");
out.extend_from_slice(&16u32.to_le_bytes());
out.extend_from_slice(&fmt.format_tag.to_le_bytes());
out.extend_from_slice(&fmt.channels.to_le_bytes());
out.extend_from_slice(&fmt.sample_rate.to_le_bytes());
out.extend_from_slice(&fmt.byte_rate.to_le_bytes());
out.extend_from_slice(&fmt.block_align.to_le_bytes());
out.extend_from_slice(&fmt.bits_per_sample.to_le_bytes());
out.extend_from_slice(b"data");
out.extend_from_slice(&data_size.to_le_bytes());
out
}
pub(super) fn bad(arg: &'static str, reason: String) -> Error {
Error::InvalidArgument { arg, reason }
}
pub(super) fn checked_wave_rates(
sample_rate: u32,
channels: u16,
bits_per_sample: u16,
) -> Result<(u32, u16)> {
if sample_rate == 0 {
return Err(bad(
"sample_rate",
"sample rate is 0 Hz; pass the rate the samples were produced at".to_string(),
));
}
let bytes_per_sample = bits_per_sample / 8;
let block_align = channels.checked_mul(bytes_per_sample).ok_or_else(|| {
bad(
"channels",
format!(
"channel count {channels} at {bits_per_sample} bits per sample overflows \
the WAV header's u16 block alignment"
),
)
})?;
let byte_rate = sample_rate.checked_mul(block_align as u32).ok_or_else(|| {
bad(
"sample_rate",
format!(
"sample rate {sample_rate} at {block_align} bytes per frame overflows \
the WAV header's u32 byte rate"
),
)
})?;
Ok((byte_rate, block_align))
}
pub(super) fn checked_data_size(sample_count: usize, bytes_per_sample: usize) -> Result<u32> {
sample_count
.checked_mul(bytes_per_sample)
.and_then(|bytes| u32::try_from(bytes).ok())
.ok_or_else(|| {
bad(
"samples",
format!(
"sample count {sample_count} is too large to encode as a WAV file \
({sample_count} x {bytes_per_sample} bytes overflows a u32 data size)"
),
)
})
}
pub(super) fn checked_riff_size(
sample_count: usize,
data_size: u32,
header_len: usize,
) -> Result<u32> {
let fixed = (header_len - 8) as u32;
fixed.checked_add(data_size).ok_or_else(|| {
bad(
"samples",
format!(
"sample count {sample_count} produces a {data_size}-byte data chunk, \
too large to encode as a WAV file ({fixed} + data_size overflows a u32 \
RIFF size)"
),
)
})
}