Last active
August 25, 2022 09:33
-
-
Save x42/eeb2aa9f9cc4a9083fb2cf2d86645c9a to your computer and use it in GitHub Desktop.
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
*.swp | |
*.lv2/ | |
*-svg/ |
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
mcomp.lv2/mcomp.so : mcomp.dsp | |
faust2lv2 mcomp.dsp | |
mcomp-svg: mcomp.dsp | |
faust -svg mcomp.dsp | |
show: mcomp-svg | |
x-www-browser mcomp-svg/process.svg | |
clean: | |
-rm -rf mcomp-svg | |
-rm -rf mcomp.lv2 | |
.PHONY: show clean |
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
declare name "mComp"; | |
declare author "Robin Gareus"; | |
declare license "GPLv2+"; | |
import ("stdfaust.lib"); | |
/* Calculate compressor gain redux. | |
* | |
* This is similar to `(co).peak_compression_gain_mono_db`, except | |
* signal power is used, and there is a fixed exponential knee. | |
* | |
* `strength` : [0,1]. The amount of gain or attenuation to be applied | |
* 0: no compression; 0.25: ratio 1:2 ; 1.0: hard limit. | |
* The compression ratio (dB/dB above threshold) is given: | |
* ratio = 1/(1-sqrt(st)) | |
* `threshold`: Level (in dB/RMS) above which compression ratio is applied, | |
* Note, since there is an exponentially curved gain (knee), the redux is | |
* -3dB at threshold. If `strength` is 1 (limiter), the threshold is also | |
* the max output level when the input exceeds the threshold value. | |
* Note: attack time is still relevant, this is not a brickwall limiter. | |
* `att`: Attack time (in seconds) - Time it takes for the signal to become fully compressed | |
* after exceeding the threshold. | |
* `rel`: Release time (in seconds) - Minimum recovery time to uncompressed signal-level | |
* after falling below threshold. | |
* `hld`: Hold (boolean) - Retain current attenuation when the signal subceeds the threshold. | |
* This prevents modulation of the noise-floor and can counter-act 'pumping'. | |
* | |
* return value is in gain to be applied in dB | |
* | |
* Example: | |
* process= _ <: par(i,2,_) : (_, (compcalc (st, th, at, rl, 0) : ba.db2linear)) : * ; | |
*/ | |
compcalc (strength, thresh, att, rel, hld, x) = loop ~ (_ , _) : select3 (2) *-strength | |
with { | |
loop (zr1, zr2) = r1, r2, ba.linear2db (max (ma.EPSILON, sqrt (2 * r2))) - thresh | |
with { | |
/* calculate signal power relative to threshold, LPF using attack time constant */ | |
x2 = lvl ~ _ | |
with { | |
lvl (y) = y + w_att * (p_thr + (x * x) - y) | |
with { | |
p_thr = .5 * pow (10.0, 0.1 * thresh); | |
w_att = 0.5 / (att * ma.SR); | |
}; | |
}; | |
p_hld = ba.if (hld != 1, 0, pow (10.0, 0.1 * thresh)); // 2 * p_thr | |
/* apply fall off using release-time */ | |
w_rel = 3.5 / (rel * ma.SR); | |
r1 = ba.if (zr1 < x2, x2, ba.if (x2 < p_hld, zr1, zr1 - w_rel * zr1)); | |
r2 = ba.if (zr2 < x2, x2, ba.if (x2 < p_hld, zr2, zr2 + w_rel * (zr1 - zr2))); | |
}; | |
}; | |
MultiBandComp (NBand, NChn) = | |
si.bus (NChn) <: si.bus (2 * NChn) // split: key, signal | |
: calc_gain | |
: apply_gain | |
: makeup_gain | |
with { | |
/* key/sidechain. Analyze input signal, and calculate gain to be applied | |
* to the signal. The live signal is passed though after the analysis data. | |
* | |
* (key, signal) -> (gains, signal) | |
*/ | |
calc_gain = ((par (i, NChn, analyze) : calc_redux), si.bus (NChn)); | |
/* Spectrum analyzer. Note: an.analyzer returns the data in reverse | |
* order. So the wires are reversed here. | |
* | |
* (1 channel key) -> (levels per freq band) | |
*/ | |
analyze = an.analyzer (6, (ctls_freq)) : ro.cross (NBand); | |
/* Run the compressor on the key signal for each band, | |
* and send the compression redux level to meters. | |
* | |
* Compressor settings are identical for all channels of each band. | |
* Yet each band has its own threshold, and stenght/ratio, | |
* while attack/release are common settings for all. | |
* | |
* This processes all channels. This allows the gain for | |
* all channels of a given band to be linked. | |
* | |
* (levels per freq band, ..) -> (gain per freq band, ..) | |
*/ | |
calc_redux = | |
(ctls_strength, ctls_thresh, si.bus (NBand * NChn)) | |
: ro.interleave (NBand, 2 + NChn) | |
: par (i, NBand, compressor (NChn)) : si.bus (NChn * NBand) | |
: par (b, NBand, par (c, NChn, meter (b + 1, c + 1))) | |
; | |
/* Apply the gain, using a shelf filter cascade (linear phase) | |
* | |
* (gains, signal) -> (signal) | |
*/ | |
apply_gain = | |
ro.interleave (NChn, NBand + 1) // separate channels | |
: par (i, NChn, ro.cross (NBand), _) // reverse gains | |
: par (i, NChn, shelfcascade ((ctls_freq))) | |
; | |
makeup_gain = par (i, NChn, _ * ctl_mg); | |
compressor (N, st, th) = calc_comp_db_NChn (si.smoo (st), si.smoo (ctl_th + th), ctl_at, ctl_rl, ctl_hd, N); | |
calc_comp_db_NChn (s,t,a,r,h,1) = compcalc (s,t,a,r,h); | |
calc_comp_db_NChn (s,t,a,r,h,N) = par (i, N, compcalc (s,t,a,r,h)) | |
<: (si.bus (N), (ba.parallelMin (N) <: si.bus (N))) | |
: ro.interleave (N, 2) | |
: par (i, N, select2 (ctl_lk)) | |
; | |
/* higher order low, band and hi shelf filter primitives */ | |
ls3 (f, g) = fi.svf.ls (f, .5, g3) : fi.svf.ls (f, .707, g3) : fi.svf.ls (f, 2, g3) with {g3 = g/3;}; | |
bs3 (f1, f2, g) = ls3 (f1, -g) : ls3 (f2, g); | |
hs3 (f, g) = fi.svf.hs (f, .5, g3) : fi.svf.hs (f, .707, g3) : fi.svf.hs (f, 2, g3) with {g3 = g/3;}; | |
/* Cascade of shelving filters to apply gain per band. | |
* | |
* `lf` : list of frequencies | |
* followed by (count(lf) + 1) gain parameters in reverse order | |
* (the first input is the gain of the last stage). | |
*/ | |
shelfcascade (lf) = fbus (lf), ls3 (first (lf)) : sc (lf) | |
with { | |
sc ((f1, f2, lf)) = fbus ((f2, lf)), bs3 (f1, f2) : sc ((f2, lf)); // recursive pattern | |
sc ((f1, f2)) = _, bs3 (f1, f2) : hs3 (f2); // halting pattern | |
fbus (l) = par (i, outputs (l), _); // a bus of the size of a list | |
first ((x, xs)) = x; // first element of a list | |
}; | |
meter (b, c) = _<: attach (_, (max (-12):min (0):vbargraph ("Redux [unit:dB][symbol:redux%b%c]redux band %b chn %c", -12, 0))); | |
ratio2stength (r) = (1 - r)^2 / r^2; | |
//TODO - ensure freq are ordered, and maintain a minimum distance -> default/min/max | |
dft_freq (i, b, t) = b * pow ((t / b), i / (NBand - 1)); | |
ctls_freq = par (i, NBand - 1, vslider ("[unit:Hz][scale:log]Frequency %i", dft_freq (i, 500, 17500), 20, 20000, 1)); | |
ctls_thresh = par (i, NBand, vslider ("[%i][unit:dB][scale:log]Band %i Threshold", 0, -24, 0, 0.5)); | |
ctls_strength = par (i, NBand, vslider ("[%i][unit:%]Band %i Ratio", 2, 1, 16, 0.1) : ratio2stength); | |
ctl_th = vslider ("Threshold[unit:dB][scale:log]", 0, -60, 0, 1); // master thresh | |
ctl_at = vslider ("Attack[unit:s][scale:log]", 0.01, 0.001, 0.1, .01); | |
ctl_rl = vslider ("Release[unit:s][scale:log]", 0.3, 0.03, 3.0, .1); | |
ctl_mg = vslider ("Makeup Gain[unit:dB][symbol:makup_gain]", 0, -6, 6, 0.5) : ba.db2linear; | |
ctl_hd = checkbox ("Hold"); | |
ctl_lk = checkbox ("Stereo link"); | |
}; | |
/* 4 band, stereo multiband compressor */ | |
process= MultiBandComp (4, 2); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment