use super::ir_parse;
use super::ir_resample;
use log::{debug, info};
use std::io;
use std::path::Path;
pub struct CabSimIr {
pub samples: Vec<f32>,
pub sample_rate: u32,
pub original_rate: u32,
pub normalized: bool,
}
impl CabSimIr {
#[cold]
pub fn load(path: &Path, target_rate: u32, normalize: bool) -> io::Result<Box<Self>> {
info!(
"[Loader] Loading IR from \"{}\" (target_rate={} Hz, normalize={})",
path.display(),
target_rate,
normalize
);
let data = ir_parse::read_file(path)?;
let (samples, original_rate) = ir_parse::parse_wav(&data)?;
let mut samples = if target_rate != 0 && target_rate != original_rate {
info!(
"[Loader] IR resampling: {} Hz -> {} Hz",
original_rate, target_rate
);
ir_resample::resample(&samples, original_rate, target_rate)?
} else {
samples
};
let effective_rate = if target_rate != 0 && target_rate != original_rate {
target_rate
} else {
original_rate
};
let normalized = if normalize {
let was_normalized = Self::normalize_in_place(&mut samples);
if was_normalized {
info!("[Loader] IR normalized to peak 1.0");
} else {
debug!("[Loader] IR normalization skipped (peak already ~1.0 or zero)");
}
was_normalized
} else {
false
};
info!(
"[Loader] IR loaded: {} samples, {} Hz, normalized={}",
samples.len(),
effective_rate,
normalized
);
Ok(Box::new(Self {
samples,
sample_rate: effective_rate,
original_rate,
normalized,
}))
}
fn normalize_in_place(samples: &mut [f32]) -> bool {
let peak = samples.iter().fold(0.0f32, |acc, &s| acc.max(s.abs()));
if peak <= 0.0 || peak >= 1.0 && (peak - 1.0).abs() < f32::EPSILON {
return false;
}
let gain = 1.0 / peak;
for s in samples.iter_mut() {
*s *= gain;
}
true
}
pub fn resample(input: &[f32], input_rate: u32, output_rate: u32) -> io::Result<Vec<f32>> {
ir_resample::resample(input, input_rate, output_rate)
}
}
#[cfg(test)]
#[path = "loader_test.rs"]
mod loader_test;