Skip to main content

Crate banished_wma

Crate banished_wma 

Source
Expand description

§banished-wma

A decoder for Windows Media Audio version 2 (WMA v2, format tag 0x161), written in Rust. WMA v2 is the codec inside xWMA files. Every sound in Banished is xWMA.

  • No dependencies and no unsafe code.
  • Builds for wasm32-unknown-unknown, so the browser version decodes the game’s sounds itself. It does not need converted files.
  • Checked against FFmpeg’s wmav2 decoder on every sound of the game: the signal-to-noise ratio must be at least 60 dB (tests/ffmpeg_parity.rs).

§Use it

[dependencies]
banished-wma = { path = "crates/wma" }

§Decode a whole stream

xWMA carries no codec extra data, so the parameters come from the WAVEFORMATEX header of the sound: the number of channels, the sample rate, the average bytes per second, and the packet size (block_align). Params::xwma sets the codec flags that xWMA implies.

use banished_wma::{decode, Params};

fn to_pcm(packets: &[u8]) -> Result<Vec<i16>, banished_wma::Error> {
    // 2 channels, 44,100 Hz, 6,000 bytes per second, 2,230-byte packets.
    let params = Params::xwma(2, 44_100, 6_000, 2_230);
    decode(&params, packets) // interleaved 16-bit samples
}

decode works like FFmpeg: a packet that fails to decode is skipped, and the output ends with one extra frame that holds the final overlap.

§Decode packet by packet

For streaming, use a Decoder. It writes float samples in the range -1 to 1.

use banished_wma::{to_i16, Decoder, Params};

fn stream(params: &Params, packets: &[u8]) -> Result<Vec<i16>, banished_wma::Error> {
    let mut decoder = Decoder::new(params)?;
    let mut pcm: Vec<f32> = Vec::new();
    for packet in packets.chunks(usize::from(params.block_align)) {
        decoder.decode_packet(packet, &mut pcm)?;
    }
    decoder.flush(&mut pcm); // the last overlap
    Ok(to_i16(&pcm))
}

§Decode a sound from the game

banished-formats reads the sound resource. banished-audio does both steps in one call (banished_audio::load_sound).

ⓘ
use banished_formats::pkg::Archive;
use banished_formats::types::sound_buffer;
use banished_wma::{decode, Params};

fn chicken(archive: &Archive) -> Result<Vec<i16>, Box<dyn std::error::Error>> {
    let sound = sound_buffer(&archive.read_ref("Audio/Animal/Chicken.rsc:version0")?)?;
    let f = &sound.format;
    let params = Params::xwma(f.channels, f.sample_rate, f.avg_bytes_per_sec, f.block_align);
    Ok(decode(&params, &sound.data)?)
}

§The API

ItemWhat it is
ParamsThe stream’s parameters. Params::xwma(channels, rate, bytes_per_sec, block_align)
decode(&Params, &[u8])Decode a whole stream to interleaved i16
Decodernew, decode_packet, flush, reset, and channels, sample_rate, delay, frame_len
to_i16(&[f32])Convert float samples to 16-bit, with rounding and clipping
ErrorUnsupported (a valid stream that uses a feature this decoder lacks) or Invalid
WMAV2, XWMA_FLAGSThe format tag and the flags that xWMA implies

The full API documentation is at https://tanepiper.github.io/unbanished/api/banished_wma/index.html.

§Source files

FileWhat it does
src/lib.rsParams, decode, to_i16, Error
src/decoder.rsSuper-frames, frames and blocks
src/mdct.rsThe inverse MDCT, through a DCT-IV with a half-size complex FFT
src/vlc.rsThe table-driven decoder for prefix codes
src/bits.rsThe bit reader. Reads past the end give zero bits, as WMA frames allow.
src/tables.rsThe constant tables that define the bitstream (codewords, band layouts)

§Tests

cargo test --release -p banished-wma

ffmpeg_parity decodes every sound of your game and compares the result with FFmpeg. It skips when game-data/original/WinData or ffmpeg is missing.

Structs§

Decoder
A WMA v2 decoder for one stream. Give it the packets in order with decode_packet, then call flush once at the end.
Params
Stream parameters, mostly from the WAVEFORMATEX header.

Enums§

Error
Why a stream could not be decoded.

Constants§

WMAV2
WAVE_FORMAT_WMAUDIO2.
XWMA_FLAGS
Encoder flags implied by xWMA, which carries no codec extradata: exponent VLCs, bit reservoir and variable block lengths, with the widest block-size range.

Functions§

decode
Decode a whole stream of block_align-sized packets to interleaved 16-bit PCM.
to_i16
Convert [-1, 1) float samples to 16-bit with rounding and clipping.

Type Aliases§

Result
A result with this crate’s Error.