const BIAS: i32 = 0x84;
const CLIP: i32 = 32635;
pub fn encode(sample: i16) -> u8 {
let mut s = sample as i32;
let sign = if s < 0 {
s = -s;
0x80
} else {
0
};
if s > CLIP {
s = CLIP;
}
s += BIAS;
let mut exponent: i32 = 7;
let mut mask = 0x4000;
while exponent > 0 && (s & mask) == 0 {
exponent -= 1;
mask >>= 1;
}
let mantissa = (s >> (exponent + 3)) & 0x0F;
!(sign | (exponent << 4) | mantissa) as u8
}
pub fn decode(ulaw: u8) -> i16 {
const EXP_LUT: [i32; 8] = [0, 132, 396, 924, 1980, 4092, 8316, 16764];
let u = !ulaw;
let sign = u & 0x80;
let exponent = ((u >> 4) & 0x07) as usize;
let mantissa = (u & 0x0F) as i32;
let mut sample = EXP_LUT[exponent] + (mantissa << (exponent as i32 + 3));
if sign != 0 {
sample = -sample;
}
sample as i16
}
pub fn encode_block(pcm: &[i16], out: &mut Vec<u8>) {
out.extend(pcm.iter().map(|&s| encode(s)));
}
pub fn decode_block(ulaw: &[u8], out: &mut Vec<i16>) {
out.extend(ulaw.iter().map(|&b| decode(b)));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_is_close() {
for s in (-32000..=32000).step_by(257) {
let back = decode(encode(s as i16));
let err = (s - back as i32).abs();
assert!(err <= (s.abs() / 8 + 256), "s={s} back={back} err={err}");
}
}
#[test]
fn zero_and_silence() {
assert!(decode(encode(0)).abs() < 256);
}
}