nsys-signal-utils 0.1.1

Signal processing utilities
Documentation
//! Functions on PCM encoded signal data

pub fn center (samples : &mut [i16]) -> &mut [i16] {
  let Some (max) = samples.iter().max() else { return &mut [] };
  let Some (min) = samples.iter().min() else { return &mut [] };
  let d = -(max + min) / 2;
  for sample in samples.iter_mut() {
    *sample += d;
  }
  samples
}

/// Centers signal before normalizing
pub fn normalize_peak (samples : &mut [i16], to : i16) -> &mut [i16] {
  let safeabs = |i| match i {
    &i16::MIN => i16::MAX,
    i         => i.abs()
  };
  let samples = center (samples);
  let Some (max_abs) = samples.iter().max_by_key (|sample| safeabs (sample)) else {
    debug_assert_eq!(samples.len(), 0);
    return &mut []
  };
  let scale = to.abs() as f64 / *max_abs as f64;
  // TODO: move expect to expression when stabilized
  #[expect(clippy::cast_possible_truncation)]
  for sample in samples.iter_mut() {
    *sample = (scale * *sample as f64) as i16
  }
  samples
}


/// Trim beginning and end of sample values that are zero.
///
/// ```
/// # extern crate signal_utils;
/// # fn main() {
/// # use signal_utils::pcm::trim_silence;
/// let pcm = vec![0, 0, 0, 1, 2, 3, 4, 0, 0];
/// debug_assert_eq!(trim_silence (&pcm[..]), &pcm[3..7]);
/// let pcm = vec![1, 2, 3, 4];
/// debug_assert_eq!(trim_silence (&pcm[..]), &[1, 2, 3, 4]);
/// # }
/// ```
pub fn trim_silence (samples : &[i16]) -> &[i16] {
  let mut start = 0;
  for (i, sample) in samples.iter().enumerate() {
    start = i;
    if *sample != 0 {
      break
    }
  }
  let mut end = samples.len();
  for i in 1..samples.len() {
    let i = samples.len() - i;
    end = i+1;
    if samples[i] != 0 {
      break
    }
  }
  if end <= start {
    start = 0;
    end   = samples.len();
  }
  &(*samples)[start..end]
}

/// Trim beginning and end of samples to first zero crossings.
///
/// ```
/// # extern crate signal_utils;
/// # fn main() {
/// # use signal_utils::pcm::trim_zeros;
/// let pcm = vec![0, 1, 2, -3, -4, 5, 6, 7, -8, 9, -10, -11];
/// debug_assert_eq!(trim_zeros (&pcm[..]), &pcm[0..10]);
/// let pcm = vec![1, 1, 0, 1, 1, 1, 0, 1, 1];
/// debug_assert_eq!(trim_zeros (&pcm[..]), &[0,1,1,1,0]);
/// # }
/// ```
pub fn trim_zeros (samples : &[i16]) -> &[i16] {
  let mut start = 0;
  for i in 0..samples.len()-1 {
    start = i+1;
    if samples[i] == 0 {
      start = i;
      break
    }
    match ((*samples)[i].is_positive(), (*samples)[i+1].is_positive()) {
      (true, false) | (false, true) => break,
      _ => {}
    }
  }
  let mut end = samples.len();
  for i in 1..samples.len() {
    let i = samples.len() - i;
    end = i;
    if samples[i] == 0 {
      break
    }
    match ((*samples)[i].is_positive(), (*samples)[i-1].is_positive()) {
      (true, false) | (false, true) => break,
      _ => {}
    }
  }
  if end <= start {
    start = 0;
    end   = samples.len();
  }
  &(*samples)[start..end]
}