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
unsafecode. - 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
wmav2decoder 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(¶ms, 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(¶ms, &sound.data)?)
}§The API
| Item | What it is |
|---|---|
Params | The stream’s parameters. Params::xwma(channels, rate, bytes_per_sec, block_align) |
decode(&Params, &[u8]) | Decode a whole stream to interleaved i16 |
Decoder | new, decode_packet, flush, reset, and channels, sample_rate, delay, frame_len |
to_i16(&[f32]) | Convert float samples to 16-bit, with rounding and clipping |
Error | Unsupported (a valid stream that uses a feature this decoder lacks) or Invalid |
WMAV2, XWMA_FLAGS | The 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
| File | What it does |
|---|---|
src/lib.rs | Params, decode, to_i16, Error |
src/decoder.rs | Super-frames, frames and blocks |
src/mdct.rs | The inverse MDCT, through a DCT-IV with a half-size complex FFT |
src/vlc.rs | The table-driven decoder for prefix codes |
src/bits.rs | The bit reader. Reads past the end give zero bits, as WMA frames allow. |
src/tables.rs | The constant tables that define the bitstream (codewords, band layouts) |
§Tests
cargo test --release -p banished-wmaffmpeg_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 callflushonce at the end. - Params
- Stream parameters, mostly from the
WAVEFORMATEXheader.
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.