use std::collections::HashMap;
use rand::Rng;
use std::f64::consts::PI;
pub enum PanLaw {
Linear,
ConstantPower,
Neg4_5dB
}
#[inline(always)]
pub fn rms(data: &[f64]) -> f64 {
let mut sum = 0.0;
for i in 0..data.len() {
sum += f64::powf(data[i], 2.0);
}
f64::sqrt(sum / data.len() as f64)
}
pub fn adjust_level(audio: &mut Vec<f64>, max_db: f64) {
let target_max_level = f64::powf(10.0, max_db / 20.0);
let mut current_max_level = 0.0;
for i in 0..audio.len() {
let current_abs = audio[i].abs();
if current_abs > current_max_level {
current_max_level = current_abs;
}
}
let scaling_factor = target_max_level / current_max_level;
for i in 0..audio.len() {
audio[i] *= scaling_factor;
}
}
pub fn fade_in(audio: &mut Vec<f64>, envelope: crate::WindowType, duration: usize) {
let duration = usize::min(duration, audio.len());
let envelope_samples = crate::generate_window(envelope, duration * 2);
for i in 0..duration {
audio[i] *= envelope_samples[i];
}
}
pub fn fade_out(audio: &mut Vec<f64>, envelope: crate::WindowType, duration: usize) {
let duration = usize::min(duration, audio.len());
let envelope_samples = crate::generate_window(envelope, duration * 2);
for i in audio.len() - duration..audio.len() {
audio[i] *= envelope_samples[i + duration * 2 - audio.len()];
}
}
pub fn leak_dc_bias_averager(audio: &mut Vec<f64>) {
let average = audio.iter().sum::<f64>() / audio.len() as f64;
for i in 0..audio.len() {
audio[i] -= average;
}
}
pub fn leak_dc_bias_filter(audio: &mut Vec<f64>) {
const ALPHA: f64 = 0.95;
let mut delay_register = 0.0;
for i in 0..audio.len() {
let combined_signal = audio[i] + ALPHA * delay_register;
audio[i] = combined_signal - delay_register;
delay_register = combined_signal;
}
}
pub fn force_equal_energy(audio: &mut Vec<f64>, dbfs: f64, window_size: usize) {
let target_level = f64::powf(10.0, dbfs / 20.0);
let num_level_frames = f64::ceil((audio.len() / window_size) as f64) as usize;
let mut energy_levels: Vec<f64> = vec![0.0; num_level_frames + 2];
for i in 0..num_level_frames {
let start_idx = i * window_size;
let end_idx = usize::min(start_idx + window_size, audio.len());
energy_levels[i+1] = rms(&audio[start_idx..end_idx]);
}
energy_levels[0] = energy_levels[1];
energy_levels[num_level_frames + 1] = energy_levels[num_level_frames];
for i in 0..window_size / 2 {
audio[i] = audio[i] * target_level / energy_levels[0];
}
for level_frame_idx in 1..num_level_frames + 1 {
let slope = (energy_levels[level_frame_idx + 1] - energy_levels[level_frame_idx]) / window_size as f64;
let y_int = energy_levels[level_frame_idx];
let start_frame = level_frame_idx * window_size - window_size / 2;
let end_frame = usize::min(start_frame + window_size, audio.len());
for sample_idx in start_frame..end_frame {
let scaler = 1.0 / (slope * (sample_idx - start_frame) as f64 + y_int);
audio[sample_idx] *= scaler;
}
}
let mut max_level = 0.0;
for sample_idx in 0..audio.len() {
max_level = f64::max(audio[sample_idx].abs(), max_level);
}
for sample_idx in 0..audio.len() {
audio[sample_idx] *= target_level / max_level;
}
}
pub fn exchange_frames(audio: &mut [f64], hop: usize) {
let end_idx = audio.len() - audio.len() % (hop * 2);
let step = hop * 2;
for i in (0..end_idx).step_by(step) {
for j in i..i+hop {
let temp = audio[j];
audio[j] = audio[j + hop];
audio[j + hop] = temp;
}
}
}
pub fn exchange_frames_stochastic(audio: &mut [f64], max_hop: usize) {
let mut future_indices: HashMap<usize, bool> = HashMap::with_capacity(audio.len());
let mut idx = 0;
while idx < audio.len() {
if !future_indices.contains_key(&idx) {
let mut possible_indices: Vec<usize> = Vec::new();
for i in idx..usize::min(idx + max_hop, audio.len()) {
if !future_indices.contains_key(&i) {
possible_indices.push(i);
}
}
let swap_idx = rand::thread_rng().gen_range(0..possible_indices.len());
let temp = audio[idx];
audio[idx] = audio[swap_idx];
audio[swap_idx] = temp;
future_indices.insert(swap_idx, true);
}
idx += 1;
}
}
pub fn panner(num_channels: usize, start_pos: f64, end_pos: f64, num_iterations: usize, pan_law: PanLaw) -> Vec<Vec<f64>> {
let mut pos_vec: Vec<f64> = vec![0.0; num_iterations];
let step_val: f64 = (end_pos - start_pos) / num_iterations as f64;
pos_vec[0] = start_pos % num_iterations as f64;
for i in 1..num_iterations {
pos_vec[i] = pos_vec[i-1] + step_val;
}
let mut pan_vec: Vec<Vec<f64>> = Vec::with_capacity(num_iterations);
for i in 0..num_iterations {
let mut coefficients: Vec<f64> = vec![0.0; num_channels];
let int_part = pos_vec[i].trunc();
let decimal_part = pos_vec[i].fract();
let pos = int_part as usize % num_channels;
let next_pos = (pos + 1) % num_channels;
let theta = decimal_part * PI / 2.0;
match pan_law {
PanLaw::Linear => {
coefficients[pos] = 1.0 - decimal_part;
coefficients[next_pos] = decimal_part;
},
PanLaw::ConstantPower => {
coefficients[pos] = f64::cos(theta);
coefficients[next_pos] = f64::sin(theta);
},
PanLaw::Neg4_5dB => {
coefficients[pos] = f64::sqrt((PI / 2.0 - theta) * 2.0 / PI * f64::cos(theta));
coefficients[next_pos] = f64::sqrt(decimal_part * f64::sin(theta));
}
}
pan_vec.push(coefficients);
}
pan_vec
}
pub fn pan_mapper(pan_coefficients: &mut [Vec<f64>], map: &[usize]) {
let mut swap: Vec<f64> = vec![0.0; map.len()];
for i in 0..pan_coefficients.len() {
for j in 0..pan_coefficients[i].len() {
swap[j] = pan_coefficients[i][j];
}
for j in 0..pan_coefficients[i].len() {
pan_coefficients[i][map[j]] = swap[j];
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const DIR: &str = "D:/Recording/tests";
const AUDIO: &str = "D:/Recording/tests/grains.wav";
#[test]
fn test_rms() {
const EPSILON: f64 = 1e-6;
let data = vec![1.1, 0.2, 4.3, -4.1, 5.3, -9.1];
assert!(f64::abs(rms(&data) - 4.957317823178175) < EPSILON);
}
#[test]
fn test_level_adjust() {
const EPSILON: f64 = 1e-6;
const TARGET_DB: f64 = -3.1;
let mut data = vec![1.1, 0.2, 4.3, -4.1, 5.3, -9.1];
adjust_level(&mut data, TARGET_DB);
assert!({
let mut maxval = f64::abs(crate::util::max(&data).unwrap());
let minval = f64::abs(crate::util::min(&data).unwrap());
if minval > maxval {
maxval = minval;
}
let db = 20.0 * f64::log10(maxval);
f64::abs(TARGET_DB - db) < EPSILON
});
}
#[test]
pub fn test_equal_energy() {
let path = String::from(AUDIO);
let mut audio = match crate::read(&path) {
Ok(x) => x,
Err(_) => panic!("could not read audio")
};
force_equal_energy(&mut audio.samples[0], -16.0, 16384);
let path: String = String::from(format!("{}/out2.wav", DIR));
match crate::write(&path, &audio) {
Ok(_) => (),
Err(_) => panic!("could not write audio")
}
}
}