1#![doc = include_str!("../README.md")]
2#![doc(
3 html_logo_url = "https://tanepiper.github.io/unbanished/assets/un-banished-icon.svg",
4 html_favicon_url = "https://tanepiper.github.io/unbanished/favicon.svg"
5)]
6
7mod bits;
8mod decoder;
9mod mdct;
10mod tables;
11mod vlc;
12
13pub use decoder::Decoder;
14
15pub const WMAV2: u16 = 0x161;
17
18pub const XWMA_FLAGS: u16 = 0x1f;
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub struct Params {
25 pub tag: u16,
27 pub channels: u16,
29 pub sample_rate: u32,
31 pub avg_bytes_per_sec: u32,
33 pub block_align: u16,
35 pub flags: u16,
37}
38
39impl Params {
40 pub fn xwma(channels: u16, sample_rate: u32, avg_bytes_per_sec: u32, block_align: u16) -> Self {
42 Params {
43 tag: WMAV2,
44 channels,
45 sample_rate,
46 avg_bytes_per_sec,
47 block_align,
48 flags: XWMA_FLAGS,
49 }
50 }
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub enum Error {
56 Unsupported(&'static str),
58 Invalid(&'static str),
60}
61
62impl std::fmt::Display for Error {
63 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
64 match self {
65 Error::Unsupported(m) => write!(f, "unsupported WMA stream: {m}"),
66 Error::Invalid(m) => write!(f, "invalid WMA data: {m}"),
67 }
68 }
69}
70
71impl std::error::Error for Error {}
72
73pub type Result<T> = std::result::Result<T, Error>;
75
76pub fn decode(params: &Params, packets: &[u8]) -> Result<Vec<i16>> {
81 let mut dec = Decoder::new(params)?;
82 let mut pcm = Vec::new();
83 for packet in packets.chunks(usize::from(params.block_align)) {
84 let _ = dec.decode_packet(packet, &mut pcm);
85 }
86 dec.flush(&mut pcm);
87 Ok(to_i16(&pcm))
88}
89
90pub fn to_i16(samples: &[f32]) -> Vec<i16> {
92 samples
93 .iter()
94 .map(|&s| (s * 32768.0).round_ties_even().clamp(-32768.0, 32767.0) as i16)
95 .collect()
96}