diff --git a/dynamics/oneknob.h b/dynamics/oneknob.h index 16c22e8..7eb52de 100644 --- a/dynamics/oneknob.h +++ b/dynamics/oneknob.h @@ -1,6 +1,7 @@ #pragma once #include "audio_math.h" #include "rms_detector.h" +#include #include namespace trnr { @@ -30,6 +31,7 @@ inline float hp_filter_process(hp_filter& f, float x) struct oneknob_comp { // params float amount = 0.f; + bool multiplied = false; // state rms_detector detector; @@ -38,9 +40,10 @@ struct oneknob_comp { float release_coef; float envelope_level; float sidechain_in; + double samplerate; }; -inline void oneknob_init(oneknob_comp& c, float samplerate, float window_ms) +inline void oneknob_init(oneknob_comp& c, double samplerate, float window_ms) { rms_init(c.detector, samplerate, window_ms); hp_filter_init(c.filter, samplerate); @@ -48,6 +51,7 @@ inline void oneknob_init(oneknob_comp& c, float samplerate, float window_ms) const float attack_ms = 0.2f; const float release_ms = 150.f; + c.samplerate = samplerate; c.attack_coef = expf(-1.0f / (attack_ms * 1e-6 * samplerate)); c.release_coef = expf(-1.0f / (release_ms * 1e-3 * samplerate)); c.envelope_level = -60.f; @@ -59,11 +63,17 @@ inline void oneknob_process_block(oneknob_comp& c, sample** audio, int frames) { const float min_user_ratio = 1.0f; const float max_user_ratio = 20.0f; - const float threshold_db = -9.f; + const float threshold_db = -12.f; - const float amount = fmaxf(0.0f, fminf(powf(c.amount, 2.f), 1.0f)); // clamp to [0, 1] + const float amount = fmaxf(0.0f, fminf(powf(c.amount, 2.f), 1.0f)); float ratio = min_user_ratio + amount * (max_user_ratio - min_user_ratio); + const float fast_attack = 0.1f; + const float slow_attack = 0.6f; + const float fast_release = 90.f; + const float slow_release = 300.f; + const float max_gr = 24.f; + for (int i = 0; i < frames; ++i) { float rms_value = rms_process(c.detector, c.sidechain_in); float envelope_in = lin_2_db(fmaxf(fabs(rms_value), 1e-20f)); @@ -89,8 +99,17 @@ inline void oneknob_process_block(oneknob_comp& c, sample** audio, int frames) audio[0][i] *= gain_reduction_lin; audio[1][i] *= gain_reduction_lin; + // calculate program-dependent attack/release times + float norm_gr = std::clamp(gain_reduction_db / max_gr, 0.f, 1.f); + float release_ms = fast_release + (slow_release - fast_release) * (1.f - norm_gr); + float attack_ms = fast_attack + (slow_attack - fast_attack) * (1.f - norm_gr); + + c.attack_coef = expf(-1.0f / (attack_ms * 1e-6 * c.samplerate)); + c.release_coef = expf(-1.0f / (release_ms * 1e-3 * c.samplerate)); + // feedback compression float sum = sqrtf(0.5f * (audio[0][i] * audio[0][i] + audio[1][i] * audio[1][i])); + if (c.multiplied) sum *= 3.f; c.sidechain_in = hp_filter_process(c.filter, sum); } }