151template <
typename SampleType =
float>
230 void reset() { previous_shaped_ = 0.0; }
237 if (!std::isfinite(character01))
return;
238 const double c = std::clamp(character01, 0.0, 1.0);
247 double drive()
const {
return drive_; }
255 return 1.0 / (1.0 + std::max(0.0, control_drive));
274 const double bias = network_.knee_voltage(std::max(amperes, 0.0));
281 return 1.0 / network_.conductance(network_.knee_voltage(std::max(amperes, 0.0)));
293 static double shape(
double s,
double beta) {
return s - beta * s * s * s / 3.0; }
299 const double s2 = s * s;
300 return 0.5 * s2 - beta * s2 * s2 / 12.0;
322 const double fundamental = 1.0 - beta * s * s / 4.0;
323 if (!(fundamental > 0.0))
return 0.0;
324 return (beta * s * s / 12.0) / fundamental;
337 SampleType
process(SampleType input,
double control_drive) {
338 if (!std::isfinite(
static_cast<double>(input)) || !std::isfinite(control_drive)) {
340 return SampleType{0};
342 const double x = std::max(0.0, control_drive);
345 const double s = std::clamp(
static_cast<double>(input) * drive_, -limit, limit);
349 const double delta = s - previous_shaped_;
354 :
shape(0.5 * (s + previous_shaped_), beta);
356 shaped =
shape(s, beta);
358 previous_shaped_ = s;
363 if (!std::isfinite(output)) {
365 return SampleType{0};
367 return static_cast<SampleType
>(output);
371 void configure_junction() {
376 network_.
leg_a = 1.0;
377 network_.
leg_b = 1.0;
386 junction::JunctionPair network_{};
387 double series_offset_ = 0.0;
389 double previous_shaped_ = 0.0;
437template <
typename SampleType =
float>
479 highpass_.prepare(
static_cast<SampleType
>(sample_rate));
480 lowpass_.prepare(
static_cast<SampleType
>(sample_rate));
481 highpass_.set_cutoff(
static_cast<SampleType
>(
kLowCornerHz));
489 previous_input_ = 0.0;
494 if (!std::isfinite(character01))
return;
500 const double steepest = std::max(positive_, negative_);
509 return u - 0.5 * symmetric_ *
u * std::abs(
u) - 0.5 * asymmetric_ *
u *
u;
515 const double u2 =
u *
u;
516 return 0.5 * u2 - symmetric_ * u2 * std::abs(
u) / 6.0 - asymmetric_ * u2 *
u / 6.0;
520 if (!std::isfinite(
static_cast<double>(input))) {
522 return SampleType{0};
524 const double u = std::clamp(
static_cast<double>(input), -limit_, limit_);
528 const double delta =
u - previous_input_;
538 const SampleType blocked = highpass_.process_highpass(
static_cast<SampleType
>(shaped));
539 const SampleType output = lowpass_.process_lowpass(blocked);
540 if (!std::isfinite(
static_cast<double>(output))) {
542 return SampleType{0};
549 TptFilterT<SampleType> lowpass_{};
555 double previous_input_ = 0.0;
633template <
typename SampleType =
float>
688 sample_rate_ = sample_rate > 0.0 ? sample_rate : sample_rate_;
689 input_bracket_.prepare(sample_rate_);
690 output_bracket_.prepare(sample_rate_);
691 bridge_.prepare(sample_rate_);
692 sidechain_hpf_.prepare(
static_cast<SampleType
>(sample_rate_));
693 sidechain_hpf_.set_cutoff(
static_cast<SampleType
>(sc_hpf_hz_));
694 fast_follower_.prepare(
static_cast<SampleType
>(sample_rate_));
695 slow_follower_.prepare(
static_cast<SampleType
>(sample_rate_));
704 input_bracket_.reset();
705 output_bracket_.reset();
707 sidechain_dc_.reset();
708 sidechain_hpf_.reset();
709 fast_follower_.reset();
710 slow_follower_.reset();
711 smoothed_reduction_db_ = 0.0;
756 bridge_.set_character(character_);
757 input_bracket_.set_character(character_);
758 output_bracket_.set_character(character_);
763 const double current_percent = mix_ * 100.0;
771 sidechain_hpf_.set_cutoff(
static_cast<SampleType
>(sc_hpf_hz_));
781 bridge_.set_adaa(on);
782 input_bracket_.set_adaa(on);
783 output_bracket_.set_adaa(on);
811 const double slope = ratio_ >=
kLimitRatio ? 0.0 : 1.0 / ratio_;
829 for (
int i = 0;
i < 96; ++
i) {
831 if (next == gr)
break;
850 if (!std::isfinite(
static_cast<double>(input))) {
852 return SampleType{0};
854 const double dry =
static_cast<double>(input);
858 const double detector_input = feedback_ ? feedback_tap_ : dry;
859 const double envelope = detect(detector_input);
860 const double envelope_db =
864 smoothed_reduction_db_ =
snap_to_zero(smoothed_reduction_db_ +
865 smooth_coef_ * (target_db - smoothed_reduction_db_));
870 const double drive = 1.0 / gain - 1.0;
872 const SampleType bracketed = input_bracket_.process(input);
873 const SampleType attenuated = bridge_.process(bracketed, drive);
874 const SampleType wet = output_bracket_.process(attenuated);
876 feedback_tap_ =
static_cast<double>(wet);
877 return static_cast<SampleType
>(dry * (1.0 - mix_) +
878 static_cast<double>(wet) * makeup_linear_ * mix_);
893 double detect(
double detector_input) {
894 const SampleType blocked = sidechain_dc_.process(
static_cast<SampleType
>(detector_input));
895 const SampleType filtered = sidechain_hpf_.process_highpass(blocked);
897 const double fast =
static_cast<double>(fast_follower_.
process(filtered));
901 const double slow =
static_cast<double>(slow_follower_.
process(filtered));
902 if (!auto_release_)
return fast;
913 sustain_ =
snap_to_zero(sustain_ + sustain_coef_ * (sustained - sustain_));
914 return fast + sustain_ * (slow - fast);
917 void update_ballistics() {
918 const auto attack =
static_cast<SampleType
>(attack_ms_);
921 fast_follower_.
set_release_ms(
static_cast<SampleType
>(release_ms_));
925 void update_smoothing() {
930 double sample_rate_ = 48000.0;
932 double threshold_db_ = -12.0;
934 double knee_db_ = 6.0;
935 double attack_ms_ = 3.0;
936 double release_ms_ = 400.0;
937 double makeup_db_ = 0.0;
938 double character_ = 0.35;
940 double sc_hpf_hz_ = 100.0;
941 bool auto_release_ =
false;
942 bool feedback_ =
true;
944 double makeup_linear_ = 1.0;
945 double smooth_coef_ = 1.0;
946 double sustain_coef_ = 1.0;
948 double smoothed_reduction_db_ = 0.0;
949 double sustain_ = 0.0;
950 double feedback_tap_ = 0.0;
952 TransformerBracketT<SampleType> input_bracket_{};
953 TransformerBracketT<SampleType> output_bracket_{};
954 DiodeBridgeGainT<SampleType> bridge_{};
955 DcBlocker<SampleType> sidechain_dc_{};
956 TptFilterT<SampleType> sidechain_hpf_{};
957 BallisticsFilterT<SampleType> fast_follower_{};
958 BallisticsFilterT<SampleType> slow_follower_{};
Peak/RMS envelope follower with independent attack and release curves.
void set_attack_ms(SampleType ms)
Definition ballistics_filter.hpp:46
void set_release_ms(SampleType ms)
Definition ballistics_filter.hpp:53
@ peak
Definition ballistics_filter.hpp:33
SampleType process(SampleType input)
Definition ballistics_filter.hpp:64
Two transformer brackets around one diode bridge, with a feedback sidechain.
Definition diode_bridge_compressor.hpp:634
double static_curve_feedback_db(double input_db) const
The gain reduction the FEEDBACK loop settles at for a given input level — the fixed point of GR = sta...
Definition diode_bridge_compressor.hpp:827
static constexpr double kKneeDbMin
Definition diode_bridge_compressor.hpp:645
static constexpr double kRatioMin
Definition diode_bridge_compressor.hpp:639
static constexpr double kScHpfHzMin
Definition diode_bridge_compressor.hpp:648
void set_character(double character01)
0..1 colour depth — the one macro.
Definition diode_bridge_compressor.hpp:753
double gain_reduction_db() const
Smoothed gain reduction in dB (≤ 0) as of the last processed sample.
Definition diode_bridge_compressor.hpp:794
static constexpr double kAttackMsMax
Definition diode_bridge_compressor.hpp:642
void set_makeup_db(double db)
Definition diode_bridge_compressor.hpp:744
void set_threshold_db(double db)
Definition diode_bridge_compressor.hpp:718
void set_knee_db(double w)
Definition diode_bridge_compressor.hpp:727
void set_release_ms(double ms)
Definition diode_bridge_compressor.hpp:738
SampleType process(SampleType input)
Definition diode_bridge_compressor.hpp:846
void set_sc_hpf_hz(double hz)
Definition diode_bridge_compressor.hpp:768
static constexpr double kThresholdDbMin
Definition diode_bridge_compressor.hpp:637
static constexpr double kCtrlSmoothMs
Definition diode_bridge_compressor.hpp:662
void reset()
Definition diode_bridge_compressor.hpp:703
void set_mix_percent(double percent)
Parallel dry/wet as a percentage, matching the catalog table's units.
Definition diode_bridge_compressor.hpp:762
void set_attack_ms(double ms)
Definition diode_bridge_compressor.hpp:732
double static_curve_db(double input_db) const
The static characteristic's gain reduction (≤ 0) for an input level in dB, ignoring the loop and the ...
Definition diode_bridge_compressor.hpp:810
void set_ratio(double r)
Definition diode_bridge_compressor.hpp:723
static constexpr double kReleaseMsMax
Definition diode_bridge_compressor.hpp:644
static constexpr double kRatioMax
Definition diode_bridge_compressor.hpp:640
static constexpr double kSustainThresholdDb
Definition diode_bridge_compressor.hpp:675
static constexpr double kAutoSlowFactor
Definition diode_bridge_compressor.hpp:666
static constexpr double kSustainTauMs
Definition diode_bridge_compressor.hpp:671
void process_block(SampleType *io, int n)
Definition diode_bridge_compressor.hpp:881
static constexpr double kLimitRatio
Definition diode_bridge_compressor.hpp:656
static constexpr double kKneeDbMax
Definition diode_bridge_compressor.hpp:646
int latency_samples() const noexcept
Zero.
Definition diode_bridge_compressor.hpp:791
static constexpr double kThresholdDbMax
Definition diode_bridge_compressor.hpp:638
void set_adaa(bool on)
Definition diode_bridge_compressor.hpp:780
static constexpr double kScHpfHzMax
Definition diode_bridge_compressor.hpp:649
void prepare(double sample_rate)
Never allocates — every member is fixed-size POD.
Definition diode_bridge_compressor.hpp:687
static constexpr double kReleaseMsMin
Definition diode_bridge_compressor.hpp:643
static constexpr double kMakeupDbMax
Definition diode_bridge_compressor.hpp:647
double control_drive() const
The control drive x currently applied to the bridge.
Definition diode_bridge_compressor.hpp:800
static double worst_case_gain()
The bound the Forge registry cites (series law 8): makeup ceiling times both brackets' peak gain.
Definition diode_bridge_compressor.hpp:839
static constexpr double kEnvelopeEpsilon
Definition diode_bridge_compressor.hpp:680
void set_feedback(bool on)
On is the authentic topology: the detector senses the output.
Definition diode_bridge_compressor.hpp:778
GainReduction gain_reduction() const noexcept
Definition diode_bridge_compressor.hpp:795
void set_auto_release(bool on)
Program-dependent dual-slope release.
Definition diode_bridge_compressor.hpp:775
static constexpr double kAttackMsMin
Definition diode_bridge_compressor.hpp:641
The diode bridge as a gain element: an exact 1/(1 + x) divider plus the audio-path curvature that div...
Definition diode_bridge_compressor.hpp:152
static double max_operating_amplitude(double beta)
Where the cubic stops being monotonic, less the safety margin.
Definition diode_bridge_compressor.hpp:304
SampleType process(SampleType input, double control_drive)
One sample.
Definition diode_bridge_compressor.hpp:337
static constexpr double kIdeality
Definition diode_bridge_compressor.hpp:161
void set_adaa(bool on)
Off is for measurement only — it is what the alias-reduction test compares against.
Definition diode_bridge_compressor.hpp:244
void reset()
Definition diode_bridge_compressor.hpp:230
static double shape_antiderivative(double s, double beta)
∫shape ds = s²/2 − β·s⁴/12.
Definition diode_bridge_compressor.hpp:298
static constexpr double kDriveBridgeFloor
Definition diode_bridge_compressor.hpp:201
static constexpr double kSaturationCurrent
Definition diode_bridge_compressor.hpp:167
void prepare(double)
Present for lifecycle symmetry with the rest of the catalog.
Definition diode_bridge_compressor.hpp:228
static constexpr double kAdaaEpsilon
Definition diode_bridge_compressor.hpp:219
void set_character(double character01)
0..1 colour depth.
Definition diode_bridge_compressor.hpp:236
static double curvature(double control_drive)
β(x) = β0·(1 + κ·x) — the curvature in force at a given control drive.
Definition diode_bridge_compressor.hpp:287
static double control_drive_for_gain_db(double gain_reduction_db)
x = 10^(|GR|/20) − 1, the drive that realises a given (negative) gain reduction in dB.
Definition diode_bridge_compressor.hpp:260
static constexpr double kDriveCurvature
Definition diode_bridge_compressor.hpp:189
static constexpr double kBaseCurvature
Definition diode_bridge_compressor.hpp:183
static double gain_for_control_drive(double control_drive)
g = 1/(1 + x).
Definition diode_bridge_compressor.hpp:254
double control_drive_for_current(double amperes) const
The control drive produced by a physical bias current, in amps.
Definition diode_bridge_compressor.hpp:273
static constexpr double kMaxControlDrive
Definition diode_bridge_compressor.hpp:206
static constexpr double kDriveBridgeMax
Definition diode_bridge_compressor.hpp:194
static constexpr double kMonotonicMargin
Definition diode_bridge_compressor.hpp:212
double drive() const
The internal operating amplitude full-scale audio is mapped to.
Definition diode_bridge_compressor.hpp:247
static double shape(double s, double beta)
The odd cubic the balanced bridge imposes, with slope exactly 1 at the origin: shape(s) = s − β·s³/3.
Definition diode_bridge_compressor.hpp:293
DiodeBridgeGainT()
Definition diode_bridge_compressor.hpp:223
static constexpr double kSeriesResistance
Definition diode_bridge_compressor.hpp:173
static double third_harmonic_ratio(double s, double beta)
Closed-form third-harmonic ratio of the shaper for a sine of amplitude s at curvature β,...
Definition diode_bridge_compressor.hpp:321
double dynamic_resistance(double amperes) const
The bridge's small-signal dynamic resistance at a bias current, in ohms.
Definition diode_bridge_compressor.hpp:280
Canonical gain-reduction meter value.
Definition dynamics_contract.hpp:27
static GainReduction from_signed_db(double signed_db) noexcept
Adapt a signed gain value, where attenuation is negative dB.
Definition dynamics_contract.hpp:32
First-order TPT (trapezoidal integration) filter.
Definition tpt_filter.hpp:30
Shared envelope and gain-reduction vocabulary for dynamics processors.
The published compressor equations, once.
The semiconductor junction law, once.
double soft_knee_reduction_db(double over_db, double knee_db, double slope)
The same characteristic in the GAIN-REDUCTION domain: dB over threshold → reduction in dB,...
Definition dynamics_core.hpp:208
double retain_finite(double candidate, double current) noexcept
Control setters retain their last valid value when automation supplies a non-finite sample.
Definition dynamics_core.hpp:117
T linear_to_db(T linear)
Linear gain → amplitude dB, floored at kDbFloorAmplitude so silence maps to a finite −200 dB rather t...
Definition units.hpp:79
T ms_to_onepole_coef(T ms, T sample_rate)
A time constant in ms → the a in the one-pole smoother y += a·(x − y), i.e.
Definition units.hpp:202
T db_to_linear(T db)
Amplitude dB → linear gain. 0 dB → 1, −6.0206 dB → 0.5.
Definition units.hpp:71
Definition additive_bank.hpp:218
DiodeBridgeCompressorT< float > DiodeBridgeCompressor
Definition diode_bridge_compressor.hpp:961
DiodeBridgeGainT< double > DiodeBridgeGain64
Definition diode_bridge_compressor.hpp:394
@ c
Definition spectrum_trace.hpp:35
@ attack
Definition envelope.hpp:58
TransformerBracketT< double > TransformerBracket64
Definition diode_bridge_compressor.hpp:560
T snap_to_zero(T x)
Returns 0 if |x| < snap_threshold, else x.
Definition denormal.hpp:45
TransformerBracketT< float > TransformerBracket
Definition diode_bridge_compressor.hpp:559
@ a
Definition formant_filter_bank.hpp:20
@ u
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
DiodeBridgeGainT< float > DiodeBridgeGain
Definition diode_bridge_compressor.hpp:393
DiodeBridgeCompressorT< double > DiodeBridgeCompressor64
Definition diode_bridge_compressor.hpp:962
double saturation_current
Is, the reverse saturation current.
Definition junction.hpp:98
double ideality
n, the ideality factor. Sets the knee's SOFTNESS: higher is softer.
Definition junction.hpp:101
double leg_b
Reciprocal series-diode count of the reverse leg.
Definition junction.hpp:109
double leg_a
Reciprocal series-diode count of the forward leg.
Definition junction.hpp:105
double conductance(double v) const
di/dv — the incremental conductance a Newton solve needs.
Definition junction.hpp:123
First-order topology-preserving transform (TPT) filter.
The real-unit vocabulary shared by every DSP block in the catalog.