symphonia-wem 0.1.0

Symphonia demuxer for Wwise Encoded Media files
Documentation
//! Reader of file chunks.

use std::num::NonZero;

use symphonia_core::{
    errors::Result,
    io::{MediaSourceStream, ReadBytes as _},
};

use crate::{PacketInfo, endianness::Endianness, header::SetupHeader};

/// Read the chunks of the file.
pub(crate) struct ChunkReader;

impl ChunkReader {
    /// Read a chunk.
    pub(crate) fn read(endianness: Endianness, mss: &mut MediaSourceStream<'_>) -> Result<Chunk> {
        // Determines what type of chunk it is
        let chunk_type = mss.read_quad_bytes()?;

        // Remaining size of the chunk
        let size = endianness.u32(mss)?;

        match &chunk_type {
            b"fmt " => Ok(Chunk::Fmt(Fmt::read(endianness, mss)?)),
            // Return the last byte only
            b"data" => Ok(Chunk::Data(mss.pos() + u64::from(size))),
            b"hash" => {
                // TODO: should this be parsed somehow?

                Ok(Chunk::Hash)
            }
            _ => symphonia_core::errors::decode_error("wem: unrecognized chunk type"),
        }
    }
}

/// A data chunk.
pub(crate) enum Chunk {
    /// Metadata.
    Fmt(Fmt),
    /// Binary data block.
    ///
    /// Value is the position of the last byte in the stream.
    Data(u64),
    /// Hashed data.
    Hash,
}

/// A format data chunk.
pub(crate) struct Fmt {
    /// Amount of channels.
    pub(crate) channels: NonZero<u16>,
    /// Sample rate.
    pub(crate) sample_rate: NonZero<u32>,
    /// Average bytes per second.
    pub(crate) avg_bytes_per_second: u32,
    /// Offset in bytes of the setup packet.
    pub(crate) setup_packet_offset: u32,
    /// Offset in bytes of the first audio packet.
    pub(crate) first_audio_packet_offset: u32,
    /// Block size if mode block flag is not set.
    pub(crate) block_size_0: u8,
    /// Block size if mode block flag is set.
    pub(crate) block_size_1: u8,
}

impl Fmt {
    /// Get packet info from data.
    pub(crate) fn packet_info(&self, setup: &SetupHeader, last_offset: u64) -> PacketInfo {
        PacketInfo {
            last_offset,
            block_size_0: self.block_size_0,
            block_size_1: self.block_size_1,
            mode_bits: setup.mode_bits as usize,
            // Mode count is 6 bits so safe to cast
            num_modes: u8::try_from(setup.mode_block_flags.len())
                .expect("Block flags out of range"),
            modes_block_flags: setup.mode_block_flags,
        }
    }

    /// Read from the media stream.
    fn read(endianness: Endianness, mss: &mut MediaSourceStream<'_>) -> Result<Self> {
        // Ensure we can get the correct offsets
        let pos = mss.pos();

        // Constant that must be there
        let codec_id = mss.read_double_bytes()?;
        if codec_id != [0xFF, 0xFF] {
            return symphonia_core::errors::decode_error(
                "wem: missing or wrong codec ID in format chunk",
            );
        }

        // Read the different values
        let channels = endianness.non_zero_u16(mss)?;
        let sample_rate = endianness.non_zero_u32(mss)?;
        let avg_bytes_per_second = endianness.u32(mss)?;
        let _block_align = endianness.u16(mss)?;
        let _bits_per_sample = endianness.u16(mss)?;

        // Ignore padding calculated from an offset
        mss.ignore_bytes(0x18 - (mss.pos() - pos))?;

        // Read the vorbis data
        let _sample_count = endianness.u32(mss)?;
        let _mod_signal = endianness.u32(mss)?;
        // TODO: do we need this?
        // let _mod_packets =
        //     mod_signal != 0x4A && mod_signal != 0x4B && mod_signal != 0x69 && mod_signal != 0x70;

        // Ignore padding calculated from an offset
        mss.ignore_bytes((0x18 + 0x10) - (mss.pos() - pos))?;
        let setup_packet_offset = endianness.u32(mss)?;
        let first_audio_packet_offset = endianness.u32(mss)?;

        // Ignore padding calculated from an offset
        mss.ignore_bytes((0x18 + 0x24) - (mss.pos() - pos))?;
        let _uid = endianness.u32(mss)?;
        let block_size_0 = mss.read_u8()?;
        let block_size_1 = mss.read_u8()?;

        Ok(Self {
            channels,
            sample_rate,
            avg_bytes_per_second,
            setup_packet_offset,
            first_audio_packet_offset,
            block_size_0,
            block_size_1,
        })
    }
}