Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
diode_bridge_compressor.hpp
Go to the documentation of this file.
1#pragma once
2
117
125#include <pulp/signal/units.hpp>
126
127#include <algorithm>
128#include <cmath>
129#include <cstdint>
130
131namespace pulp::signal {
132
133// ── The current-controlled variable attenuator ────────────────────────────
134
151template <typename SampleType = float>
153public:
154 // ── Design parameters (the complete roster) ───────────────────────────
155
161 static constexpr double kIdeality = 1.9;
162
167 static constexpr double kSaturationCurrent = 1e-14;
168
173 static constexpr double kSeriesResistance = 1000.0;
174
183 static constexpr double kBaseCurvature = 0.5;
184
189 static constexpr double kDriveCurvature = 0.6;
190
194 static constexpr double kDriveBridgeMax = 0.20;
195
201 static constexpr double kDriveBridgeFloor = 0.3;
202
206 static constexpr double kMaxControlDrive = 30.62;
207
212 static constexpr double kMonotonicMargin = 0.98;
213
219 static constexpr double kAdaaEpsilon = 1e-5;
220
221 // ── Lifecycle ─────────────────────────────────────────────────────────
222
223 DiodeBridgeGainT() { configure_junction(); }
224
228 void prepare(double /*sample_rate*/) { reset(); }
229
230 void reset() { previous_shaped_ = 0.0; }
231
232 // ── Controls ──────────────────────────────────────────────────────────
233
236 void set_character(double character01) {
237 if (!std::isfinite(character01)) return;
238 const double c = std::clamp(character01, 0.0, 1.0);
239 drive_ = kDriveBridgeMax * (kDriveBridgeFloor + (1.0 - kDriveBridgeFloor) * c);
240 }
241
244 void set_adaa(bool on) { adaa_ = on; }
245
247 double drive() const { return drive_; }
248
249 // ── The control law ───────────────────────────────────────────────────
250
254 static double gain_for_control_drive(double control_drive) {
255 return 1.0 / (1.0 + std::max(0.0, control_drive));
256 }
257
260 static double control_drive_for_gain_db(double gain_reduction_db) {
261 return units::db_to_linear(-std::min(0.0, gain_reduction_db)) - 1.0;
262 }
263
273 double control_drive_for_current(double amperes) const {
274 const double bias = network_.knee_voltage(std::max(amperes, 0.0));
275 return kSeriesResistance * network_.conductance(bias) - series_offset_;
276 }
277
280 double dynamic_resistance(double amperes) const {
281 return 1.0 / network_.conductance(network_.knee_voltage(std::max(amperes, 0.0)));
282 }
283
284 // ── The colour ────────────────────────────────────────────────────────
285
287 static double curvature(double control_drive) {
288 return kBaseCurvature * (1.0 + kDriveCurvature * std::max(0.0, control_drive));
289 }
290
293 static double shape(double s, double beta) { return s - beta * s * s * s / 3.0; }
294
298 static double shape_antiderivative(double s, double beta) {
299 const double s2 = s * s;
300 return 0.5 * s2 - beta * s2 * s2 / 12.0;
301 }
302
304 static double max_operating_amplitude(double beta) {
305 return beta > 0.0 ? kMonotonicMargin / std::sqrt(beta) : 1e30;
306 }
307
321 static double third_harmonic_ratio(double s, double beta) {
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;
325 }
326
327 // ── Processing ────────────────────────────────────────────────────────
328
337 SampleType process(SampleType input, double control_drive) {
338 if (!std::isfinite(static_cast<double>(input)) || !std::isfinite(control_drive)) {
339 reset();
340 return SampleType{0};
341 }
342 const double x = std::max(0.0, control_drive);
343 const double beta = curvature(x);
344 const double limit = max_operating_amplitude(beta);
345 const double s = std::clamp(static_cast<double>(input) * drive_, -limit, limit);
346
347 double shaped;
348 if (adaa_) {
349 const double delta = s - previous_shaped_;
350 shaped = std::abs(delta) > kAdaaEpsilon
351 ? (shape_antiderivative(s, beta) -
352 shape_antiderivative(previous_shaped_, beta)) /
353 delta
354 : shape(0.5 * (s + previous_shaped_), beta);
355 } else {
356 shaped = shape(s, beta);
357 }
358 previous_shaped_ = s;
359
360 // Divide the drive back out: the shaper contributes harmonics, never
361 // level (series law 1). See the class doc block.
362 const double output = snap_to_zero(gain_for_control_drive(x) * shaped / drive_);
363 if (!std::isfinite(output)) {
364 reset();
365 return SampleType{0};
366 }
367 return static_cast<SampleType>(output);
368 }
369
370private:
371 void configure_junction() {
373 network_.ideality = kIdeality;
374 // The audio sees the balanced bridge as an antiparallel MATCHED pair —
375 // symmetric, hence the odd-only curvature above. One diode per leg.
376 network_.leg_a = 1.0;
377 network_.leg_b = 1.0;
378 // `conductance` at zero bias is not zero (it is `2·Is/θ`), so the drive
379 // law is offset to make `x(0) = 0` exactly. Without this a zero control
380 // current would ask for a gain of 1/(1 + 1.5e-10) rather than unity —
381 // inaudible, but it would make "no current means no reduction" an
382 // approximation instead of an identity.
383 series_offset_ = kSeriesResistance * network_.conductance(0.0);
384 }
385
386 junction::JunctionPair network_{};
387 double series_offset_ = 0.0;
388 double drive_ = kDriveBridgeMax * (kDriveBridgeFloor + (1.0 - kDriveBridgeFloor) * 0.35);
389 double previous_shaped_ = 0.0;
390 bool adaa_ = true;
391};
392
395
396// ── The transformer bracket ───────────────────────────────────────────────
397
437template <typename SampleType = float>
439public:
443 static constexpr double kLowCornerHz = 20.0;
444
447 static constexpr double kHighCornerHz = 28000.0;
448
453 static constexpr double kSaturationDepth = 0.15;
454
459 static constexpr double kEvenAsymmetry = 0.5;
460
466 static constexpr double kMonotonicMargin = 0.98;
467
470 static constexpr double kAdaaEpsilon = 1e-5;
471
476 static constexpr double kPeakGain = 1.0;
477
478 void prepare(double sample_rate) {
479 highpass_.prepare(static_cast<SampleType>(sample_rate));
480 lowpass_.prepare(static_cast<SampleType>(sample_rate));
481 highpass_.set_cutoff(static_cast<SampleType>(kLowCornerHz));
482 lowpass_.set_cutoff(static_cast<SampleType>(kHighCornerHz));
483 reset();
484 }
485
486 void reset() {
487 highpass_.reset();
488 lowpass_.reset();
489 previous_input_ = 0.0;
490 }
491
493 void set_character(double character01) {
494 if (!std::isfinite(character01)) return;
495 const double a = kSaturationDepth * std::clamp(character01, 0.0, 1.0);
496 positive_ = a * (1.0 + kEvenAsymmetry);
497 negative_ = a * (1.0 - kEvenAsymmetry);
498 symmetric_ = a;
499 asymmetric_ = a * kEvenAsymmetry;
500 const double steepest = std::max(positive_, negative_);
501 limit_ = steepest > 0.0 ? kMonotonicMargin / steepest : 1e30;
502 }
503
504 void set_adaa(bool on) { adaa_ = on; }
505
508 double saturate(double u) const {
509 return u - 0.5 * symmetric_ * u * std::abs(u) - 0.5 * asymmetric_ * u * u;
510 }
511
514 double saturate_antiderivative(double u) const {
515 const double u2 = u * u;
516 return 0.5 * u2 - symmetric_ * u2 * std::abs(u) / 6.0 - asymmetric_ * u2 * u / 6.0;
517 }
518
519 SampleType process(SampleType input) {
520 if (!std::isfinite(static_cast<double>(input))) {
521 reset();
522 return SampleType{0};
523 }
524 const double u = std::clamp(static_cast<double>(input), -limit_, limit_);
525
526 double shaped;
527 if (adaa_) {
528 const double delta = u - previous_input_;
529 shaped = std::abs(delta) > kAdaaEpsilon
530 ? (saturate_antiderivative(u) - saturate_antiderivative(previous_input_)) /
531 delta
532 : saturate(0.5 * (u + previous_input_));
533 } else {
534 shaped = saturate(u);
535 }
536 previous_input_ = u;
537
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))) {
541 reset();
542 return SampleType{0};
543 }
544 return output;
545 }
546
547private:
548 TptFilterT<SampleType> highpass_{};
549 TptFilterT<SampleType> lowpass_{};
550 double symmetric_ = kSaturationDepth * 0.35;
551 double asymmetric_ = kSaturationDepth * 0.35 * kEvenAsymmetry;
552 double positive_ = kSaturationDepth * 0.35 * (1.0 + kEvenAsymmetry);
553 double negative_ = kSaturationDepth * 0.35 * (1.0 - kEvenAsymmetry);
554 double limit_ = kMonotonicMargin / (kSaturationDepth * 0.35 * (1.0 + kEvenAsymmetry));
555 double previous_input_ = 0.0;
556 bool adaa_ = true;
557};
558
561
562// ── The complete device ───────────────────────────────────────────────────
563
633template <typename SampleType = float>
635public:
636 // ── Control ranges (mirrored by the catalog node's table) ─────────────
637 static constexpr double kThresholdDbMin = -40.0;
638 static constexpr double kThresholdDbMax = 6.0;
639 static constexpr double kRatioMin = 1.5;
640 static constexpr double kRatioMax = 20.0;
641 static constexpr double kAttackMsMin = 0.5;
642 static constexpr double kAttackMsMax = 100.0;
643 static constexpr double kReleaseMsMin = 50.0;
644 static constexpr double kReleaseMsMax = 2000.0;
645 static constexpr double kKneeDbMin = 0.0;
646 static constexpr double kKneeDbMax = 18.0;
647 static constexpr double kMakeupDbMax = 24.0;
648 static constexpr double kScHpfHzMin = 20.0;
649 static constexpr double kScHpfHzMax = 400.0;
650
651 // ── Design parameters ─────────────────────────────────────────────────
652
656 static constexpr double kLimitRatio = 20.0;
657
662 static constexpr double kCtrlSmoothMs = 1.0;
663
666 static constexpr double kAutoSlowFactor = 6.0;
667
671 static constexpr double kSustainTauMs = 200.0;
672
675 static constexpr double kSustainThresholdDb = 3.0;
676
680 static constexpr double kEnvelopeEpsilon = 1e-9;
681
682 // ── Lifecycle ─────────────────────────────────────────────────────────
683
687 void prepare(double sample_rate) {
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_));
696 fast_follower_.set_mode(BallisticsFilterT<SampleType>::Mode::peak);
697 slow_follower_.set_mode(BallisticsFilterT<SampleType>::Mode::peak);
698 update_ballistics();
699 update_smoothing();
700 reset();
701 }
702
703 void reset() {
704 input_bracket_.reset();
705 output_bracket_.reset();
706 bridge_.reset();
707 sidechain_dc_.reset();
708 sidechain_hpf_.reset();
709 fast_follower_.reset();
710 slow_follower_.reset();
711 smoothed_reduction_db_ = 0.0;
712 sustain_ = 0.0;
713 feedback_tap_ = 0.0;
714 }
715
716 // ── Controls (real units throughout) ──────────────────────────────────
717
718 void set_threshold_db(double db) {
719 threshold_db_ = std::clamp(dynamics::retain_finite(db, threshold_db_),
721 }
722
723 void set_ratio(double r) {
724 ratio_ = std::clamp(dynamics::retain_finite(r, ratio_), kRatioMin, kRatioMax);
725 }
726
727 void set_knee_db(double w) {
728 knee_db_ = std::clamp(dynamics::retain_finite(w, knee_db_),
730 }
731
732 void set_attack_ms(double ms) {
733 attack_ms_ = std::clamp(dynamics::retain_finite(ms, attack_ms_),
735 update_ballistics();
736 }
737
738 void set_release_ms(double ms) {
739 release_ms_ = std::clamp(dynamics::retain_finite(ms, release_ms_),
741 update_ballistics();
742 }
743
744 void set_makeup_db(double db) {
745 makeup_db_ = std::clamp(dynamics::retain_finite(db, makeup_db_),
746 0.0, kMakeupDbMax);
747 makeup_linear_ = units::db_to_linear(makeup_db_);
748 }
749
753 void set_character(double character01) {
754 character_ = std::clamp(dynamics::retain_finite(character01, character_),
755 0.0, 1.0);
756 bridge_.set_character(character_);
757 input_bracket_.set_character(character_);
758 output_bracket_.set_character(character_);
759 }
760
762 void set_mix_percent(double percent) {
763 const double current_percent = mix_ * 100.0;
764 mix_ = std::clamp(dynamics::retain_finite(percent, current_percent),
765 0.0, 100.0) / 100.0;
766 }
767
768 void set_sc_hpf_hz(double hz) {
769 sc_hpf_hz_ = std::clamp(dynamics::retain_finite(hz, sc_hpf_hz_),
771 sidechain_hpf_.set_cutoff(static_cast<SampleType>(sc_hpf_hz_));
772 }
773
775 void set_auto_release(bool on) { auto_release_ = on; }
776
778 void set_feedback(bool on) { feedback_ = on; }
779
780 void set_adaa(bool on) {
781 bridge_.set_adaa(on);
782 input_bracket_.set_adaa(on);
783 output_bracket_.set_adaa(on);
784 }
785
786 // ── Queries ───────────────────────────────────────────────────────────
787
791 int latency_samples() const noexcept { return 0; }
792
794 double gain_reduction_db() const { return smoothed_reduction_db_; }
795 GainReduction gain_reduction() const noexcept {
796 return GainReduction::from_signed_db(smoothed_reduction_db_);
797 }
798
800 double control_drive() const {
801 return DiodeBridgeGainT<SampleType>::control_drive_for_gain_db(smoothed_reduction_db_);
802 }
803
810 double static_curve_db(double input_db) const {
811 const double slope = ratio_ >= kLimitRatio ? 0.0 : 1.0 / ratio_;
812 return dynamics::soft_knee_reduction_db(input_db - threshold_db_, knee_db_, slope);
813 }
814
827 double static_curve_feedback_db(double input_db) const {
828 double gr = 0.0;
829 for (int i = 0; i < 96; ++i) {
830 const double next = 0.5 * (gr + static_curve_db(input_db + gr));
831 if (next == gr) break;
832 gr = next;
833 }
834 return gr;
835 }
836
843
844 // ── Processing ────────────────────────────────────────────────────────
845
846 SampleType process(SampleType input) {
847 // Every stage below owns recursive state, including the feedback tap.
848 // A non-finite input is therefore a recovery boundary: do not let it
849 // enter the loop, and make the next finite sample start validly.
850 if (!std::isfinite(static_cast<double>(input))) {
851 reset();
852 return SampleType{0};
853 }
854 const double dry = static_cast<double>(input);
855
856 // Detection. The feedback tap is last sample's pre-makeup output: the
857 // loop's only delay, and it lives in the CONTROL path, not the audio.
858 const double detector_input = feedback_ ? feedback_tap_ : dry;
859 const double envelope = detect(detector_input);
860 const double envelope_db =
861 units::linear_to_db(std::abs(envelope) + kEnvelopeEpsilon);
862
863 const double target_db = static_curve_db(envelope_db);
864 smoothed_reduction_db_ = snap_to_zero(smoothed_reduction_db_ +
865 smooth_coef_ * (target_db - smoothed_reduction_db_));
866
867 // Gain and drive are derived from ONE smoothed quantity, so they cannot
868 // disagree about how much reduction is being applied.
869 const double gain = units::db_to_linear(smoothed_reduction_db_);
870 const double drive = 1.0 / gain - 1.0;
871
872 const SampleType bracketed = input_bracket_.process(input);
873 const SampleType attenuated = bridge_.process(bracketed, drive);
874 const SampleType wet = output_bracket_.process(attenuated);
875
876 feedback_tap_ = static_cast<double>(wet);
877 return static_cast<SampleType>(dry * (1.0 - mix_) +
878 static_cast<double>(wet) * makeup_linear_ * mix_);
879 }
880
881 void process_block(SampleType* io, int n) {
882 for (int i = 0; i < n; ++i) io[i] = process(io[i]);
883 }
884
885private:
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);
896
897 const double fast = static_cast<double>(fast_follower_.process(filtered));
898 // Keep the slow branch synchronized even while its readout is disabled.
899 // Otherwise enabling Auto mid-release blends `fast` against a stale
900 // zero state and briefly recovers gain faster than the manual follower.
901 const double slow = static_cast<double>(slow_follower_.process(filtered));
902 if (!auto_release_) return fast;
903
904 // Program-dependent dual slope. The spec says `max(fast, slow)`, which
905 // is identically the SLOW follower — during release both decay from the
906 // same value and the slow one is always the larger, so the maximum
907 // never selects the fast branch and the mode degenerates into a single
908 // long release with nothing program-dependent about it. Blending on a
909 // sustain indicator is what makes it dual-slope: fast recovery right
910 // after a transient, slow hold once compression has been sustained.
911 // Equals `max(fast, slow)` in the sustained limit.
912 const double sustained = -smoothed_reduction_db_ > kSustainThresholdDb ? 1.0 : 0.0;
913 sustain_ = snap_to_zero(sustain_ + sustain_coef_ * (sustained - sustain_));
914 return fast + sustain_ * (slow - fast);
915 }
916
917 void update_ballistics() {
918 const auto attack = static_cast<SampleType>(attack_ms_);
919 fast_follower_.set_attack_ms(attack);
920 slow_follower_.set_attack_ms(attack);
921 fast_follower_.set_release_ms(static_cast<SampleType>(release_ms_));
922 slow_follower_.set_release_ms(static_cast<SampleType>(release_ms_ * kAutoSlowFactor));
923 }
924
925 void update_smoothing() {
926 smooth_coef_ = units::ms_to_onepole_coef(kCtrlSmoothMs, sample_rate_);
927 sustain_coef_ = units::ms_to_onepole_coef(kSustainTauMs, sample_rate_);
928 }
929
930 double sample_rate_ = 48000.0;
931
932 double threshold_db_ = -12.0;
933 double ratio_ = 4.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;
939 double mix_ = 1.0;
940 double sc_hpf_hz_ = 100.0;
941 bool auto_release_ = false;
942 bool feedback_ = true;
943
944 double makeup_linear_ = 1.0;
945 double smooth_coef_ = 1.0;
946 double sustain_coef_ = 1.0;
947
948 double smoothed_reduction_db_ = 0.0;
949 double sustain_ = 0.0;
950 double feedback_tap_ = 0.0;
951
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_{};
959};
960
963
964} // namespace pulp::signal
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
One input or output transformer: band-limit, mild saturation, DC-blocked.
Definition diode_bridge_compressor.hpp:438
static constexpr double kLowCornerHz
Definition diode_bridge_compressor.hpp:443
static constexpr double kHighCornerHz
Definition diode_bridge_compressor.hpp:447
void reset()
Definition diode_bridge_compressor.hpp:486
static constexpr double kAdaaEpsilon
Definition diode_bridge_compressor.hpp:470
double saturate_antiderivative(double u) const
∫sat du.
Definition diode_bridge_compressor.hpp:514
void set_adaa(bool on)
Definition diode_bridge_compressor.hpp:504
SampleType process(SampleType input)
Definition diode_bridge_compressor.hpp:519
void set_character(double character01)
0..1 colour depth, shared with the bridge.
Definition diode_bridge_compressor.hpp:493
static constexpr double kEvenAsymmetry
Definition diode_bridge_compressor.hpp:459
static constexpr double kSaturationDepth
Definition diode_bridge_compressor.hpp:453
static constexpr double kMonotonicMargin
Definition diode_bridge_compressor.hpp:466
void prepare(double sample_rate)
Definition diode_bridge_compressor.hpp:478
double saturate(double u) const
u − (a/2)·u·|u| − (a·ε/2)·u².
Definition diode_bridge_compressor.hpp:508
static constexpr double kPeakGain
Definition diode_bridge_compressor.hpp:476
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.