use crate::{Error, Result};
pub const S16_MIN: i32 = -32768;
pub const S16_MAX: i32 = 32767;
#[must_use]
pub fn nint(x: f64) -> f64 {
if !x.is_finite() {
return 0.0;
}
if x >= 0.0 {
(x + 0.5).floor()
} else {
(x - 0.5).ceil()
}
}
#[must_use]
pub fn to_s16(sample: f64) -> i16 {
nint(sample).clamp(S16_MIN as f64, S16_MAX as f64) as i16
}
#[must_use]
pub fn channel_to_s16(samples: &[f64]) -> Vec<i16> {
samples.iter().copied().map(to_s16).collect()
}
pub fn interleave_s16(channels: &[Vec<f64>]) -> Result<Vec<i16>> {
if channels.is_empty() {
return Ok(Vec::new());
}
let frame_len = channels[0].len();
if channels.iter().any(|c| c.len() != frame_len) {
return Err(Error::PcmInvalid);
}
let num_channels = channels.len();
let mut out = Vec::with_capacity(frame_len * num_channels);
for n in 0..frame_len {
for ch in channels {
out.push(to_s16(ch[n]));
}
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nint_rounds_half_away_from_zero() {
assert_eq!(nint(2.5), 3.0);
assert_eq!(nint(-2.5), -3.0);
assert_eq!(nint(0.5), 1.0);
assert_eq!(nint(-0.5), -1.0);
assert_eq!(nint(1.5), 2.0);
assert_eq!(nint(-1.5), -2.0);
}
#[test]
fn nint_rounds_non_halves_to_nearest() {
assert_eq!(nint(2.4), 2.0);
assert_eq!(nint(2.6), 3.0);
assert_eq!(nint(-2.4), -2.0);
assert_eq!(nint(-2.6), -3.0);
assert_eq!(nint(0.0), 0.0);
assert_eq!(nint(-0.0), 0.0);
}
#[test]
fn nint_non_finite_is_zero() {
assert_eq!(nint(f64::NAN), 0.0);
assert_eq!(nint(f64::INFINITY), 0.0);
assert_eq!(nint(f64::NEG_INFINITY), 0.0);
}
#[test]
fn to_s16_saturates() {
assert_eq!(to_s16(0.0), 0);
assert_eq!(to_s16(100.4), 100);
assert_eq!(to_s16(100.5), 101);
assert_eq!(to_s16(-100.5), -101);
assert_eq!(to_s16(40000.0), S16_MAX as i16);
assert_eq!(to_s16(-40000.0), S16_MIN as i16);
assert_eq!(to_s16(32767.0), 32767);
assert_eq!(to_s16(-32768.0), -32768);
assert_eq!(to_s16(32767.5), 32767);
assert_eq!(to_s16(-32768.5), -32768);
}
#[test]
fn channel_to_s16_maps_each_sample() {
let got = channel_to_s16(&[0.0, 1.4, 1.5, -1.5, 50000.0]);
assert_eq!(got, vec![0, 1, 2, -2, S16_MAX as i16]);
}
#[test]
fn interleave_two_channels() {
let l = vec![0.0, 10.0, 20.0];
let r = vec![1.0, 11.0, 21.0];
let got = interleave_s16(&[l, r]).unwrap();
assert_eq!(got, vec![0, 1, 10, 11, 20, 21]);
}
#[test]
fn interleave_single_channel_is_identity_order() {
let mono = vec![3.4, 3.5, -3.5];
let got = interleave_s16(&[mono]).unwrap();
assert_eq!(got, vec![3, 4, -4]);
}
#[test]
fn interleave_empty_is_empty() {
assert!(interleave_s16(&[]).unwrap().is_empty());
}
#[test]
fn interleave_rejects_length_mismatch() {
let l = vec![0.0, 1.0];
let r = vec![0.0, 1.0, 2.0];
assert!(matches!(interleave_s16(&[l, r]), Err(Error::PcmInvalid)));
}
}