use crate::wav::FormatCode;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Companding {
ALaw,
MuLaw,
}
const fn mulaw_to_linear(u_val: u8) -> i16 {
const BIAS: i32 = 0x84;
let u = !u_val as i32;
let mantissa = (u & 0x0F) << 3;
let exponent = (u & 0x70) >> 4;
let magnitude = ((mantissa + BIAS) << exponent) - BIAS;
if (u & 0x80) != 0 {
-magnitude as i16
} else {
magnitude as i16
}
}
const fn alaw_to_linear(a_val: u8) -> i16 {
let a = (a_val ^ 0x55) as i32;
let mut magnitude = (a & 0x0F) << 4;
let segment = (a & 0x70) >> 4;
match segment {
0 => magnitude += 8,
1 => magnitude += 0x108,
_ => {
magnitude += 0x108;
magnitude <<= segment - 1;
},
}
if (a & 0x80) != 0 {
magnitude as i16
} else {
-magnitude as i16
}
}
const fn build_lut(mu: bool) -> [i16; 256] {
let mut table = [0i16; 256];
let mut i = 0usize;
while i < 256 {
table[i] = if mu {
mulaw_to_linear(i as u8)
} else {
alaw_to_linear(i as u8)
};
i += 1;
}
table
}
static MULAW_LUT: [i16; 256] = build_lut(true);
static ALAW_LUT: [i16; 256] = build_lut(false);
impl Companding {
pub const fn from_format(format: FormatCode) -> Option<Self> {
match format {
FormatCode::ALaw => Some(Companding::ALaw),
FormatCode::MuLaw => Some(Companding::MuLaw),
_ => None,
}
}
#[inline]
pub fn decode(self, byte: u8) -> i16 {
match self {
Companding::MuLaw => MULAW_LUT[byte as usize],
Companding::ALaw => ALAW_LUT[byte as usize],
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn mulaw_known_values() {
assert_eq!(Companding::MuLaw.decode(0xFF), 0);
assert_eq!(Companding::MuLaw.decode(0x7F), 0);
assert_eq!(Companding::MuLaw.decode(0x00), -32124);
assert_eq!(Companding::MuLaw.decode(0x80), 32124);
}
#[test]
fn alaw_known_values() {
assert_eq!(Companding::ALaw.decode(0xD5), 8);
assert_eq!(Companding::ALaw.decode(0x55), -8);
assert_eq!(Companding::ALaw.decode(0x2A), -32256);
assert_eq!(Companding::ALaw.decode(0xAA), 32256);
}
#[test]
fn from_format_maps_correctly() {
assert_eq!(Companding::from_format(FormatCode::ALaw), Some(Companding::ALaw));
assert_eq!(Companding::from_format(FormatCode::MuLaw), Some(Companding::MuLaw));
assert_eq!(Companding::from_format(FormatCode::Pcm), None);
}
}