Files
bass-foundry/plugins/quench/core/tests/realtime.rs
T
nvrl 3d316cbe49 feat(quench): add Quench sub-safe clipper
Quench splits the signal with a Linkwitz-Riley crossover and lets the sub
pass unclipped. The top is clipped symmetrically at the ceiling minus the
sub's held peak, so it does not pump at the sub's rate and the sum stays
under the ceiling. What the clip removes is high-passed at the split, so its
products stay out of the low band. Two correction passes and an oversampled
hard clip hold the ceiling; a safety clip catches the downsampler's ringing.

- Controls: Drive, Ceiling, Split, Hold, Soft, Bypass.
- Listen to Quench, a plain clipper at the same settings, or the delta.
- Editor meter compares how much the low band changed for Quench and for a
  plain clip, with a scope of the output, the sub and the top's room.
- Measured: sub level +0.04 dB against -2.2 dB for a plain clip, low-band
  products -35 dB against -16 dB, at 0.5-1.2 dB less RMS.
- Registered in the catalog, workflows, editor-check, README, chain and
  development docs, and the website copy.
2026-11-06 15:51:36 +01:00

66 lines
2.2 KiB
Rust

use quench_core::{Engine, Monitor, Settings};
use std::{
alloc::{GlobalAlloc, Layout, System},
cell::Cell,
};
struct CheckedAllocator;
thread_local! {
static CHECK:Cell<bool>=const {Cell::new(false)};
static OPERATIONS:Cell<usize>=const {Cell::new(0)};
}
fn record() {
if CHECK.try_with(Cell::get).unwrap_or(false) {
let _ = OPERATIONS.try_with(|n| n.set(n.get() + 1));
}
}
unsafe impl GlobalAlloc for CheckedAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
record();
unsafe { System.alloc(layout) }
}
unsafe fn dealloc(&self, pointer: *mut u8, layout: Layout) {
record();
unsafe { System.dealloc(pointer, layout) }
}
unsafe fn realloc(&self, pointer: *mut u8, layout: Layout, size: usize) -> *mut u8 {
record();
unsafe { System.realloc(pointer, layout, size) }
}
}
#[global_allocator]
static ALLOCATOR: CheckedAllocator = CheckedAllocator;
#[test]
fn processing_settings_monitors_and_analysis_do_not_allocate_or_free() {
let mut engine = Engine::new(48000., 2).unwrap();
let mut settings = Settings {
drive: 12.,
..Settings::default()
};
engine.set_settings(settings).unwrap();
let monitors = [Monitor::Quench, Monitor::Plain, Monitor::Delta];
CHECK.with(|v| v.set(true));
for i in 0..96000 {
if i % 4800 == 0 {
let step = i / 4800;
settings.split = 60. + 20. * (step % 6) as f32;
settings.hold = 20. + 40. * (step % 3) as f32;
settings.soft = 25. * (step % 5) as f32;
settings.ceiling = -3. * (step % 3) as f32;
settings.bypass = step % 7 == 6;
engine.set_settings(settings).unwrap();
}
let t = i as f32 / 48000.;
let x = (std::f32::consts::TAU * 45. * t).sin() * 0.5
+ (std::f32::consts::TAU * 733. * t).sin() * 0.5;
std::hint::black_box(engine.process_sample([x, -x], monitors[(i / 9600) % 3]));
if i % 1600 == 0 {
std::hint::black_box(engine.take_analyzer());
}
}
engine.reset();
CHECK.with(|v| v.set(false));
assert_eq!(OPERATIONS.with(Cell::get), 0);
}