Skip to content

Commit

Permalink
Add MVP safety limiter impl
Browse files Browse the repository at this point in the history
 * I seem to be able to understand how compressors work much better now than when I was working on the multi-band compressor.  Probably because this one is way simpler in multiple ways and totally static.
 * Fix issue loading idential wavetables in fm synth
 * Remove code in faust AWP that clips values to [-1,1]
  • Loading branch information
Ameobea committed Dec 13, 2024
1 parent 832ee6c commit 3a62e7c
Show file tree
Hide file tree
Showing 11 changed files with 36 additions and 12 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,4 @@ public/midi_renderer.wasm
public/oscilloscope.wasm
public/spectrum_viz_full.wasm
public/sampler.wasm
public/safety_limiter.wasm
5 changes: 5 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ build-all:
cp ./engine/target/wasm32-unknown-unknown/release/oscilloscope.wasm ./public
cp ./engine/target/wasm32-unknown-unknown/release/spectrum_viz_full.wasm ./public
cp ./engine/target/wasm32-unknown-unknown/release/sampler.wasm ./public
cp ./engine/target/wasm32-unknown-unknown/release/safety_limiter.wasm ./public
cp ./engine/build/* ./src

just build-sinsy
Expand Down Expand Up @@ -319,3 +320,7 @@ build-sampler:
debug-sampler:
cd ./engine/sampler && cargo build --target wasm32-unknown-unknown && \
cp ../target/wasm32-unknown-unknown/debug/sampler.wasm ../../public

debug-safety-limiter:
cd ./engine/safety_limiter && cargo build --target wasm32-unknown-unknown && \
cp ../target/wasm32-unknown-unknown/debug/safety_limiter.wasm ../../public
10 changes: 9 additions & 1 deletion engine/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ members = [
"midi_renderer",
"oscilloscope",
"canvas_utils",
"sampler"
"sampler",
"safety_limiter"
]

[profile.release]
Expand Down
4 changes: 4 additions & 0 deletions engine/compressor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ crate-type = ["cdylib", "rlib"]

[dependencies]
dsp = { path = "../dsp" }

[features]
exports = []
default = ["exports"]
11 changes: 9 additions & 2 deletions engine/compressor/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![feature(const_float_methods)]

use dsp::{
circular_buffer::CircularBuffer,
db_to_gain,
Expand Down Expand Up @@ -224,7 +226,7 @@ fn detect_level_peak(

/// Given the attack time in milliseconds, compute the coefficient for a one-pole lowpass filter to
/// be used in the envelope follower.
fn compute_attack_coefficient(attack_time_ms: f32) -> f32 {
pub const fn compute_attack_coefficient(attack_time_ms: f32) -> f32 {
let attack_time_s = (attack_time_ms * 0.001).max(0.0001);
let attack_time_samples = attack_time_s * SAMPLE_RATE;
let attack_coefficient = 1. - 1. / attack_time_samples;
Expand All @@ -233,7 +235,7 @@ fn compute_attack_coefficient(attack_time_ms: f32) -> f32 {

/// Given the release time in milliseconds, compute the coefficient for a one-pole highpass filter
/// to be used in the envelope follower.
fn compute_release_coefficient(release_time_ms: f32) -> f32 {
pub const fn compute_release_coefficient(release_time_ms: f32) -> f32 {
let release_time_s = (release_time_ms * 0.001).max(0.0001);
let release_time_samples = release_time_s * SAMPLE_RATE;
let release_coefficient = 1. / release_time_samples;
Expand Down Expand Up @@ -547,6 +549,7 @@ impl MultibandCompressor {
}
}

#[cfg(feature = "exports")]
#[no_mangle]
pub extern "C" fn init_compressor() -> *mut MultibandCompressor {
use std::fmt::Write;
Expand All @@ -561,24 +564,28 @@ pub extern "C" fn init_compressor() -> *mut MultibandCompressor {
Box::into_raw(Box::new(compressor))
}

#[cfg(feature = "exports")]
#[no_mangle]
pub extern "C" fn get_compressor_input_buf_ptr(compressor: *mut MultibandCompressor) -> *mut f32 {
let compressor = unsafe { &mut *compressor };
compressor.input_buffer.as_mut_ptr()
}

#[cfg(feature = "exports")]
#[no_mangle]
pub extern "C" fn get_compressor_output_buf_ptr(compressor: *mut MultibandCompressor) -> *mut f32 {
let compressor = unsafe { &mut *compressor };
compressor.output_buffer.as_mut_ptr()
}

#[cfg(feature = "exports")]
#[no_mangle]
pub extern "C" fn get_sab_ptr(compressor: *mut MultibandCompressor) -> *mut f32 {
let compressor = unsafe { &mut *compressor };
compressor.sab.as_mut_ptr()
}

#[cfg(feature = "exports")]
#[no_mangle]
pub extern "C" fn process_compressor(
compressor: *mut MultibandCompressor,
Expand Down
2 changes: 1 addition & 1 deletion engine/waveform_renderer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub fn get_waveform_buf_ptr(ctx: *mut WaveformRendererCtx) -> *mut f32 {
}

#[inline(always)]
fn ms_to_samples(sample_rate: f32, ms: f32) -> u32 { ((ms * sample_rate) / 1000.) as u32 }
const fn ms_to_samples(sample_rate: f32, ms: f32) -> u32 { ((ms * sample_rate) / 1000.) as u32 }

#[inline(always)]
fn sample_to_y_val(sample: f32, half_height: f32, max_distance_from_0: f32) -> u32 {
Expand Down
3 changes: 2 additions & 1 deletion engine/wavetable/src/fm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2699,7 +2699,8 @@ pub extern "C" fn fm_synth_get_wavetable_data_ptr(
ctx.wavetables[wavetable_ix] = new_wavetable;
} else if ctx.wavetables.len() != wavetable_ix {
panic!(
"Tried to set wavetable index {} but only {} wavetables exist",
"Tried to set wavetable index {} but only {} wavetables exist. Wavetable bank indices are \
managed by the JS code in `FmSynth.tsx`; it's not our fault!",
wavetable_ix,
ctx.wavetables.len()
);
Expand Down
2 changes: 1 addition & 1 deletion engine/wavetable/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![feature(const_maybe_uninit_assume_init, get_mut_unchecked)]
#![feature(get_mut_unchecked)]

pub mod fm;

Expand Down
6 changes: 1 addition & 5 deletions faust-compiler/FaustWorkletModuleTemplate.template.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,11 +274,7 @@ class FaustAudioWorkletProcessor extends AudioWorkletProcessor {
for (let i = 0; i < Math.min(outputs.length, this.dspOutChannels.length); i++) {
const dspOutput = this.dspOutChannels[i];
for (let channelIx = 0; channelIx < outputs[i].length; channelIx++) {
// If we send NaNs or out-of-range values, computer gets very upset and destroys
// my entire system audio on Linux until I shut down this AWP.
for (let sampleIx = 0; sampleIx < outputs[i][channelIx].length; sampleIx++) {
outputs[i][channelIx][sampleIx] = clamp(-1.0, 1.0, dspOutput[sampleIx]);
}
outputs[i][channelIx].set(dspOutput);
}
}

Expand Down
1 change: 1 addition & 0 deletions src/graphEditor/nodes/CustomAudio/FMSynth/FMSynth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ export default class FMSynth implements ForeignNode {
samples: bank.samples,
};
if (
this.lastSetWavetableData.wavetableIx === setWavetableData.wavetableIx &&
this.lastSetWavetableData.baseFrequency === setWavetableData.baseFrequency &&
this.lastSetWavetableData.waveformsPerDimension === setWavetableData.waveformsPerDimension &&
this.lastSetWavetableData.waveformLength === setWavetableData.waveformLength &&
Expand Down

0 comments on commit 3a62e7c

Please sign in to comment.