Skip to main content

symphonia_wem/
lib.rs

1//! Symphonia adapter for Wwise Encoded Media files.
2
3pub(crate) mod bits;
4mod chunk;
5pub(crate) mod codebook;
6mod endianness;
7pub(crate) mod header;
8pub(crate) mod math;
9mod packet;
10
11use symphonia_core::{
12    codecs::{
13        CodecParameters,
14        audio::{AudioCodecParameters, well_known::CODEC_ID_VORBIS},
15    },
16    common::FourCc,
17    errors::{Error, Result, SeekErrorKind},
18    formats::{
19        FormatId, FormatInfo, FormatOptions, FormatReader, MediaInfo, SeekMode, SeekTo, SeekedTo,
20        Track,
21        probe::{ProbeableFormat, Score, Scoreable},
22    },
23    io::{MediaSourceStream, ReadBytes as _, ScopedStream},
24    meta::{Metadata, MetadataLog},
25    packet::Packet,
26};
27
28use crate::{
29    chunk::{Chunk, ChunkReader},
30    header::{IdentHeader, SetupHeader},
31    packet::{PacketInfo, PacketReader},
32};
33
34/// Format information.
35const FORMAT_INFO: FormatInfo = FormatInfo {
36    format: FormatId::new(FourCc::new(*b"wem ")),
37    short_name: "wem",
38    long_name: "Wwise Encoded Media",
39};
40
41/// Demuxer for a Wwise Encoded Media file.
42pub struct WemReader<'s> {
43    /// Read the data from any source.
44    mss: MediaSourceStream<'s>,
45    /// Information about the media.
46    media_info: MediaInfo,
47    /// Time ordered metadata.
48    metadata: MetadataLog,
49    /// Different codec bitstreams.
50    tracks: Vec<Track>,
51    /// Reader of packets.
52    packet_reader: PacketReader,
53}
54
55impl<'s> WemReader<'s> {
56    /// Try reading from a stream.
57    ///
58    /// # Errors
59    ///
60    /// - When reading the bytestream fails.
61    pub fn try_new(mut mss: MediaSourceStream<'s>) -> Result<Self> {
62        // Read the first 4 bytes of the stream, should be RIFF or RIFX, denotes the endianness
63        let riff_marker = mss.read_quad_bytes()?;
64        let endianness = match &riff_marker {
65            b"RIFF" => endianness::Endianness::Little,
66            b"RIFX" => endianness::Endianness::Big,
67            _ => {
68                return symphonia_core::errors::unsupported_error(
69                    "wem: missing RIFF stream marker",
70                );
71            }
72        };
73
74        // Length of the next RIFF block
75        let _riff_size = endianness.u32(&mut mss)?;
76
77        // RIFF form
78        let riff_form = mss.read_quad_bytes()?;
79        if riff_form != *b"WAVE" {
80            // We only support WAVE
81            return symphonia_core::errors::unsupported_error("wem: RIFF form is not WAVE");
82        }
83        let mut format = None;
84
85        // Read each chunk
86        loop {
87            let chunk = ChunkReader::read(endianness, &mut mss)?;
88
89            match chunk {
90                Chunk::Fmt(fmt) => {
91                    // Store
92                    format = Some(fmt);
93                }
94                Chunk::Data(last_offset) => {
95                    // Ensure the format chunk is read
96                    let Some(format) = format.as_ref() else {
97                        return symphonia_core::errors::decode_error("wem: missing format chunk");
98                    };
99
100                    // Convert channels to vorbis channels
101                    let channels =
102                        symphonia_common::xiph::audio::vorbis::vorbis_channels_to_channels(
103                            u8::try_from(format.channels.get())
104                                .map_err(|_| Error::LimitError("wem: too many channels"))?,
105                        )
106                        .ok_or(Error::LimitError("wem: too many channels"))?;
107
108                    // Create the fake header packets
109                    let ident_packet = IdentHeader::new(format)?;
110                    let setup_packet = SetupHeader::new(format, &mut mss)?;
111
112                    // Set the packet info
113                    let packet_info = format.packet_info(&setup_packet, last_offset);
114
115                    // Create the extra data
116                    let mut extra_data = ident_packet.into_inner().to_vec();
117                    extra_data.append(&mut setup_packet.into_inner());
118
119                    // Set the codec parameters for the track
120                    let mut codec_params = AudioCodecParameters::new();
121                    codec_params
122                        .for_codec(CODEC_ID_VORBIS)
123                        .with_sample_rate(format.sample_rate.get())
124                        .with_channels(channels)
125                        // Send the headers' packet data to vorbis
126                        .with_extra_data(extra_data.into_boxed_slice());
127
128                    // Create a track
129                    let mut track = Track::new(0);
130                    track.with_codec_params(CodecParameters::Audio(codec_params));
131                    let tracks = vec![track];
132
133                    // Create the media info
134                    let media_info = MediaInfo::new();
135                    let metadata = MetadataLog::default();
136
137                    // Setup packet reader
138                    let mut packet_reader = PacketReader::new(packet_info);
139                    // Read the first packet, required for parsing the consecutive ones
140                    packet_reader.read_first(&mut mss)?;
141
142                    // This chunk is always last, if it's not found something went wrong with reading and an error will probably be thrown reading the next chunk
143                    return Ok(Self {
144                        mss,
145                        media_info,
146                        metadata,
147                        tracks,
148                        packet_reader,
149                    });
150                }
151                Chunk::Hash => {
152                    return symphonia_core::errors::unsupported_error(
153                        "wem: can't handle hash chunks",
154                    );
155                }
156            }
157        }
158    }
159}
160
161impl ProbeableFormat<'_> for WemReader<'_> {
162    fn try_probe_new(
163        mss: MediaSourceStream<'_>,
164        _opts: FormatOptions,
165    ) -> Result<Box<dyn FormatReader + '_>> {
166        Ok(Box::new(WemReader::try_new(mss)?))
167    }
168
169    fn probe_data() -> &'static [symphonia_core::formats::probe::ProbeFormatData] {
170        &[symphonia_core::support_format!(
171            FORMAT_INFO,
172            &["wem"],
173            &["application/octet-stream"],
174            &[b"RIFF", b"RIFX"]
175        )]
176    }
177}
178
179impl Scoreable for WemReader<'_> {
180    fn score(mut src: ScopedStream<&mut MediaSourceStream<'_>>) -> Result<Score> {
181        // Take the riff marker
182        let riff_marker = src.read_quad_bytes()?;
183        if riff_marker != *b"RIFF" && riff_marker != *b"RIFX" {
184            return Ok(Score::Unsupported);
185        }
186
187        // Ignore the size of the riff block
188        src.ignore_bytes(4)?;
189
190        // Take the riff form
191        let riff_form = src.read_quad_bytes()?;
192        if riff_form != *b"WAVE" {
193            return Ok(Score::Unsupported);
194        }
195
196        Ok(Score::Supported(255))
197    }
198}
199
200impl FormatReader for WemReader<'_> {
201    fn format_info(&self) -> &FormatInfo {
202        &FORMAT_INFO
203    }
204
205    fn media_info(&self) -> &MediaInfo {
206        &self.media_info
207    }
208
209    fn metadata(&mut self) -> Metadata<'_> {
210        self.metadata.metadata()
211    }
212
213    fn seek(&mut self, _mode: SeekMode, _to: SeekTo) -> Result<SeekedTo> {
214        // No tracks so nothing to seek
215        if self.tracks.is_empty() {
216            return symphonia_core::errors::seek_error(SeekErrorKind::Unseekable);
217        }
218
219        todo!()
220    }
221
222    fn tracks(&self) -> &[Track] {
223        &self.tracks
224    }
225
226    fn next_packet(&mut self) -> Result<Option<Packet>> {
227        // Read the next packet from the stream
228        self.packet_reader.read(&mut self.mss, self.tracks[0].id)
229    }
230
231    fn into_inner<'s>(self: Box<Self>) -> MediaSourceStream<'s>
232    where
233        Self: 's,
234    {
235        self.mss
236    }
237}