Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
fet_compressor.hpp
Go to the documentation of this file.
1#pragma once
2
230
231#include <pulp/signal/biquad.hpp>
235#include <pulp/signal/units.hpp>
237
238#include <algorithm>
239#include <array>
240#include <cmath>
241#include <cstddef>
242#include <cstdint>
243
244namespace pulp::signal {
245
250enum class FetRatio : std::uint8_t {
256};
257
259template <typename SampleType = float>
261public:
263
264 // ── Resampler geometry: the latency contract's inputs ──────────────────
265
269 static constexpr int kOversamplingFactor = 4;
270
275 static constexpr int kResamplerTaps = 65;
276
279 static constexpr double kResamplerKaiserBeta = 8.0;
280
284 static constexpr double kResamplerCutoffFraction = 0.96;
285
288 static constexpr int kLatencySamples =
289 2 * ((kResamplerTaps - 1) / 2) / kOversamplingFactor;
290
291 // ── Design parameters: the divider ────────────────────────────────────
292
295 static constexpr double kFetOnResistance = 120.0;
296
299 static constexpr double kFetOffResistance = 1.0e6;
300
304 static constexpr double kGainReductionMaxDb = 40.0;
305
306 // ── Design parameters: the coloration term ────────────────────────────
307
314 static constexpr double kColorationAlphaMax = 0.08;
315
318 static constexpr double kColorationAlphaCeiling = 0.16;
319
323 static constexpr double kColorationHeadroomClamp = 1.2;
324
325 // ── Design parameters: all-buttons-in ─────────────────────────────────
326 //
327 // **Honest gap, stated plainly.** Moore (2012) documents the QUALITATIVE
328 // phenomenon — heavier, more distorted, less predictable compression — from
329 // listening and usage analysis, not circuit-level component values. No
330 // citable source gives numbers for any of the five constants below. They
331 // are original engineering choices; re-tuning by ear must stay inside the
332 // stated ranges and must preserve the DIRECTION of each deviation.
333
335 static constexpr double kAbiRatio = 20.0;
336
339 static constexpr double kAbiKneeWidenMult = 2.5;
340
346 static constexpr double kAbiBiasShiftDb = 1.5;
347
350 static constexpr double kAbiAttackScale = 0.6;
351
353 static constexpr double kAbiReleaseScale = 0.75;
354
357 static constexpr double kAbiAlphaExtraMult = 1.4;
358
359 // ── Design parameters: the transformer tilt ───────────────────────────
360
365 static constexpr double kTransformerCornerHz = 60.0;
366
369 static constexpr double kTransformerTiltDbMax = 3.0;
370
374 static constexpr double kTransformerQ = 0.70710678118654752;
375
376 // ── Design parameters: everything else ────────────────────────────────
377
381 static constexpr double kThresholdDbfs = -18.0;
382
386 static constexpr double kLevelEpsilon = dynamics::kLevelEpsilon;
387
388 // Control ranges. These mirror the catalog node's parameter table; the
389 // table is the canonical declaration and these are the same numbers at the
390 // call site, not a second independent one.
391 static constexpr double kInputGainDbMin = -20.0;
392 static constexpr double kInputGainDbMax = 40.0;
393 static constexpr double kOutputGainDbMin = -20.0;
394 static constexpr double kOutputGainDbMax = 20.0;
395 static constexpr double kAttackUsMin = 20.0;
396 static constexpr double kAttackUsMax = 800.0;
397 static constexpr double kReleaseMsMin = 50.0;
398 static constexpr double kReleaseMsMax = 1100.0;
399 static constexpr double kKneeDbMin = 0.0;
400 static constexpr double kKneeDbMax = 6.0;
401
402 // ── Lifecycle ─────────────────────────────────────────────────────────
403
404 FetCompressorT() { prepare(44100.0); }
405
410 void prepare(double sample_rate) {
411 sample_rate_ = sample_rate > 0.0 ? sample_rate : sample_rate_;
412 oversampled_rate_ = sample_rate_ * kOversamplingFactor;
413
414 design_resampler();
415 solve_divider();
416 update_ratio_dependents();
417 update_coefficients();
418 update_transformer();
419 reset();
420 }
421
424 void reset() {
425 base_history_.fill(0.0);
426 os_history_.fill(0.0);
427 dry_delay_.fill(0.0);
428 base_write_ = 0;
429 os_write_ = 0;
430 dry_write_ = 0;
431 detector_db_ = 0.0;
432 control_ = 0.0;
433 feedback_ = 0.0;
434 transformer_.reset();
435 }
436
437 // ── Controls (real units throughout) ──────────────────────────────────
438 //
439 // Taken as `double` rather than `SampleType`. A `double` parameter accepts
440 // everything a `SampleType` one would and rounds nothing on the way in, and
441 // it matches `feedforward_compressor.hpp` so the two members of the family
442 // present the same call shape.
443
445 void set_input_gain_db(double db) {
446 input_gain_db_ = std::clamp(dynamics::retain_finite(db, input_gain_db_),
448 input_gain_ = units::db_to_linear(input_gain_db_);
449 }
450
452 void set_output_gain_db(double db) {
453 output_gain_db_ = std::clamp(dynamics::retain_finite(db, output_gain_db_),
455 output_gain_ = units::db_to_linear(output_gain_db_);
456 }
457
458 void set_ratio(Ratio r) {
459 ratio_button_ = r;
460 update_ratio_dependents();
461 update_coefficients();
462 }
463
464 Ratio ratio() const { return ratio_button_; }
465
466 void set_attack_us(double us) {
467 attack_us_ = std::clamp(dynamics::retain_finite(us, attack_us_),
469 update_coefficients();
470 }
471
472 void set_release_ms(double ms) {
473 release_ms_ = std::clamp(dynamics::retain_finite(ms, release_ms_),
475 update_coefficients();
476 }
477
480 void set_knee_db(double db) {
481 knee_request_db_ = std::clamp(dynamics::retain_finite(db, knee_request_db_),
483 update_ratio_dependents();
484 }
485
487 void set_transformer_amount(double pct01) {
488 transformer_amount_ = std::clamp(
489 dynamics::retain_finite(pct01, transformer_amount_), 0.0, 1.0);
490 update_transformer();
491 }
492
495 void set_mix(double pct01) {
496 mix_ = std::clamp(dynamics::retain_finite(pct01, mix_), 0.0, 1.0);
497 }
498
499 // ── Reported quantities ───────────────────────────────────────────────
500
502 int latency_samples() const noexcept { return kLatencySamples; }
503
504 double sample_rate() const noexcept { return sample_rate_; }
505 double oversampled_rate() const noexcept { return oversampled_rate_; }
506
511 double static_curve_db(double level_db) const {
512 return dynamics::soft_knee_output_db(level_db, kThresholdDbfs, knee_db_, ratio_value_);
513 }
514
516 double gain_computer_db(double level_db) const {
517 return level_db - static_curve_db(level_db);
518 }
519
523 double measured_static_curve_db(double input_db) const {
524 const double m = loop_slope();
525 const double w = knee_db_;
526 const double over = input_db - bias_shift_db_ - kThresholdDbfs;
527 if (2.0 * over < -w) return kThresholdDbfs + over;
528 if (w > 0.0 && m > 0.0 && 2.0 * over <= w * (1.0 + m)) {
529 // Quadratic branch: (m/2W)·s² + s − (over + W/2) = 0, s = u + W/2.
530 const double s =
531 (w / m) * (std::sqrt(1.0 + (2.0 * m / w) * (over + 0.5 * w)) - 1.0);
532 return kThresholdDbfs + s - 0.5 * w;
533 }
534 return kThresholdDbfs + over / (1.0 + m);
535 }
536
538 double measured_gain_reduction_db(double input_db) const {
539 return input_db - measured_static_curve_db(input_db);
540 }
541
544 double loop_slope() const { return 1.0 - 1.0 / ratio_value_; }
545
547 double measured_ratio() const { return 1.0 + loop_slope(); }
548
551 double measured_knee_db() const { return knee_db_ * (2.0 + loop_slope()) * 0.5; }
552
554 double nominal_ratio() const { return ratio_value_; }
555
557 double effective_knee_db() const { return knee_db_; }
558
560 double bias_shift_db() const { return bias_shift_db_; }
561
564 double coloration_depth() const { return alpha_max_; }
565
569 double attack_coefficient() const { return attack_coef_; }
570 double release_coefficient() const { return release_coef_; }
571
576 double gain_reduction_db() const { return detector_db_; }
577 GainReduction gain_reduction() const noexcept {
578 return GainReduction::from_magnitude_db(detector_db_);
579 }
580
582 double control_voltage() const { return control_; }
583
584 // ── The divider law ───────────────────────────────────────────────────
585
588 double divider_conductance(double c) const {
589 return g_min_ + std::clamp(c, 0.0, 1.0) * (g_max_ - g_min_);
590 }
591
597 double divider_small_signal_gain(double c) const {
598 return trim_ / (1.0 + series_resistance_ * divider_conductance(c));
599 }
600
604 double divider_gain(double c, double v) const {
605 return trim_ / (1.0 + series_resistance_ *
607 }
608
611 double coloration_multiplier(double c, double v) const {
612 const double clamped = std::clamp(v, -kColorationHeadroomClamp, kColorationHeadroomClamp);
613 return 1.0 + alpha_max_ * std::clamp(c, 0.0, 1.0) * clamped;
614 }
615
620 return 1.0 + alpha_max_ * kColorationHeadroomClamp;
621 }
622
627 double control_for_reduction_db(double db) const {
628 if (!(db > 0.0)) return 0.0;
629 const double rest = 1.0 + series_resistance_ * g_min_;
630 const double target = rest * std::pow(10.0, db / 20.0);
631 const double c = (target - rest) / (series_resistance_ * (g_max_ - g_min_));
632 return std::clamp(c, 0.0, 1.0);
633 }
634
640 static constexpr double kDividerGainSupremum = 1.0;
641
644 return (g_max_ - g_min_) * (1.0 - a) > a * g_min_;
645 }
646
651 double resampler_peak_gain_bound() const { return resampler_peak_bound_; }
652
656 double worst_case_gain() const {
657 const double wet = input_gain_ * kDividerGainSupremum * resampler_peak_bound_ *
658 output_gain_ * kTransformerGainSupremum;
659 return (1.0 - mix_) + mix_ * wet;
660 }
661
665 static constexpr double kTransformerGainSupremum = 1.0;
666
667 // ── Processing ────────────────────────────────────────────────────────
668
671 SampleType process(SampleType input) {
672 // The feedback tap, both resampler histories, the dry delay, and the
673 // transformer are recursive. Reject a non-finite sample before any of
674 // them sees it, then restart from the class's documented fresh state.
675 if (!std::isfinite(static_cast<double>(input))) {
676 reset();
677 return SampleType{0};
678 }
679 const double x = static_cast<double>(input);
680 const double dry = push_dry(x);
681 const double driven = x * input_gain_;
682
683 // Interpolate: one base sample in, `kOversamplingFactor` out.
684 base_history_[base_write_] = driven;
685
686 double decimated = 0.0;
687 for (int phase = 0; phase < kOversamplingFactor; ++phase) {
688 const double v = interpolate_phase(phase);
689
690 // ── the loop ──────────────────────────────────────────────────
691 // The detector reads the PREVIOUS oversampled divider output. That
692 // one line is the entire topological difference from the
693 // feedforward design.
694 const double level_db = dynamics::amplitude_db(feedback_, kLevelEpsilon);
695 const double requested = gain_computer_db(level_db) + bias_shift_db_;
696 detector_db_ = snap_to_zero(
697 requested > detector_db_
698 ? attack_coef_ * detector_db_ + (1.0 - attack_coef_) * requested
699 : release_coef_ * detector_db_ + (1.0 - release_coef_) * requested);
700 control_ = control_for_reduction_db(detector_db_);
701
702 const double y = v * divider_gain(control_, v);
703 feedback_ = y;
704
705 os_history_[os_write_] = y;
706 // The first oversample is the decimator's output grid point; taking
707 // it here keeps the composite delay an exact integer.
708 if (phase == 0) decimated = decimate();
709 os_write_ = os_write_ + 1 < kResamplerTaps ? os_write_ + 1 : 0;
710 }
711 base_write_ = base_write_ + 1 < kBaseHistory ? base_write_ + 1 : 0;
712
713 const double wet = transformer_.process(decimated * output_gain_);
714 return static_cast<SampleType>(snap_to_zero(dry + mix_ * (wet - dry)));
715 }
716
717 void process_block(SampleType* io, int n) {
718 for (int i = 0; i < n; ++i) io[i] = process(io[i]);
719 }
720
721private:
722 static constexpr int kBaseHistory =
724
725 // ── Resampler ─────────────────────────────────────────────────────────
726
727 void design_resampler() {
728 // Cutoff expressed in cycles per OVERSAMPLED sample: base Nyquist is
729 // `0.5/kOversamplingFactor` there.
730 const double cutoff =
731 kResamplerCutoffFraction * 0.5 / static_cast<double>(kOversamplingFactor);
732 const auto prototype = design_windowed_sinc(static_cast<std::size_t>(kResamplerTaps),
733 cutoff, kResamplerKaiserBeta);
734 double decimation_l1 = 0.0;
735 for (int i = 0; i < kResamplerTaps; ++i) {
736 decimation_[static_cast<std::size_t>(i)] = prototype[static_cast<std::size_t>(i)];
737 decimation_l1 += std::abs(prototype[static_cast<std::size_t>(i)]);
738 }
739
740 // Polyphase decomposition of the interpolator. Zero-stuffing by L drops
741 // the DC gain to 1/L, so each branch carries the compensating factor L.
742 double interpolation_l1 = 0.0;
743 for (int phase = 0; phase < kOversamplingFactor; ++phase) {
744 int count = 0;
745 double branch_l1 = 0.0;
746 for (int tap = phase; tap < kResamplerTaps; tap += kOversamplingFactor) {
747 const double coefficient =
748 kOversamplingFactor * prototype[static_cast<std::size_t>(tap)];
749 interpolation_[static_cast<std::size_t>(phase)][static_cast<std::size_t>(count)] =
750 coefficient;
751 branch_l1 += std::abs(coefficient);
752 ++count;
753 }
754 interpolation_length_[static_cast<std::size_t>(phase)] = count;
755 interpolation_l1 = std::max(interpolation_l1, branch_l1);
756 }
757 resampler_peak_bound_ = interpolation_l1 * decimation_l1;
758 }
759
761 double interpolate_phase(int phase) const {
762 const auto p = static_cast<std::size_t>(phase);
763 const int count = interpolation_length_[p];
764 double sum = 0.0;
765 int index = base_write_;
766 for (int k = 0; k < count; ++k) {
767 sum += interpolation_[p][static_cast<std::size_t>(k)] *
768 base_history_[static_cast<std::size_t>(index)];
769 if (--index < 0) index = kBaseHistory - 1;
770 }
771 return sum;
772 }
773
775 double decimate() const {
776 double sum = 0.0;
777 int index = os_write_;
778 for (int k = 0; k < kResamplerTaps; ++k) {
779 sum += decimation_[static_cast<std::size_t>(k)] *
780 os_history_[static_cast<std::size_t>(index)];
781 if (--index < 0) index = kResamplerTaps - 1;
782 }
783 return sum;
784 }
785
788 double push_dry(double x) {
789 constexpr std::size_t size = kLatencySamples + 1;
790 const std::size_t read = (dry_write_ + size - kLatencySamples) % size;
791 const double out = dry_delay_[read];
792 dry_delay_[dry_write_] = x;
793 dry_write_ = (dry_write_ + 1) % size;
794 return out;
795 }
796
797 // ── Derived quantities ────────────────────────────────────────────────
798
803 void solve_divider() {
804 g_min_ = 1.0 / kFetOffResistance;
805 g_max_ = 1.0 / kFetOnResistance;
806 const double k = std::pow(10.0, -kGainReductionMaxDb / 20.0);
807 series_resistance_ = kFetOffResistance * kFetOnResistance * (k - 1.0) /
809 trim_ = 1.0 + series_resistance_ * g_min_;
810 }
811
812 void update_ratio_dependents() {
813 const bool abi = ratio_button_ == Ratio::all_buttons_in;
814 switch (ratio_button_) {
815 case Ratio::r4_1: ratio_value_ = 4.0; break;
816 case Ratio::r8_1: ratio_value_ = 8.0; break;
817 case Ratio::r12_1: ratio_value_ = 12.0; break;
818 case Ratio::r20_1: ratio_value_ = 20.0; break;
819 case Ratio::all_buttons_in: ratio_value_ = kAbiRatio; break;
820 }
821 knee_db_ = abi ? knee_request_db_ * kAbiKneeWidenMult : knee_request_db_;
822 bias_shift_db_ = abi ? kAbiBiasShiftDb : 0.0;
823 alpha_max_ = abi ? std::min(kColorationAlphaMax * kAbiAlphaExtraMult,
826 }
827
828 void update_coefficients() {
829 const bool abi = ratio_button_ == Ratio::all_buttons_in;
830 const double attack_s =
831 attack_us_ * 1e-6 * (abi ? kAbiAttackScale : 1.0);
832 const double release_s =
833 release_ms_ * 1e-3 * (abi ? kAbiReleaseScale : 1.0);
834 attack_coef_ = retain(attack_s);
835 release_coef_ = retain(release_s);
836 }
837
840 double retain(double tau_seconds) const {
841 return dynamics::one_pole_retain(tau_seconds, oversampled_rate_);
842 }
843
844 void update_transformer() {
845 transformer_.set_coefficients(
847 kTransformerQ, sample_rate_, -transformer_amount_ * kTransformerTiltDbMax);
848 }
849
850 // ── State ─────────────────────────────────────────────────────────────
851
852 double sample_rate_ = 44100.0;
853 double oversampled_rate_ = 44100.0 * kOversamplingFactor;
854
855 // Controls.
856 double input_gain_db_ = 0.0;
857 double output_gain_db_ = 0.0;
858 double attack_us_ = 200.0;
859 double release_ms_ = 300.0;
860 double knee_request_db_ = 1.0;
861 double transformer_amount_ = 0.6;
862 double mix_ = 1.0;
863 Ratio ratio_button_ = Ratio::r4_1;
864
865 // Derived from controls.
866 double input_gain_ = 1.0;
867 double output_gain_ = 1.0;
868 double ratio_value_ = 4.0;
869 double knee_db_ = 1.0;
870 double bias_shift_db_ = 0.0;
871 double alpha_max_ = kColorationAlphaMax;
872 double attack_coef_ = 0.0;
873 double release_coef_ = 0.0;
874
875 // Derived from the divider's design parameters.
876 double g_min_ = 0.0;
877 double g_max_ = 0.0;
878 double series_resistance_ = 0.0;
879 double trim_ = 1.0;
880
881 // Resampler.
882 std::array<double, kResamplerTaps> decimation_{};
883 std::array<std::array<double, kBaseHistory>, kOversamplingFactor> interpolation_{};
884 std::array<int, kOversamplingFactor> interpolation_length_{};
885 double resampler_peak_bound_ = 1.0;
886
887 // Running state.
888 std::array<double, kBaseHistory> base_history_{};
889 std::array<double, kResamplerTaps> os_history_{};
890 std::array<double, kLatencySamples + 1> dry_delay_{};
891 int base_write_ = 0;
892 int os_write_ = 0;
893 std::size_t dry_write_ = 0;
894
895 double detector_db_ = 0.0;
896 double control_ = 0.0;
897 double feedback_ = 0.0;
898 BiquadT<double> transformer_{};
899};
900
903
904} // namespace pulp::signal
@ low_shelf
Definition biquad.hpp:58
Feedback-topology FET compressor.
Definition fet_compressor.hpp:260
static constexpr int kOversamplingFactor
Definition fet_compressor.hpp:269
static constexpr double kOutputGainDbMax
Definition fet_compressor.hpp:394
void set_ratio(Ratio r)
Definition fet_compressor.hpp:458
Ratio ratio() const
Definition fet_compressor.hpp:464
static constexpr double kAttackUsMax
Definition fet_compressor.hpp:396
static constexpr double kGainReductionMaxDb
Definition fet_compressor.hpp:304
static constexpr double kDividerGainSupremum
Definition fet_compressor.hpp:640
bool divider_supremum_is_provable() const
Definition fet_compressor.hpp:642
void reset()
Zeroes every state buffer.
Definition fet_compressor.hpp:424
static constexpr double kColorationAlphaCeiling
Definition fet_compressor.hpp:318
double divider_conductance(double c) const
Shunt conductance at control voltage c — the quantity that is linear in control voltage,...
Definition fet_compressor.hpp:588
static constexpr double kResamplerKaiserBeta
Definition fet_compressor.hpp:279
static constexpr double kAbiAttackScale
Definition fet_compressor.hpp:350
static constexpr double kAttackUsMin
Definition fet_compressor.hpp:395
double sample_rate() const noexcept
Definition fet_compressor.hpp:504
double loop_slope() const
The static curve's slope in the detector's domain, B = 1 − 1/R — the loop's only remaining gain term ...
Definition fet_compressor.hpp:544
double measured_ratio() const
The measured input/output ratio: 1 + B = 2 − 1/R, never R.
Definition fet_compressor.hpp:547
static constexpr double kThresholdDbfs
Definition fet_compressor.hpp:381
static constexpr int kResamplerTaps
Definition fet_compressor.hpp:275
void process_block(SampleType *io, int n)
Definition fet_compressor.hpp:717
SampleType process(SampleType input)
One mono sample.
Definition fet_compressor.hpp:671
double control_for_reduction_db(double db) const
The exact inverse of divider_small_signal_gain in the dB domain: the control voltage that makes the d...
Definition fet_compressor.hpp:627
double coloration_depth() const
Coloration depth ceiling in force: α_max, or α_max·kAbiAlphaExtraMult clamped to kColorationAlphaCeil...
Definition fet_compressor.hpp:564
void set_release_ms(double ms)
Definition fet_compressor.hpp:472
static constexpr double kTransformerTiltDbMax
Definition fet_compressor.hpp:369
double divider_gain(double c, double v) const
Instantaneous divider gain, including the coloration term.
Definition fet_compressor.hpp:604
void set_mix(double pct01)
0..1 parallel blend.
Definition fet_compressor.hpp:495
void set_transformer_amount(double pct01)
0..1 depth of the output transformer's low-frequency tilt.
Definition fet_compressor.hpp:487
static constexpr double kAbiRatio
Definition fet_compressor.hpp:335
void set_knee_db(double db)
Soft-knee width around the fixed reference, in the DETECTOR's domain.
Definition fet_compressor.hpp:480
static constexpr double kTransformerGainSupremum
Definition fet_compressor.hpp:665
static constexpr double kAbiAlphaExtraMult
Definition fet_compressor.hpp:357
static constexpr int kLatencySamples
Definition fet_compressor.hpp:288
double nominal_ratio() const
The nominal ratio feeding the gain computer (20 in all-buttons-in).
Definition fet_compressor.hpp:554
double measured_knee_db() const
The knee width a user measures at the INPUT, in dB.
Definition fet_compressor.hpp:551
double static_curve_db(double level_db) const
The static characteristic's output for a level in dB, per Giannoulis et al.
Definition fet_compressor.hpp:511
static constexpr double kTransformerCornerHz
Definition fet_compressor.hpp:365
static constexpr double kFetOffResistance
Definition fet_compressor.hpp:299
static constexpr double kKneeDbMax
Definition fet_compressor.hpp:400
double divider_small_signal_gain(double c) const
Small-signal divider gain at fixed c: R_fet/(R_s + R_fet) with the rest-state trim folded in,...
Definition fet_compressor.hpp:597
static constexpr double kAbiKneeWidenMult
Definition fet_compressor.hpp:339
FetRatio Ratio
Definition fet_compressor.hpp:262
double control_voltage() const
The control voltage currently driving the divider, 0..1.
Definition fet_compressor.hpp:582
static constexpr double kReleaseMsMin
Definition fet_compressor.hpp:397
int latency_samples() const noexcept
Exact, parameter independent. See the file doc block.
Definition fet_compressor.hpp:502
static constexpr double kLevelEpsilon
Definition fet_compressor.hpp:386
GainReduction gain_reduction() const noexcept
Definition fet_compressor.hpp:577
double gain_reduction_db() const
Smoothed gain reduction in dB (≥ 0, a magnitude) as of the last oversampled step — the detector itsel...
Definition fet_compressor.hpp:576
double coloration_multiplier(double c, double v) const
1 + α(c)·v, the conductance multiplier.
Definition fet_compressor.hpp:611
static constexpr double kResamplerCutoffFraction
Definition fet_compressor.hpp:284
static constexpr double kInputGainDbMax
Definition fet_compressor.hpp:392
double measured_gain_reduction_db(double input_db) const
Measured gain reduction in dB (≥ 0) at a steady input level.
Definition fet_compressor.hpp:538
FetCompressorT()
Definition fet_compressor.hpp:404
double attack_coefficient() const
Detector retain coefficients, exp(−1/(τ·fs_os)).
Definition fet_compressor.hpp:569
static constexpr double kColorationHeadroomClamp
Definition fet_compressor.hpp:323
void set_input_gain_db(double db)
The only lever into gain reduction — there is no threshold control.
Definition fet_compressor.hpp:445
void set_output_gain_db(double db)
Makeup gain, applied after the divider and before the transformer.
Definition fet_compressor.hpp:452
static constexpr double kAbiReleaseScale
Definition fet_compressor.hpp:353
double worst_case_gain() const
Closed-form ℓ∞ bound on |y/x| for the whole block at the current settings (series law 8).
Definition fet_compressor.hpp:656
double release_coefficient() const
Definition fet_compressor.hpp:570
double gain_computer_db(double level_db) const
Gain reduction the static curve asks for, as a POSITIVE magnitude in dB.
Definition fet_compressor.hpp:516
double effective_knee_db() const
Knee width in force, after the all-buttons-in widening.
Definition fet_compressor.hpp:557
void prepare(double sample_rate)
Designs the resampler prototype, solves the divider's series resistance, and configures the transform...
Definition fet_compressor.hpp:410
static constexpr double kInputGainDbMin
Definition fet_compressor.hpp:391
static constexpr double kKneeDbMin
Definition fet_compressor.hpp:399
double bias_shift_db() const
Detector bias shift in force (non-zero only in all-buttons-in).
Definition fet_compressor.hpp:560
double resampler_peak_gain_bound() const
‖h_up‖₁ · ‖h_down‖₁ for the shipped taps — a true ℓ∞ bound on the resampling pair's peak gain for ANY...
Definition fet_compressor.hpp:651
double coloration_multiplier_bound() const
The bound on |1 + α(c)·v|.
Definition fet_compressor.hpp:619
static constexpr double kOutputGainDbMin
Definition fet_compressor.hpp:393
static constexpr double kTransformerQ
Definition fet_compressor.hpp:374
static constexpr double kAbiBiasShiftDb
Definition fet_compressor.hpp:346
double oversampled_rate() const noexcept
Definition fet_compressor.hpp:505
static constexpr double kReleaseMsMax
Definition fet_compressor.hpp:398
double measured_static_curve_db(double input_db) const
The static curve a user actually measures, input dB → output dB: the closed-loop fixed point ŷ = x̂ −...
Definition fet_compressor.hpp:523
static constexpr double kColorationAlphaMax
Definition fet_compressor.hpp:314
void set_attack_us(double us)
Definition fet_compressor.hpp:466
static constexpr double kFetOnResistance
Definition fet_compressor.hpp:295
Canonical gain-reduction meter value.
Definition dynamics_contract.hpp:27
static GainReduction from_magnitude_db(double magnitude_db) noexcept
Adapt an attenuation magnitude, where reduction is positive dB.
Definition dynamics_contract.hpp:37
Shared envelope and gain-reduction vocabulary for dynamics processors.
The published compressor equations, once.
@ count
sentinel: number of real capabilities (keep LAST)
Definition canvas_capability.hpp:38
@ index
Definition gpu_pass.hpp:140
double soft_knee_output_db(double input_db, double threshold_db, double knee_db, double ratio)
The characteristic in the OUTPUT-LEVEL domain: input dB → output dB.
Definition dynamics_core.hpp:193
double one_pole_retain(double tau_seconds, double sample_rate)
α = exp(−1/(τ·fs)) — the RETAIN coefficient (see the file doc block).
Definition dynamics_core.hpp:155
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
double amplitude_db(double amplitude, double epsilon)
An amplitude → dB, floored by an ADDEND rather than a max().
Definition dynamics_core.hpp:135
constexpr double kLevelEpsilon
Level-conversion floor, guarding log10(0).
Definition dynamics_core.hpp:125
@ size
Definition config.hpp:216
std::uint64_t read(std::span< const std::uint8_t > b, std::size_t p, unsigned n) noexcept
Definition spectral_frame_codec.hpp:111
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
FetCompressorT< double > FetCompressor64
Definition fet_compressor.hpp:902
@ c
Definition spectrum_trace.hpp:35
@ rest
Low for the whole stage — silence with duration.
Definition stage_sequencer.hpp:29
T snap_to_zero(T x)
Returns 0 if |x| < snap_threshold, else x.
Definition denormal.hpp:45
@ a
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
FetCompressorT< float > FetCompressor
Definition fet_compressor.hpp:901
std::vector< double > design_windowed_sinc(std::size_t taps, double cutoff, double beta)
Definition windowed_sinc_design.hpp:58
FetRatio
The ratio buttons.
Definition fet_compressor.hpp:250
@ all_buttons_in
Definition fet_compressor.hpp:255
@ r12_1
Definition fet_compressor.hpp:253
@ r20_1
Definition fet_compressor.hpp:254
@ r8_1
Definition fet_compressor.hpp:252
@ r4_1
Definition fet_compressor.hpp:251
float sum(const float *data, std::size_t count) noexcept
@ p
Definition input_events.hpp:40
@ k
Definition input_events.hpp:40
@ x
Definition input_events.hpp:41
The real-unit vocabulary shared by every DSP block in the catalog.
Shared Kaiser-windowed-sinc FIR design helpers.