pub type Sample = f32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Channels {
Mono,
Stereo,
}
impl Channels {
#[inline]
pub fn count(self) -> usize {
match self {
Channels::Mono => 1,
Channels::Stereo => 2,
}
}
pub fn from_count(count: usize) -> Option<Self> {
match count {
1 => Some(Channels::Mono),
2 => Some(Channels::Stereo),
_ => None,
}
}
}
#[derive(Debug, Clone)]
pub struct AudioBuffer {
pub data: Vec<Sample>,
pub sample_rate: u32,
pub channels: Channels,
}
impl std::fmt::Display for AudioBuffer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"AudioBuffer({} frames, {}Hz, {:?}, {:.3}s)",
self.num_frames(),
self.sample_rate,
self.channels,
self.duration_secs()
)
}
}
impl AudioBuffer {
pub fn try_new(
data: Vec<Sample>,
sample_rate: u32,
channels: Channels,
) -> Result<Self, crate::StretchError> {
if sample_rate == 0 || sample_rate > 768000 || data.len() % channels.count() != 0 {
return Err(crate::StretchError::InvalidFormat(
"invalid buffer geometry or rate".into(),
));
}
if data.iter().any(|s| !s.is_finite()) {
return Err(crate::StretchError::NonFiniteInput);
}
Ok(Self {
data,
sample_rate,
channels,
})
}
pub fn new(
data: Vec<Sample>,
sample_rate: u32,
channels: Channels,
) -> Result<Self, crate::StretchError> {
Self::try_new(data, sample_rate, channels)
}
#[inline]
pub fn num_frames(&self) -> usize {
if self.data.is_empty() {
return 0;
}
self.data.len() / self.channels.count()
}
#[inline]
pub fn duration_secs(&self) -> f64 {
self.num_frames() as f64 / self.sample_rate as f64
}
pub fn channel(&self, ch: usize) -> Vec<Sample> {
let nc = self.channels.count();
if ch >= nc {
return Vec::new();
}
self.data.iter().skip(ch).step_by(nc).copied().collect()
}
#[inline]
pub fn into_data(self) -> Vec<Sample> {
self.data
}
pub fn from_mono(data: Vec<Sample>, sample_rate: u32) -> Result<Self, crate::StretchError> {
Self::new(data, sample_rate, Channels::Mono)
}
pub fn from_stereo(data: Vec<Sample>, sample_rate: u32) -> Result<Self, crate::StretchError> {
Self::new(data, sample_rate, Channels::Stereo)
}
#[inline]
pub fn is_empty(&self) -> bool {
self.data.is_empty()
}
#[inline]
pub fn is_mono(&self) -> bool {
self.channels == Channels::Mono
}
#[inline]
pub fn is_stereo(&self) -> bool {
self.channels == Channels::Stereo
}
pub fn left(&self) -> Vec<Sample> {
self.channel(0)
}
pub fn right(&self) -> Vec<Sample> {
if self.channels == Channels::Mono {
self.data.clone()
} else {
self.channel(1)
}
}
pub fn mix_to_mono(&self) -> Self {
if self.channels == Channels::Mono {
return self.clone();
}
let nc = self.channels.count();
let inv = 1.0 / nc as f32;
let mono = self
.data
.chunks_exact(nc)
.map(|frame| frame.iter().sum::<f32>() * inv)
.collect();
Self {
data: mono,
sample_rate: self.sample_rate,
channels: Channels::Mono,
}
}
pub fn to_stereo(&self) -> Self {
if self.channels == Channels::Stereo {
return self.clone();
}
let stereo = self.data.iter().flat_map(|&s| [s, s]).collect();
Self {
data: stereo,
sample_rate: self.sample_rate,
channels: Channels::Stereo,
}
}
#[inline]
pub fn total_samples(&self) -> usize {
self.data.len()
}
pub fn slice(&self, start_frame: usize, num_frames: usize) -> Self {
let total_frames = self.num_frames();
if start_frame >= total_frames {
return Self {
data: Vec::new(),
sample_rate: self.sample_rate,
channels: self.channels,
};
}
let end_frame = (start_frame + num_frames).min(total_frames);
let nc = self.channels.count();
let start_idx = start_frame * nc;
let end_idx = end_frame * nc;
Self {
data: self.data[start_idx..end_idx].to_vec(),
sample_rate: self.sample_rate,
channels: self.channels,
}
}
pub fn concatenate(buffers: &[&AudioBuffer]) -> Self {
if buffers.is_empty() {
return Self {
data: vec![],
sample_rate: 44100,
channels: Channels::Mono,
};
}
let sample_rate = buffers[0].sample_rate;
let channels = buffers[0].channels;
let total_len: usize = buffers.iter().map(|b| b.data.len()).sum();
let mut data = Vec::with_capacity(total_len);
for buf in buffers {
if buf.sample_rate != sample_rate || buf.channels != channels {
return Self {
data: Vec::new(),
sample_rate,
channels,
};
}
data.extend_from_slice(&buf.data);
}
Self {
data,
sample_rate,
channels,
}
}
pub fn normalize(&self, target_peak: f32) -> Self {
let current_peak = self.data.iter().map(|s| s.abs()).fold(0.0f32, f32::max);
if current_peak == 0.0 {
return self.clone();
}
let gain = target_peak / current_peak;
Self {
data: self.data.iter().map(|s| s * gain).collect(),
sample_rate: self.sample_rate,
channels: self.channels,
}
}
pub fn apply_gain(&self, gain_db: f32) -> Self {
let linear = 10.0f32.powf(gain_db / 20.0);
Self {
data: self.data.iter().map(|s| s * linear).collect(),
sample_rate: self.sample_rate,
channels: self.channels,
}
}
pub fn trim_silence(&self, threshold: f32) -> Self {
let nc = self.channels.count();
let total_frames = self.num_frames();
if total_frames == 0 {
return self.clone();
}
let first = (0..total_frames)
.find(|&frame| (0..nc).any(|ch| self.data[frame * nc + ch].abs() >= threshold));
let first = match first {
Some(f) => f,
None => {
return Self {
data: vec![],
sample_rate: self.sample_rate,
channels: self.channels,
};
}
};
let last = (0..total_frames)
.rev()
.find(|&frame| (0..nc).any(|ch| self.data[frame * nc + ch].abs() >= threshold))
.unwrap_or(first);
self.slice(first, last - first + 1)
}
pub fn from_channels(channels_data: &[Vec<Sample>], sample_rate: u32) -> Self {
let nc = channels_data.len();
let channels = if nc == 1 {
Channels::Mono
} else {
Channels::Stereo
};
let num_frames = channels_data.iter().map(|c| c.len()).min().unwrap_or(0);
let mut data = Vec::with_capacity(num_frames * nc);
for i in 0..num_frames {
for ch in channels_data {
data.push(ch[i]);
}
}
Self {
data,
sample_rate,
channels,
}
}
}
impl PartialEq for AudioBuffer {
fn eq(&self, other: &Self) -> bool {
self.sample_rate == other.sample_rate
&& self.channels == other.channels
&& self.data == other.data
}
}
impl AsRef<[Sample]> for AudioBuffer {
#[inline]
fn as_ref(&self) -> &[Sample] {
&self.data
}
}
impl AsMut<[Sample]> for AudioBuffer {
#[inline]
fn as_mut(&mut self) -> &mut [Sample] {
&mut self.data
}
}
impl From<AudioBuffer> for Vec<Sample> {
#[inline]
fn from(buffer: AudioBuffer) -> Self {
buffer.data
}
}
#[derive(Debug)]
pub struct FrameIter<'a> {
data: &'a [Sample],
channels: usize,
pos: usize,
}
impl<'a> Iterator for FrameIter<'a> {
type Item = &'a [Sample];
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.pos + self.channels > self.data.len() {
return None;
}
let frame = &self.data[self.pos..self.pos + self.channels];
self.pos += self.channels;
Some(frame)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = (self.data.len() - self.pos) / self.channels;
(remaining, Some(remaining))
}
}
impl<'a> ExactSizeIterator for FrameIter<'a> {}
impl<'a> IntoIterator for &'a AudioBuffer {
type Item = &'a [Sample];
type IntoIter = FrameIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.frames()
}
}
impl AudioBuffer {
pub fn frames(&self) -> FrameIter<'_> {
FrameIter {
data: &self.data,
channels: self.channels.count(),
pos: 0,
}
}
#[inline]
pub fn peak(&self) -> f32 {
self.data.iter().map(|s| s.abs()).fold(0.0f32, f32::max)
}
pub fn rms(&self) -> f32 {
if self.data.is_empty() {
return 0.0;
}
let sum_sq: f64 = self.data.iter().map(|&s| (s as f64) * (s as f64)).sum();
(sum_sq / self.data.len() as f64).sqrt() as f32
}
pub fn fade_in(&self, duration_frames: usize) -> Self {
let nc = self.channels.count();
let total_frames = self.num_frames();
let fade_frames = duration_frames.min(total_frames);
let mut data = self.data.clone();
for frame in 0..fade_frames {
let gain = frame as f32 / fade_frames as f32;
for ch in 0..nc {
data[frame * nc + ch] *= gain;
}
}
Self {
data,
sample_rate: self.sample_rate,
channels: self.channels,
}
}
pub fn fade_out(&self, duration_frames: usize) -> Self {
let nc = self.channels.count();
let total_frames = self.num_frames();
let fade_frames = duration_frames.min(total_frames);
let fade_start = total_frames - fade_frames;
let mut data = self.data.clone();
for frame in fade_start..total_frames {
let pos_in_fade = frame - fade_start;
let gain = 1.0 - pos_in_fade as f32 / fade_frames as f32;
for ch in 0..nc {
data[frame * nc + ch] *= gain;
}
}
Self {
data,
sample_rate: self.sample_rate,
channels: self.channels,
}
}
pub fn resample(&self, target_sample_rate: u32) -> Self {
if target_sample_rate == self.sample_rate || self.data.is_empty() {
return Self {
data: self.data.clone(),
sample_rate: target_sample_rate,
channels: self.channels,
};
}
let nc = self.channels.count();
let src_frames = self.num_frames();
let target_frames = (src_frames as f64 * target_sample_rate as f64
/ self.sample_rate as f64)
.round() as usize;
if target_frames == 0 {
return Self {
data: vec![],
sample_rate: target_sample_rate,
channels: self.channels,
};
}
let mut output = Vec::with_capacity(target_frames * nc);
for ch in 0..nc {
let channel_data: Vec<f32> = self.data.iter().skip(ch).step_by(nc).copied().collect();
let resampled =
crate::support::resample::resample_sinc_default(&channel_data, target_frames);
if ch == 0 {
output.resize(target_frames * nc, 0.0);
}
for (i, &s) in resampled.iter().enumerate() {
output[i * nc + ch] = s;
}
}
Self {
data: output,
sample_rate: target_sample_rate,
channels: self.channels,
}
}
pub fn crossfade_into(&self, other: &AudioBuffer, crossfade_frames: usize) -> Self {
if self.sample_rate != other.sample_rate || self.channels != other.channels {
return self.clone();
}
let nc = self.channels.count();
let self_frames = self.num_frames();
let other_frames = other.num_frames();
let fade_frames = crossfade_frames.min(self_frames).min(other_frames);
if fade_frames == 0 {
let mut data = self.data.clone();
data.extend_from_slice(&other.data);
return Self {
data,
sample_rate: self.sample_rate,
channels: self.channels,
};
}
let non_fade_self = self_frames - fade_frames;
let total_frames = self_frames + other_frames - fade_frames;
let mut data = Vec::with_capacity(total_frames * nc);
data.extend_from_slice(&self.data[..non_fade_self * nc]);
for i in 0..fade_frames {
let t = i as f32 / fade_frames as f32;
let fade_out = 0.5 * (1.0 + (std::f32::consts::PI * t).cos());
let fade_in = 1.0 - fade_out;
let self_frame = non_fade_self + i;
let other_frame = i;
for ch in 0..nc {
let s = self.data[self_frame * nc + ch] * fade_out
+ other.data[other_frame * nc + ch] * fade_in;
data.push(s);
}
}
if fade_frames < other_frames {
data.extend_from_slice(&other.data[fade_frames * nc..]);
}
Self {
data,
sample_rate: self.sample_rate,
channels: self.channels,
}
}
pub fn reverse(&self) -> Self {
let nc = self.channels.count();
let num_frames = self.num_frames();
let mut data = Vec::with_capacity(self.data.len());
for frame in (0..num_frames).rev() {
let start = frame * nc;
data.extend_from_slice(&self.data[start..start + nc]);
}
Self {
data,
sample_rate: self.sample_rate,
channels: self.channels,
}
}
#[inline]
pub fn channel_count(&self) -> usize {
self.channels.count()
}
pub fn split_at(&self, frame: usize) -> (Self, Self) {
let nc = self.channels.count();
let total_frames = self.num_frames();
let split = frame.min(total_frames);
let left = Self {
data: self.data[..split * nc].to_vec(),
sample_rate: self.sample_rate,
channels: self.channels,
};
let right = Self {
data: self.data[split * nc..].to_vec(),
sample_rate: self.sample_rate,
channels: self.channels,
};
(left, right)
}
pub fn repeat(&self, count: usize) -> Self {
if count == 0 || self.data.is_empty() {
return Self {
data: vec![],
sample_rate: self.sample_rate,
channels: self.channels,
};
}
let mut data = Vec::with_capacity(self.data.len() * count);
for _ in 0..count {
data.extend_from_slice(&self.data);
}
Self {
data,
sample_rate: self.sample_rate,
channels: self.channels,
}
}
pub fn mix(&self, other: &AudioBuffer) -> Self {
if self.sample_rate != other.sample_rate || self.channels != other.channels {
return self.clone();
}
let len = self.data.len().max(other.data.len());
let mut data = vec![0.0f32; len];
for (i, d) in data.iter_mut().enumerate() {
let a = if i < self.data.len() {
self.data[i]
} else {
0.0
};
let b = if i < other.data.len() {
other.data[i]
} else {
0.0
};
*d = a + b;
}
Self {
data,
sample_rate: self.sample_rate,
channels: self.channels,
}
}
pub fn silence(sample_rate: u32, duration_secs: f64) -> Self {
let num_samples = (sample_rate as f64 * duration_secs).round() as usize;
Self {
data: vec![0.0; num_samples],
sample_rate,
channels: Channels::Mono,
}
}
pub fn tone(freq_hz: f64, sample_rate: u32, duration_secs: f64, amplitude: f32) -> Self {
let num_samples = (sample_rate as f64 * duration_secs).round() as usize;
let data: Vec<f32> = (0..num_samples)
.map(|i| {
amplitude
* (2.0 * std::f64::consts::PI * freq_hz * i as f64 / sample_rate as f64).sin()
as f32
})
.collect();
Self {
data,
sample_rate,
channels: Channels::Mono,
}
}
pub fn pan(&self, pan: f32) -> Self {
assert!(
(-1.0..=1.0).contains(&pan),
"pan must be in [-1.0, 1.0], got {pan}"
);
if self.channels == Channels::Stereo {
return self.clone();
}
let angle = (pan + 1.0) * 0.5 * std::f32::consts::FRAC_PI_2;
let gain_l = angle.cos();
let gain_r = angle.sin();
let mut data = Vec::with_capacity(self.data.len() * 2);
for &s in &self.data {
data.push(s * gain_l);
data.push(s * gain_r);
}
Self {
data,
sample_rate: self.sample_rate,
channels: Channels::Stereo,
}
}
pub fn with_gain_envelope(&self, breakpoints: &[(f64, f32)]) -> Self {
if breakpoints.is_empty() || self.data.is_empty() {
return self.clone();
}
let nc = self.channels.count();
let num_frames = self.num_frames();
let mut data = self.data.clone();
for frame in 0..num_frames {
let time = frame as f64 / self.sample_rate as f64;
let gain = interpolate_breakpoints(breakpoints, time);
for ch in 0..nc {
data[frame * nc + ch] *= gain;
}
}
Self {
data,
sample_rate: self.sample_rate,
channels: self.channels,
}
}
pub fn remove_dc(&self) -> Self {
if self.data.is_empty() {
return self.clone();
}
let nc = self.channels.count();
let num_frames = self.num_frames();
let mut data = self.data.clone();
for ch in 0..nc {
let sum: f64 = self
.data
.iter()
.skip(ch)
.step_by(nc)
.map(|&s| s as f64)
.sum();
let mean = (sum / num_frames as f64) as f32;
for frame in 0..num_frames {
data[frame * nc + ch] -= mean;
}
}
Self {
data,
sample_rate: self.sample_rate,
channels: self.channels,
}
}
pub fn apply_window(&self, window_type: crate::support::window::WindowType) -> Self {
if self.data.is_empty() {
return self.clone();
}
let num_frames = self.num_frames();
let window = crate::support::window::generate_window(window_type, num_frames);
let nc = self.channels.count();
let mut data = self.data.clone();
for frame in 0..num_frames {
for ch in 0..nc {
data[frame * nc + ch] *= window[frame];
}
}
Self {
data,
sample_rate: self.sample_rate,
channels: self.channels,
}
}
}
fn interpolate_breakpoints(breakpoints: &[(f64, f32)], time: f64) -> f32 {
if breakpoints.len() == 1 || time <= breakpoints[0].0 {
return breakpoints[0].1;
}
if time >= breakpoints[breakpoints.len() - 1].0 {
return breakpoints[breakpoints.len() - 1].1;
}
for i in 1..breakpoints.len() {
if time <= breakpoints[i].0 {
let (t0, v0) = breakpoints[i - 1];
let (t1, v1) = breakpoints[i];
let dt = t1 - t0;
if dt <= 0.0 {
return v1;
}
let frac = ((time - t0) / dt) as f32;
return v0 + frac * (v1 - v0);
}
}
breakpoints[breakpoints.len() - 1].1
}