use rubato::{
Resampler, SincFixedIn, SincInterpolationParameters, SincInterpolationType, WindowFunction,
};
pub struct RockWallLimiter<const BUFSIZE: usize, const NCHAN: usize> {
release: f32,
hold_time: i32,
hold_timer: i32,
last_amplitude: f32,
upsampler: SincFixedIn<f32>,
upsample_buffers: Vec<Vec<f32>>,
pub ambi_mode: bool,
}
#[inline(always)]
fn sanitize(x: f32) -> f32 {
if f32::is_infinite(x) {
return 0.0;
}
if f32::is_nan(x) {
return 0.0;
}
if x.abs() < f32::MIN_POSITIVE {
return 0.0;
}
x
}
impl<const BUFSIZE: usize, const NCHAN: usize> RockWallLimiter<BUFSIZE, NCHAN> {
pub fn new(samplerate: f32, ambi_mode: bool) -> Self {
let params = SincInterpolationParameters {
sinc_len: 256,
f_cutoff: 0.95,
interpolation: SincInterpolationType::Linear,
oversampling_factor: 256,
window: WindowFunction::BlackmanHarris2,
};
let upsampler = SincFixedIn::new(4.0, 4.0, params, BUFSIZE, NCHAN).unwrap();
let upsampler_bufsize = upsampler.output_frames_max();
RockWallLimiter {
hold_time: (0.1 * samplerate) as i32,
hold_timer: 0,
release: f32::powf(0.001, 1.0 / (0.1 * samplerate)),
last_amplitude: 0.0,
upsampler,
upsample_buffers: (0..NCHAN).map(|_| vec![0.0; upsampler_bufsize]).collect(),
ambi_mode,
}
}
#[inline(always)]
pub fn process_frame(&mut self, input: &mut [f32; NCHAN], sidechain: f32) {
let inst_amp = sanitize(sidechain).abs();
let amp_from_follower = if self.hold_timer > 0 {
self.hold_timer -= 1;
self.last_amplitude
} else {
self.last_amplitude * self.release + inst_amp * (1.0 - self.release)
};
let amplitude = if amp_from_follower > inst_amp {
amp_from_follower
} else {
self.hold_timer = self.hold_time;
inst_amp
};
self.last_amplitude = sanitize(amplitude);
let gain = if amplitude > 1.0 {
1.0 / amplitude
} else {
1.0
};
for c in 0..NCHAN {
input[c] = sanitize(input[c]) * gain;
}
}
pub fn process(&mut self, block: [[f32; BUFSIZE]; NCHAN]) -> [[f32; BUFSIZE]; NCHAN] {
let mut out_buf = [[0.0; BUFSIZE]; NCHAN];
let mut workbuf = [0.0; NCHAN];
if self.ambi_mode {
let mut mask = [false; NCHAN];
mask[0] = true;
self.upsampler
.process_into_buffer(&block, &mut self.upsample_buffers, Some(&mask))
.unwrap();
} else {
self.upsampler
.process_into_buffer(&block, &mut self.upsample_buffers, None)
.unwrap();
}
for s in 0..BUFSIZE {
let mut true_peak = f32::MIN;
if self.ambi_mode {
true_peak = f32::max(true_peak, f32::abs(block[0][s]));
for j in 0..4 {
true_peak = f32::max(true_peak, f32::abs(self.upsample_buffers[0][s * 4 + j]));
}
for c in 0..NCHAN {
workbuf[c] = block[c][s];
}
} else {
for c in 0..NCHAN {
true_peak = f32::max(true_peak, f32::abs(block[c][s]));
for j in 0..4 {
true_peak =
f32::max(true_peak, f32::abs(self.upsample_buffers[c][s * 4 + j]));
}
workbuf[c] = block[c][s];
}
}
self.process_frame(&mut workbuf, true_peak);
for c in 0..NCHAN {
out_buf[c][s] = workbuf[c];
}
}
out_buf
}
}