use crate::audio_process::resamplers::StreamResampler;
pub fn linear_to_ulaw(sample: i16) -> u8 {
const BIAS: i32 = 0x84;
const CLIP: i32 = 8159;
const SEG_UEND: [i32; 8] = [0x3F, 0x7F, 0xFF, 0x1FF, 0x3FF, 0x7FF, 0xFFF, 0x1FFF];
let mut pcm = (sample as i32) >> 2; let mask;
if pcm < 0 {
pcm = -pcm;
mask = 0x7F;
} else {
mask = 0xFF;
}
if pcm > CLIP {
pcm = CLIP;
}
pcm += BIAS >> 2;
let mut seg = 8;
for (i, &end) in SEG_UEND.iter().enumerate() {
if pcm <= end {
seg = i;
break;
}
}
if seg >= 8 {
(0x7F ^ mask) as u8
} else {
let uval = ((seg as i32) << 4) | ((pcm >> (seg as i32 + 1)) & 0x0F);
(uval ^ mask) as u8
}
}
pub fn ulaw_to_linear(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 i32;
let mantissa = (u & 0x0F) as i32;
let sample = EXP_LUT[exponent as usize] + (mantissa << (exponent + 3));
if sign != 0 { (-sample) as i16 } else { sample as i16 }
}
fn bytes_to_i16(audio: &[u8]) -> Vec<i16> {
audio.chunks_exact(2).map(|c| i16::from_le_bytes([c[0], c[1]])).collect()
}
fn i16_to_bytes(samples: &[i16]) -> Vec<u8> {
samples.iter().flat_map(|s| s.to_le_bytes()).collect()
}
fn i16_to_f32(samples: &[i16]) -> Vec<f32> {
samples.iter().map(|&s| s as f32).collect()
}
fn f32_to_i16(samples: &[f32]) -> Vec<i16> {
samples
.iter()
.map(|&s| s.clamp(i16::MIN as f32, i16::MAX as f32) as i16)
.collect()
}
pub fn pcm_to_ulaw(pcm: &[u8], resampler: Option<&mut StreamResampler>) -> Vec<u8> {
let samples = bytes_to_i16(pcm);
let resampled = match resampler {
Some(r) => f32_to_i16(&r.process(&i16_to_f32(&samples))),
None => samples,
};
resampled.iter().map(|&s| linear_to_ulaw(s)).collect()
}
pub fn ulaw_to_pcm(ulaw: &[u8], resampler: Option<&mut StreamResampler>) -> Vec<u8> {
let samples: Vec<i16> = ulaw.iter().map(|&b| ulaw_to_linear(b)).collect();
match resampler {
Some(r) => i16_to_bytes(&f32_to_i16(&r.process(&i16_to_f32(&samples)))),
None => i16_to_bytes(&samples),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn linear_to_ulaw_reference_vectors() {
assert_eq!(linear_to_ulaw(0), 0xFF);
assert_eq!(linear_to_ulaw(-1), 0x7E);
assert_eq!(linear_to_ulaw(32767), 0x80);
assert_eq!(linear_to_ulaw(-32768), 0x00);
assert_eq!(linear_to_ulaw(1000), 0xCE);
assert_eq!(linear_to_ulaw(-1000), 0x4E);
}
#[test]
fn ulaw_to_linear_reference_vectors() {
assert_eq!(ulaw_to_linear(0xFF), 0);
assert_eq!(ulaw_to_linear(0x80), 32124);
assert_eq!(ulaw_to_linear(0x00), -32124);
}
#[test]
fn ulaw_round_trip_is_stable() {
for x in (-32768..=32767).step_by(97) {
let x = x as i16;
let once = ulaw_to_linear(linear_to_ulaw(x));
let twice = ulaw_to_linear(linear_to_ulaw(once));
assert_eq!(once, twice, "re-encoding a decoded sample must be idempotent");
if x != 0 {
assert_eq!(once.signum(), x.signum(), "sign must be preserved for {x}");
}
}
}
#[test]
fn pcm_to_ulaw_no_resampler_is_pure_encode() {
let pcm = i16_to_bytes(&[0, -1, 1000, -1000]);
let ulaw = pcm_to_ulaw(&pcm, None);
assert_eq!(ulaw, vec![0xFF, 0x7E, 0xCE, 0x4E]);
}
#[test]
fn ulaw_to_pcm_no_resampler_round_trips_bytes() {
let ulaw = vec![0xFF, 0x7F, 0xDE, 0x5E];
let pcm = ulaw_to_pcm(&ulaw, None);
let samples = bytes_to_i16(&pcm);
assert_eq!(samples.len(), 4);
assert_eq!(samples[0], ulaw_to_linear(0xFF));
assert_eq!(samples[3], ulaw_to_linear(0x5E));
}
}