use crate::audio::{AudioInfo, ChannelLayout, Channels};
use crate::io::{ReadBuffer, SampleFormat};
use crate::{codecs, errors, Result};
pub enum Chunk {
Fmt(AudioInfo),
Data(u32),
Unknown([u8; 4], u32),
}
const WAVE_FORMAT_PCM: u16 = 0x0001;
const WAVE_FORMAT_IEEE_FLOAT: u16 = 0x0003;
const WAVE_FORMAT_ALAW: u16 = 0x0006;
const WAVE_FORMAT_MULAW: u16 = 0x0007;
const WAVE_FORMAT_EXTENSIBLE: u16 = 0xfffe;
const KSDATAFORMAT_SUBTYPE_PCM: [u8; 16] = [
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,
];
const KSDATAFORMAT_SUBTYPE_IEEE_FLOAT: [u8; 16] = [
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,
];
const KSDATAFORMAT_SUBTYPE_ALAW: [u8; 16] = [
0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,
];
const KSDATAFORMAT_SUBTYPE_MULAW: [u8; 16] = [
0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,
];
pub fn read_next_chunk<R: ReadBuffer>(reader: &mut R) -> Result<Option<Chunk>> {
let mut chunk_type = [0; 4];
if reader.read_into(&mut chunk_type).is_err() {
return Ok(None);
}
let len = reader.read_le_u32()?;
match &chunk_type {
b"fmt " => {
let info = read_fmt_chunk(reader, len);
Ok(Some(Chunk::Fmt(info?)))
}
b"data" => Ok(Some(Chunk::Data(len))),
_ => {
reader.skip_bytes(len as usize)?;
Ok(Some(Chunk::Unknown(chunk_type, len)))
}
}
}
fn read_fmt_chunk<R: ReadBuffer>(reader: &mut R, chunk_len: u32) -> Result<AudioInfo> {
if chunk_len < 16 {
return errors::parse_error("invalid fmt chunk size");
}
let format_tag = reader.read_le_u16()?; let n_channels = reader.read_le_u16()?;
let sample_rate = reader.read_le_u32()?;
let n_bytes_per_sec = reader.read_le_u32()?;
let block_align = reader.read_le_u16()?;
let bits_per_sample = reader.read_le_u16()?;
if (Some(bits_per_sample) != (block_align / n_channels).checked_mul(8))
|| (Some(n_bytes_per_sec) != (block_align as u32).checked_mul(sample_rate))
{
return errors::parse_error("inconsistent fmt chunk");
}
let audio_info = AudioInfo {
codec: codecs::CODEC_TYPE_NULL,
audio_format: SampleFormat::U16,
sample_rate: sample_rate,
total_samples: 0,
bits_per_sample: bits_per_sample as u32,
channels: Channels::FRONT_LEFT,
channel_layout: ChannelLayout::Mono,
};
match format_tag {
WAVE_FORMAT_PCM => read_wave_format_pcm(reader, chunk_len, n_channels, audio_info),
WAVE_FORMAT_IEEE_FLOAT => read_wave_format_ieee(reader, chunk_len, n_channels, audio_info),
WAVE_FORMAT_ALAW => read_wave_format_alaw(reader, chunk_len, n_channels, audio_info),
WAVE_FORMAT_MULAW => read_wave_format_mulaw(reader, chunk_len, n_channels, audio_info),
WAVE_FORMAT_EXTENSIBLE => read_wave_format_ext(reader, chunk_len, audio_info),
_ => errors::unsupported_error("encoding format not supported"),
}
}
fn read_wave_format_pcm<R: ReadBuffer>(
reader: &mut R,
chunk_len: u32,
n_channels: u16,
mut audio_info: AudioInfo,
) -> Result<AudioInfo> {
if chunk_len > 16 {
reader.skip_bytes((chunk_len - 16) as usize)?;
}
audio_info.codec = match audio_info.bits_per_sample {
8 => codecs::CODEC_TYPE_PCM_U8,
16 => codecs::CODEC_TYPE_PCM_S16LE,
24 => codecs::CODEC_TYPE_PCM_S24LE,
32 => codecs::CODEC_TYPE_PCM_S32LE,
_ => {
return errors::parse_error("Bits per sample for fmt_pcm must be 8, 16, 24 or 32 bits.")
}
};
audio_info.channel_layout = match n_channels {
1 => ChannelLayout::Mono,
2 => ChannelLayout::Stereo,
_ => return errors::parse_error("Only max two channels supported for fmt_pcm."),
};
audio_info.channels = ChannelLayout::into_channels(audio_info.channel_layout);
return Ok(audio_info);
}
fn read_wave_format_ieee<R: ReadBuffer>(
reader: &mut R,
chunk_len: u32,
n_channels: u16,
mut audio_info: AudioInfo,
) -> Result<AudioInfo> {
let mut extra_size = 0;
if chunk_len == 18 {
extra_size = reader.read_le_u16()?;
}
if extra_size != 0 || chunk_len > 18 {
return errors::parse_error("Malformed fmt_ieee chunk.");
}
audio_info.codec = match audio_info.bits_per_sample {
32 => codecs::CODEC_TYPE_PCM_F32LE,
_ => return errors::parse_error("Bits per sample for fmt_ieee must be 32 bits."),
};
audio_info.channel_layout = match n_channels {
1 => ChannelLayout::Mono,
2 => ChannelLayout::Stereo,
_ => return errors::parse_error("Max two channels supported for fmt_ieee."),
};
audio_info.channels = ChannelLayout::into_channels(audio_info.channel_layout);
audio_info.audio_format = SampleFormat::F32;
return Ok(audio_info);
}
fn read_wave_format_ext<R: ReadBuffer>(
reader: &mut R,
chunk_len: u32,
mut audio_info: AudioInfo,
) -> Result<AudioInfo> {
if chunk_len < 40 {
return errors::parse_error("Malformed fmt_ext chunk.");
}
let extra_size = reader.read_le_u16()?;
if extra_size != 22 {
return errors::parse_error("Extra data size not 22 bytes for fmt_ext chunk.");
}
if (audio_info.bits_per_sample & 0x7) != 0 {
return errors::parse_error(
"Bits per encoded sample for fmt_ext must be a multiple of 8 bits.",
);
}
audio_info.bits_per_sample = reader.read_le_u16()? as u32;
let channel_mask = reader.read_le_u32()?;
let mut sub_format_guid = [0u8; 16];
reader.read_into(&mut sub_format_guid)?;
audio_info.codec = match sub_format_guid {
KSDATAFORMAT_SUBTYPE_PCM => {
if audio_info.bits_per_sample > 32 {
return errors::parse_error(
"Bits per sample for fmt_ext PCM sub-type must be <= 32 bits.",
);
}
match audio_info.bits_per_sample {
8 => codecs::CODEC_TYPE_PCM_U8,
16 => codecs::CODEC_TYPE_PCM_S16LE,
24 => codecs::CODEC_TYPE_PCM_S24LE,
32 => codecs::CODEC_TYPE_PCM_S32LE,
_ => unreachable!(),
}
}
KSDATAFORMAT_SUBTYPE_IEEE_FLOAT => {
if audio_info.bits_per_sample == 32 {
audio_info.audio_format = SampleFormat::F32;
codecs::CODEC_TYPE_PCM_F32LE
} else {
return errors::parse_error(
"Bits per sample for fmt_ext IEEE sub-type must be 32 bits.",
);
}
}
KSDATAFORMAT_SUBTYPE_ALAW => codecs::CODEC_TYPE_PCM_ALAW,
KSDATAFORMAT_SUBTYPE_MULAW => codecs::CODEC_TYPE_PCM_MULAW,
_ => return errors::unsupported_error("Unsupported fmt_ext sub-type."),
};
audio_info.channels = decode_channel_mask(channel_mask);
if audio_info.channels.count() == 2 {
audio_info.channel_layout = ChannelLayout::Stereo;
}
return Ok(audio_info);
}
fn read_wave_format_alaw<R: ReadBuffer>(
reader: &mut R,
chunk_len: u32,
n_channels: u16,
mut audio_info: AudioInfo,
) -> Result<AudioInfo> {
if chunk_len > 16 {
reader.skip_bytes((chunk_len - 16) as usize)?;
}
audio_info.codec = codecs::CODEC_TYPE_PCM_ALAW;
audio_info.channel_layout = match n_channels {
1 => ChannelLayout::Mono,
2 => ChannelLayout::Stereo,
_ => return errors::parse_error("Only max two channels supported for fmt_alaw."),
};
audio_info.channels = ChannelLayout::into_channels(audio_info.channel_layout);
return Ok(audio_info);
}
fn read_wave_format_mulaw<R: ReadBuffer>(
reader: &mut R,
chunk_len: u32,
n_channels: u16,
mut audio_info: AudioInfo,
) -> Result<AudioInfo> {
if chunk_len > 16 {
reader.skip_bytes((chunk_len - 16) as usize)?;
}
audio_info.codec = codecs::CODEC_TYPE_PCM_MULAW;
audio_info.channel_layout = match n_channels {
1 => ChannelLayout::Mono,
2 => ChannelLayout::Stereo,
_ => return errors::parse_error("Only max two channels supported for fmt_mulaw."),
};
audio_info.channels = ChannelLayout::into_channels(audio_info.channel_layout);
return Ok(audio_info);
}
fn decode_channel_mask(channel_mask: u32) -> Channels {
const SPEAKER_FRONT_LEFT: u32 = 0x1;
const SPEAKER_FRONT_RIGHT: u32 = 0x2;
const SPEAKER_FRONT_CENTER: u32 = 0x4;
const SPEAKER_LOW_FREQUENCY: u32 = 0x8;
const SPEAKER_BACK_LEFT: u32 = 0x10;
const SPEAKER_BACK_RIGHT: u32 = 0x20;
const SPEAKER_FRONT_LEFT_OF_CENTER: u32 = 0x40;
const SPEAKER_FRONT_RIGHT_OF_CENTER: u32 = 0x80;
const SPEAKER_BACK_CENTER: u32 = 0x100;
const SPEAKER_SIDE_LEFT: u32 = 0x200;
const SPEAKER_SIDE_RIGHT: u32 = 0x400;
const SPEAKER_TOP_CENTER: u32 = 0x800;
const SPEAKER_TOP_FRONT_LEFT: u32 = 0x1000;
const SPEAKER_TOP_FRONT_CENTER: u32 = 0x2000;
const SPEAKER_TOP_FRONT_RIGHT: u32 = 0x4000;
const SPEAKER_TOP_BACK_LEFT: u32 = 0x8000;
const SPEAKER_TOP_BACK_CENTER: u32 = 0x10000;
const SPEAKER_TOP_BACK_RIGHT: u32 = 0x20000;
let mut channels = Channels::empty();
if channel_mask & SPEAKER_FRONT_LEFT != 0 {
channels |= Channels::FRONT_LEFT;
}
if channel_mask & SPEAKER_FRONT_RIGHT != 0 {
channels |= Channels::FRONT_RIGHT;
}
if channel_mask & SPEAKER_FRONT_CENTER != 0 {
channels |= Channels::FRONT_CENTRE;
}
if channel_mask & SPEAKER_LOW_FREQUENCY != 0 {
channels |= Channels::LFE1;
}
if channel_mask & SPEAKER_BACK_LEFT != 0 {
channels |= Channels::BACK_LEFT;
}
if channel_mask & SPEAKER_BACK_RIGHT != 0 {
channels |= Channels::BACK_RIGHT;
}
if channel_mask & SPEAKER_FRONT_LEFT_OF_CENTER != 0 {
channels |= Channels::FRONT_LEFT_CENTRE;
}
if channel_mask & SPEAKER_FRONT_RIGHT_OF_CENTER != 0 {
channels |= Channels::FRONT_RIGHT_CENTRE;
}
if channel_mask & SPEAKER_BACK_CENTER != 0 {
channels |= Channels::BACK_CENTRE;
}
if channel_mask & SPEAKER_SIDE_LEFT != 0 {
channels |= Channels::SIDE_LEFT;
}
if channel_mask & SPEAKER_SIDE_RIGHT != 0 {
channels |= Channels::SIDE_RIGHT;
}
if channel_mask & SPEAKER_TOP_CENTER != 0 {
channels |= Channels::TOP_CENTRE;
}
if channel_mask & SPEAKER_TOP_FRONT_LEFT != 0 {
channels |= Channels::TOP_FRONT_LEFT;
}
if channel_mask & SPEAKER_TOP_FRONT_CENTER != 0 {
channels |= Channels::TOP_FRONT_CENTRE;
}
if channel_mask & SPEAKER_TOP_FRONT_RIGHT != 0 {
channels |= Channels::TOP_FRONT_RIGHT;
}
if channel_mask & SPEAKER_TOP_BACK_LEFT != 0 {
channels |= Channels::TOP_BACK_LEFT;
}
if channel_mask & SPEAKER_TOP_BACK_CENTER != 0 {
channels |= Channels::TOP_BACK_CENTRE;
}
if channel_mask & SPEAKER_TOP_BACK_RIGHT != 0 {
channels |= Channels::TOP_BACK_RIGHT;
}
channels
}