pub(crate) mod bits;
mod chunk;
pub(crate) mod codebook;
mod endianness;
pub(crate) mod header;
pub(crate) mod math;
mod packet;
use symphonia_core::{
codecs::{
CodecParameters,
audio::{AudioCodecParameters, well_known::CODEC_ID_VORBIS},
},
common::FourCc,
errors::{Error, Result, SeekErrorKind},
formats::{
FormatId, FormatInfo, FormatOptions, FormatReader, MediaInfo, SeekMode, SeekTo, SeekedTo,
Track,
probe::{ProbeableFormat, Score, Scoreable},
},
io::{MediaSourceStream, ReadBytes as _, ScopedStream},
meta::{Metadata, MetadataLog},
packet::Packet,
};
use crate::{
chunk::{Chunk, ChunkReader},
header::{IdentHeader, SetupHeader},
packet::{PacketInfo, PacketReader},
};
const FORMAT_INFO: FormatInfo = FormatInfo {
format: FormatId::new(FourCc::new(*b"wem ")),
short_name: "wem",
long_name: "Wwise Encoded Media",
};
pub struct WemReader<'s> {
mss: MediaSourceStream<'s>,
media_info: MediaInfo,
metadata: MetadataLog,
tracks: Vec<Track>,
packet_reader: PacketReader,
}
impl<'s> WemReader<'s> {
pub fn try_new(mut mss: MediaSourceStream<'s>) -> Result<Self> {
let riff_marker = mss.read_quad_bytes()?;
let endianness = match &riff_marker {
b"RIFF" => endianness::Endianness::Little,
b"RIFX" => endianness::Endianness::Big,
_ => {
return symphonia_core::errors::unsupported_error(
"wem: missing RIFF stream marker",
);
}
};
let _riff_size = endianness.u32(&mut mss)?;
let riff_form = mss.read_quad_bytes()?;
if riff_form != *b"WAVE" {
return symphonia_core::errors::unsupported_error("wem: RIFF form is not WAVE");
}
let mut format = None;
loop {
let chunk = ChunkReader::read(endianness, &mut mss)?;
match chunk {
Chunk::Fmt(fmt) => {
format = Some(fmt);
}
Chunk::Data(last_offset) => {
let Some(format) = format.as_ref() else {
return symphonia_core::errors::decode_error("wem: missing format chunk");
};
let channels =
symphonia_common::xiph::audio::vorbis::vorbis_channels_to_channels(
u8::try_from(format.channels.get())
.map_err(|_| Error::LimitError("wem: too many channels"))?,
)
.ok_or(Error::LimitError("wem: too many channels"))?;
let ident_packet = IdentHeader::new(format)?;
let setup_packet = SetupHeader::new(format, &mut mss)?;
let packet_info = format.packet_info(&setup_packet, last_offset);
let mut extra_data = ident_packet.into_inner().to_vec();
extra_data.append(&mut setup_packet.into_inner());
let mut codec_params = AudioCodecParameters::new();
codec_params
.for_codec(CODEC_ID_VORBIS)
.with_sample_rate(format.sample_rate.get())
.with_channels(channels)
.with_extra_data(extra_data.into_boxed_slice());
let mut track = Track::new(0);
track.with_codec_params(CodecParameters::Audio(codec_params));
let tracks = vec![track];
let media_info = MediaInfo::new();
let metadata = MetadataLog::default();
let mut packet_reader = PacketReader::new(packet_info);
packet_reader.read_first(&mut mss)?;
return Ok(Self {
mss,
media_info,
metadata,
tracks,
packet_reader,
});
}
Chunk::Hash => {
return symphonia_core::errors::unsupported_error(
"wem: can't handle hash chunks",
);
}
}
}
}
}
impl ProbeableFormat<'_> for WemReader<'_> {
fn try_probe_new(
mss: MediaSourceStream<'_>,
_opts: FormatOptions,
) -> Result<Box<dyn FormatReader + '_>> {
Ok(Box::new(WemReader::try_new(mss)?))
}
fn probe_data() -> &'static [symphonia_core::formats::probe::ProbeFormatData] {
&[symphonia_core::support_format!(
FORMAT_INFO,
&["wem"],
&["application/octet-stream"],
&[b"RIFF", b"RIFX"]
)]
}
}
impl Scoreable for WemReader<'_> {
fn score(mut src: ScopedStream<&mut MediaSourceStream<'_>>) -> Result<Score> {
let riff_marker = src.read_quad_bytes()?;
if riff_marker != *b"RIFF" && riff_marker != *b"RIFX" {
return Ok(Score::Unsupported);
}
src.ignore_bytes(4)?;
let riff_form = src.read_quad_bytes()?;
if riff_form != *b"WAVE" {
return Ok(Score::Unsupported);
}
Ok(Score::Supported(255))
}
}
impl FormatReader for WemReader<'_> {
fn format_info(&self) -> &FormatInfo {
&FORMAT_INFO
}
fn media_info(&self) -> &MediaInfo {
&self.media_info
}
fn metadata(&mut self) -> Metadata<'_> {
self.metadata.metadata()
}
fn seek(&mut self, _mode: SeekMode, _to: SeekTo) -> Result<SeekedTo> {
if self.tracks.is_empty() {
return symphonia_core::errors::seek_error(SeekErrorKind::Unseekable);
}
todo!()
}
fn tracks(&self) -> &[Track] {
&self.tracks
}
fn next_packet(&mut self) -> Result<Option<Packet>> {
self.packet_reader.read(&mut self.mss, self.tracks[0].id)
}
fn into_inner<'s>(self: Box<Self>) -> MediaSourceStream<'s>
where
Self: 's,
{
self.mss
}
}