-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add missing safety limiter files from last commit
- Loading branch information
Showing
2 changed files
with
126 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
name = "safety_limiter" | ||
version = "0.1.0" | ||
authors = ["Casey Primozic <[email protected]>"] | ||
edition = "2021" | ||
|
||
[lib] | ||
crate-type = ["cdylib", "rlib"] | ||
|
||
[dependencies] | ||
dsp = { path = "../dsp" } | ||
compressor = { path = "../compressor", default-features = false } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
use std::ptr::addr_of_mut; | ||
|
||
use dsp::db_to_gain; | ||
|
||
static mut IO_BUFFER: [f32; dsp::FRAME_SIZE] = [0.0; dsp::FRAME_SIZE]; | ||
|
||
const LOOKAHEAD_SAMPLE_COUNT: usize = 4; | ||
|
||
struct SafetyLimiterState { | ||
pub lookahead_buffer: [f32; LOOKAHEAD_SAMPLE_COUNT], | ||
pub envelope: f32, | ||
} | ||
|
||
impl SafetyLimiterState { | ||
pub const fn new() -> Self { | ||
Self { | ||
lookahead_buffer: [0.0; LOOKAHEAD_SAMPLE_COUNT], | ||
envelope: 0.0, | ||
} | ||
} | ||
} | ||
|
||
static mut STATE: SafetyLimiterState = SafetyLimiterState::new(); | ||
|
||
const ATTACK_COEFFICIENT: f32 = 0.3; | ||
|
||
const RELEASE_COEFFICIENT: f32 = 0.05; | ||
|
||
const THRESHOLD: f32 = 10.; | ||
const RATIO: f32 = 40.; | ||
|
||
fn io_buf() -> &'static mut [f32; dsp::FRAME_SIZE] { unsafe { &mut *addr_of_mut!(IO_BUFFER) } } | ||
|
||
fn state() -> &'static mut SafetyLimiterState { unsafe { &mut *addr_of_mut!(STATE) } } | ||
|
||
fn detect_level_peakand_apply_envelope(envelope: &mut f32, sample: f32) -> f32 { | ||
let abs_sample = sample.abs(); | ||
println!("abs_sample={abs_sample}, envelope={envelope}"); | ||
dsp::one_pole( | ||
envelope, | ||
abs_sample, | ||
if abs_sample > *envelope { | ||
ATTACK_COEFFICIENT | ||
} else { | ||
RELEASE_COEFFICIENT | ||
}, | ||
) | ||
} | ||
|
||
fn compute_gain_to_apply(detected_level_db: f32) -> f32 { | ||
let target_level_db = THRESHOLD + (detected_level_db - THRESHOLD) / RATIO; | ||
let db_to_reduce = detected_level_db - target_level_db; | ||
db_to_gain(-db_to_reduce) | ||
} | ||
|
||
fn process(envelope: &mut f32, sample: f32) -> f32 { | ||
// some audio drivers behave badly when you send them `NaN` or `Infinity`... | ||
if !sample.is_normal() { | ||
return 0.; | ||
} | ||
|
||
// default to limiting with a very short attack and release | ||
let detected_level_linear = detect_level_peakand_apply_envelope(envelope, sample); | ||
dbg!(detected_level_linear); | ||
let detected_level_db = dsp::gain_to_db(detected_level_linear); | ||
|
||
if detected_level_db < THRESHOLD { | ||
return sample; | ||
} | ||
|
||
let gain_to_apply = compute_gain_to_apply(detected_level_db); | ||
println!("sample={sample}, gain_to_apply={gain_to_apply}"); | ||
let sample = sample * gain_to_apply; | ||
|
||
// apply hard clipping as a last resort | ||
dsp::clamp(-4., 4., sample) | ||
} | ||
|
||
#[no_mangle] | ||
pub extern "C" fn safety_limiter_process() { | ||
let state = state(); | ||
let io_buf = io_buf(); | ||
|
||
for &sample in &state.lookahead_buffer { | ||
process(&mut state.envelope, sample); | ||
} | ||
|
||
for &sample in &io_buf[..LOOKAHEAD_SAMPLE_COUNT] { | ||
process(&mut state.envelope, sample); | ||
} | ||
|
||
state | ||
.lookahead_buffer | ||
.copy_from_slice(&io_buf[io_buf.len() - LOOKAHEAD_SAMPLE_COUNT..]); | ||
} | ||
|
||
#[test] | ||
fn coefficients() { | ||
println!("100. to db: {}", dsp::gain_to_db(100.)); | ||
|
||
let mut envelope = 0.; | ||
let signal = vec![ | ||
0., 0., 40., 40., 40., 40., 40., 40., 40., 40., 40., 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, | ||
0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, | ||
0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, | ||
]; | ||
let mut applied = vec![0.; signal.len()]; | ||
|
||
for i in 0..signal.len() { | ||
applied[i] = process(&mut envelope, signal[i]); | ||
} | ||
|
||
println!("{applied:?}") | ||
} |