150template <
typename SampleType =
float>
318 sample_rate_ = (std::isfinite(sample_rate) && sample_rate > 0.0) ? sample_rate : 48000.0;
324 modulator_dc_.reset();
325 carrier_dc_low_.reset();
326 carrier_dc_high_.reset();
327 sibilance_hp_.reset();
328 pre_emphasis_state_ = 0.0;
329 oscillator_.reset(0.0);
336 previous_sign_ =
true;
339 unvoiced_latched_ =
false;
340 freeze_edge_ =
false;
342 for (std::size_t k = 0; k <
kBandSlots; ++k) {
343 bands_[k].analysis_a.reset();
344 bands_[k].analysis_b.reset();
345 bands_[k].synthesis_a.reset();
346 bands_[k].synthesis_b.reset();
347 bands_[k].follower.reset();
348 bands_[k].analysis_out = 0.0;
349 bands_[k].envelope = 0.0;
350 bands_[k].held = 0.0;
351 bands_[k].gain = 0.0;
370 if (!std::isfinite(f_lo) || !std::isfinite(f_hi))
return;
373 if (freq_hi_ <= freq_lo_ * 2.0) freq_hi_ = freq_lo_ * 2.0;
381 internal_wave_ = wave;
387 if (!std::isfinite(
static_cast<double>(w01)))
return;
389 oscillator_.set_pulse_width(pulse_width_);
395 if (!std::isfinite(
static_cast<double>(hz)))
return;
401 void set_noise_mix(SampleType m01) { noise_mix_ = clamp_unit(m01, noise_mix_); }
405 if (!std::isfinite(
static_cast<double>(ms)))
return;
412 if (!std::isfinite(
static_cast<double>(ms)))
return;
418 unvoiced_sensitivity_ = clamp_unit(s01, unvoiced_sensitivity_);
427 if (!std::isfinite(
static_cast<double>(st)))
return;
440 if (!std::isfinite(
static_cast<double>(db)))
return;
455 void set_dry_wet(SampleType w01) { dry_wet_ = clamp_unit(w01, dry_wet_); }
459 void process(SampleType modulator, SampleType carrier_ext, SampleType& out_dry) {
463 if (!std::isfinite(
static_cast<double>(modulator)) ||
464 !std::isfinite(
static_cast<double>(carrier_ext))) {
466 out_dry = SampleType{0};
469 const double modulator_in =
static_cast<double>(modulator);
470 const double blocked = modulator_dc_.process(modulator_in);
474 const double emphasised = blocked -
kPreEmphasis * pre_emphasis_state_;
475 pre_emphasis_state_ = blocked;
477 update_zero_crossings(blocked);
480 double energy_total = 0.0;
481 double energy_high = 0.0;
482 for (std::size_t k = 0; k < active_; ++k) {
483 Band& band = bands_[k];
484 const double filtered =
485 band.analysis_b.
process(band.analysis_a.
process(emphasised)) * band.normalisation;
486 band.analysis_out = filtered;
487 band.envelope = band.follower.
process(filtered);
488 energy_total += band.envelope;
493 const double zcr_norm =
495 const double hf_ratio = energy_high / (energy_total +
kEnergyEpsilon);
496 const double raw = std::clamp(0.5 * zcr_norm + 0.5 * hf_ratio -
497 (0.5 - unvoiced_sensitivity_) *
kSensSpan,
506 if (raw >
kUvEnter) unvoiced_latched_ =
true;
507 else if (raw <
kUvLeave) unvoiced_latched_ =
false;
508 unvoiced_ += unvoiced_coefficient_ * ((unvoiced_latched_ ? 1.0 : 0.0) - unvoiced_);
514 if (freeze_ && !freeze_edge_)
515 for (std::size_t k = 0; k < active_; ++k) bands_[k].held = bands_[k].envelope;
516 freeze_edge_ = freeze_;
518 for (std::size_t k = 0; k < active_; ++k)
519 source_[k] = freeze_ ? bands_[k].held : bands_[k].envelope;
520 for (std::size_t j = 0; j < active_; ++j)
521 bands_[j].gain = shifted_level(
static_cast<double>(j) - shift_bands_);
528 const double noise = rng_.next_bipolar<
double>();
529 const double increment = pitch_hz_ / sample_rate_;
530 const double oscillator = oscillator_.next(increment);
532 const double raw_carrier = carrier_source_ == CarrierSource::internal
534 :
static_cast<double>(carrier_ext);
541 const double internal_noise = std::max(noise_mix_, unvoiced_noise);
542 const bool internal = carrier_source_ == CarrierSource::internal;
543 const double mixed_low =
544 internal ? (1.0 - internal_noise) * raw_carrier + internal_noise * noise : raw_carrier;
545 const double mixed_high =
546 internal ? mixed_low : (1.0 - unvoiced_noise) * raw_carrier + unvoiced_noise * noise;
553 const double carrier_low = carrier_dc_low_.process(mixed_low);
554 const double carrier_high = carrier_dc_high_.process(mixed_high);
558 for (std::size_t k = 0; k < active_; ++k) {
559 Band& band = bands_[k];
560 const double input = band.center_hz >
kSibilanceCornerHz ? carrier_high : carrier_low;
561 const double filtered =
562 band.synthesis_b.
process(band.synthesis_a.
process(input)) * band.normalisation;
563 sum += vca_.process(filtered, band.gain);
569 wet += sibilance_mix_ * unvoiced_ * sibilance_hp_.process_highpass(blocked);
572 const double output = (1.0 - dry_wet_) * blocked + dry_wet_ * wet;
573 if (!std::isfinite(output)) {
575 out_dry = SampleType{0};
578 out_dry =
static_cast<SampleType
>(output);
592 double band_q()
const {
return q_band_; }
597 double band_center_hz(
int k)
const {
return in_range(k) ? bands_[index(k)].center_hz : 0.0; }
598 double attack_eff_ms(
int k)
const {
return in_range(k) ? bands_[index(k)].attack_ms : 0.0; }
599 double release_eff_ms(
int k)
const {
return in_range(k) ? bands_[index(k)].release_ms : 0.0; }
603 double analysis_band(
int k)
const {
return in_range(k) ? bands_[index(k)].analysis_out : 0.0; }
606 double band_envelope(
int k)
const {
return in_range(k) ? bands_[index(k)].envelope : 0.0; }
610 double synthesis_gain(
int k)
const {
return in_range(k) ? bands_[index(k)].gain : 0.0; }
616 double zcr_hz()
const {
return zcr_hz_; }
622 return 1000.0 *
static_cast<double>(zcr_length_) / sample_rate_;
632 double center_hz = 0.0;
640 double normalisation = 1.0;
641 double attack_ms = 0.0;
642 double release_ms = 0.0;
643 double analysis_out = 0.0;
644 double envelope = 0.0;
649 static constexpr std::size_t kZcrMaxSamples =
651 static constexpr std::size_t kZcrWords = (kZcrMaxSamples + 63u) / 64u;
653 bool in_range(
int k)
const {
return k >= 0 && k < band_count_; }
654 static std::size_t index(
int k) {
return static_cast<std::size_t
>(k); }
656 double clamp_unit(SampleType v,
double fallback)
const {
657 const double d =
static_cast<double>(
v);
658 return std::isfinite(d) ? std::clamp(d, 0.0, 1.0) :
fallback;
664 active_ =
static_cast<std::size_t
>(band_count_);
665 const double span = std::max(freq_hi_ / freq_lo_, 1.0000001);
666 ratio_ = std::pow(span, 1.0 /
static_cast<double>(band_count_ - 1));
668 const double root = std::sqrt(ratio_);
669 q_band_ = 1.0 / (root - 1.0 / root);
676 const double ceiling = 0.45 * sample_rate_;
678 Band& band = bands_[
k];
679 const double nominal = freq_lo_ * std::pow(ratio_,
static_cast<double>(k));
680 band.center_hz = std::min(nominal, ceiling);
681 band.normalisation = 1.0 / (q_section_ * q_section_);
682 for (
auto* section : {&band.analysis_a, &band.analysis_b, &band.synthesis_a,
683 &band.synthesis_b}) {
685 section->set_sample_rate(sample_rate_);
686 section->set_resonance(q_section_);
687 section->set_frequency(band.center_hz);
690 band.follower.prepare(sample_rate_);
696 sibilance_hp_.prepare(sample_rate_);
699 const double dc_pole = std::exp(-2.0 * kPiConstant *
kDcBlockHz / sample_rate_);
700 modulator_dc_.set_pole(dc_pole);
701 carrier_dc_low_.set_pole(dc_pole);
702 carrier_dc_high_.set_pole(dc_pole);
704 zcr_length_ =
static_cast<std::size_t
>(
705 std::llround(std::clamp(
kZcrWindowMs * 0.001 * sample_rate_, 1.0,
706 static_cast<double>(kZcrMaxSamples))));
711 unvoiced_coefficient_ =
712 1.0 - std::exp(-kTenToNinety / (
kUvSmoothMs * 0.001 * sample_rate_));
716 oscillator_.set_pulse_width(pulse_width_);
724 void apply_ballistics() {
726 Band& band = bands_[
k];
727 const double center = std::max(band.center_hz, 1.0);
730 band.follower.set_attack_ms(band.attack_ms);
731 band.follower.set_release_ms(band.release_ms);
738 void update_shift() {
739 shift_bands_ = formant_shift_st_ / (12.0 * std::log2(ratio_));
746 double shifted_level(
double x)
const {
747 const double floor_x = std::floor(x);
748 const auto lower =
static_cast<long long>(floor_x);
749 const double frac =
x - floor_x;
750 const auto count =
static_cast<long long>(active_);
751 const double a = (lower >= 0 && lower < count) ? source_[static_cast<std::size_t>(lower)] : 0.0;
753 (lower + 1 >= 0 && lower + 1 <
count) ? source_[
static_cast<std::size_t
>(lower + 1)] : 0.0;
754 return a + frac * (
b -
a);
759 void update_zero_crossings(
double x) {
760 const bool sign =
x >= 0.0;
761 const bool crossing = sign != previous_sign_;
762 previous_sign_ = sign;
764 const std::size_t word = zcr_position_ >> 6;
765 const std::uint64_t bit = std::uint64_t{1} << (zcr_position_ & 63u);
766 if ((zcr_bits_[word] & bit) != 0
u) --zcr_count_;
768 zcr_bits_[word] |= bit;
771 zcr_bits_[word] &= ~bit;
773 if (++zcr_position_ >= zcr_length_) zcr_position_ = 0;
775 zcr_hz_ =
static_cast<double>(zcr_count_) * sample_rate_ /
776 static_cast<double>(zcr_length_);
784 static constexpr double kTenToNinety = 2.2;
786 static constexpr double kPiConstant = 3.14159265358979323846;
788 std::array<Band, kBandSlots> bands_{};
789 std::array<double, kBandSlots> source_{};
790 std::array<std::uint64_t, kZcrWords> zcr_bits_{};
792 DcBlocker<double> modulator_dc_{};
793 DcBlocker<double> carrier_dc_low_{};
794 DcBlocker<double> carrier_dc_high_{};
795 TptFilterT<double> sibilance_hp_{};
796 osc::VaOscillator oscillator_{};
800 double sample_rate_ = 48000.0;
801 double freq_lo_ = 120.0;
802 double freq_hi_ = 7000.0;
804 double q_band_ = 1.0;
805 double q_section_ = 1.0;
807 double attack_ms_ = 1.5;
808 double release_ms_ = 15.0;
809 double noise_mix_ = 0.15;
810 double unvoiced_sensitivity_ = 0.5;
811 double sibilance_mix_ = 0.35;
812 double formant_shift_st_ = 0.0;
813 double shift_bands_ = 0.0;
814 double output_trim_db_ = 0.0;
815 double output_trim_ = 1.0;
816 double dry_wet_ = 1.0;
818 double pulse_width_ = 0.5;
820 double pre_emphasis_state_ = 0.0;
821 double unvoiced_ = 0.0;
822 double unvoiced_coefficient_ = 0.0;
823 double zcr_hz_ = 0.0;
825 std::size_t active_ = 16;
826 std::size_t zcr_length_ = 1;
827 std::size_t zcr_position_ = 0;
828 std::size_t zcr_count_ = 0;
830 int band_count_ = 16;
833 bool previous_sign_ =
true;
834 bool unvoiced_latched_ =
false;
835 bool freeze_ =
false;
836 bool freeze_edge_ =
false;
Peak/RMS envelope follower with independent attack and release curves.
Envelope follower with configurable attack and release times.
Definition ballistics_filter.hpp:31
@ peak
Definition ballistics_filter.hpp:33
SampleType process(SampleType input)
Definition ballistics_filter.hpp:64
@ bandpass
Definition svf.hpp:18
SampleType process(SampleType input)
Definition svf.hpp:54
A classic channel vocoder: analysis bank → envelopes → matched synthesis bank.
Definition vocoder.hpp:151
static constexpr double kSibilanceCornerHz
Definition vocoder.hpp:234
void set_dry_wet(SampleType w01)
0 = the (DC-blocked) modulator passes through, 1 = fully vocoded.
Definition vocoder.hpp:455
double release_eff_ms(int k) const
Definition vocoder.hpp:599
static constexpr double kMaxSampleRate
Definition vocoder.hpp:180
void set_carrier_source(CarrierSource source)
Definition vocoder.hpp:377
void reset()
Definition vocoder.hpp:323
double bands_per_octave() const
Definition vocoder.hpp:594
static constexpr double kFreqHiMaxHz
Definition vocoder.hpp:300
static constexpr int kMaxBands
Definition vocoder.hpp:161
VocoderT()
Definition vocoder.hpp:314
static constexpr double kSensSpan
Definition vocoder.hpp:244
void prepare(double sample_rate)
Fixes the sample rate, recomputes every coefficient, and clears state.
Definition vocoder.hpp:317
static constexpr double kDefaultPitchHz
Definition vocoder.hpp:278
static constexpr double kFormantShiftMinSt
Definition vocoder.hpp:307
double band_center_hz(int k) const
Definition vocoder.hpp:597
void set_release_ms(SampleType ms)
Follower release as a 10→90 % time in ms, floored per band.
Definition vocoder.hpp:411
static constexpr double kMinPitchHz
Definition vocoder.hpp:279
static constexpr double kOutputTrimMaxDb
Definition vocoder.hpp:310
void set_output_trim_db(SampleType db)
Definition vocoder.hpp:439
void set_internal_wave(InternalWave wave)
Definition vocoder.hpp:380
static constexpr double kUvEnter
Definition vocoder.hpp:239
static constexpr double kFreqHiMinHz
Definition vocoder.hpp:299
static constexpr double kMaxPitchHz
Definition vocoder.hpp:280
static constexpr double kWorstCaseGain
Definition vocoder.hpp:293
static constexpr double kDcBlockHz
Definition vocoder.hpp:174
static constexpr double kOutputHeadroomTrim
Definition vocoder.hpp:287
double section_q() const
Definition vocoder.hpp:593
double zcr_hz() const
Measured zero-crossing rate of the modulator, Hz.
Definition vocoder.hpp:616
static constexpr double kFreqLoMinHz
Definition vocoder.hpp:297
void set_noise_mix(SampleType m01)
Definition vocoder.hpp:401
int band_count() const
Definition vocoder.hpp:590
void set_internal_pitch_hz(SampleType hz)
The internal carrier's frequency.
Definition vocoder.hpp:394
static constexpr double kOutputTrimMinDb
Definition vocoder.hpp:309
static constexpr double kReleaseMinMs
Definition vocoder.hpp:303
double shift_bands() const
Definition vocoder.hpp:595
static constexpr double kZcrLo
Definition vocoder.hpp:222
double unvoiced() const
The smoothed voicing decision: 0 fully voiced, 1 fully unvoiced.
Definition vocoder.hpp:613
static constexpr double kReleaseMaxMs
Definition vocoder.hpp:304
double band_ratio() const
Definition vocoder.hpp:591
double attack_eff_ms(int k) const
Definition vocoder.hpp:598
static constexpr double kFormantShiftMaxSt
Definition vocoder.hpp:308
static constexpr double kUvLeave
Definition vocoder.hpp:240
void set_formant_shift_semitones(SampleType st)
Formant shift in semitones, converted to a fractional band-index offset through the bank's own ratio,...
Definition vocoder.hpp:426
static constexpr double kZcrWindowMs
Definition vocoder.hpp:227
void set_sibilance_mix(SampleType m01)
Definition vocoder.hpp:421
static constexpr double kEnergyEpsilon
Definition vocoder.hpp:254
static constexpr double kAttackMaxMs
Definition vocoder.hpp:302
void set_internal_pulse_width(SampleType w01)
Definition vocoder.hpp:386
double band_envelope(int k) const
The live follower output for band k, ignoring freeze and shift.
Definition vocoder.hpp:606
void set_attack_ms(SampleType ms)
Follower attack as a 10→90 % time in ms, floored per band.
Definition vocoder.hpp:404
static constexpr double kAttackCycles
Definition vocoder.hpp:214
void set_band_range_hz(double f_lo, double f_hi)
Analysis and synthesis span.
Definition vocoder.hpp:369
static constexpr double kAttackMinMs
Definition vocoder.hpp:301
double band_q() const
Definition vocoder.hpp:592
void process(SampleType modulator, SampleType carrier_ext, SampleType &out_dry)
Definition vocoder.hpp:459
void set_unvoiced_sensitivity(SampleType s01)
Definition vocoder.hpp:417
int latency_samples() const
0 for every configuration. See this file's latency note.
Definition vocoder.hpp:356
static constexpr double kPreEmphasis
Definition vocoder.hpp:200
VocoderWave InternalWave
Definition vocoder.hpp:154
static constexpr double kFreqLoMaxHz
Definition vocoder.hpp:298
static constexpr double kZcrHi
Definition vocoder.hpp:224
double analysis_band(int k) const
The 4th-order analysis output for band k on the most recent sample — the filter's own signal,...
Definition vocoder.hpp:603
static constexpr double kUnvoicedNoise
Definition vocoder.hpp:260
static constexpr double kPulseWidthMin
Definition vocoder.hpp:305
static constexpr double kUvSmoothMs
Definition vocoder.hpp:249
static constexpr double kCascadeBWFactor
Definition vocoder.hpp:194
bool formant_freeze() const
Definition vocoder.hpp:437
double internal_pitch_hz() const
Definition vocoder.hpp:399
VocoderCarrier CarrierSource
Definition vocoder.hpp:153
static constexpr int kMinBands
Definition vocoder.hpp:162
void set_band_count(int n)
Band count, 10 .
Definition vocoder.hpp:362
double zcr_window_ms() const
Effective ZCR window length in ms.
Definition vocoder.hpp:621
static constexpr double kPulseWidthMax
Definition vocoder.hpp:306
double synthesis_gain(int k) const
The level actually applied to synthesis band k — post-freeze, post-shift.
Definition vocoder.hpp:610
CarrierSource carrier_source() const
Definition vocoder.hpp:378
static constexpr std::uint32_t kNoiseSeed
Definition vocoder.hpp:267
static constexpr double kRippleCycles
Definition vocoder.hpp:210
void set_formant_freeze(bool on)
Latches the band levels.
Definition vocoder.hpp:436
static constexpr std::size_t kBandSlots
Definition vocoder.hpp:163
@ count
sentinel: number of real capabilities (keep LAST)
Definition canvas_capability.hpp:38
@ d
Definition pitch.hpp:22
@ b
Definition pitch.hpp:31
@ square
Value steps of +2 at the wrap and -2 at the pulse edge.
Definition va.hpp:116
@ saw
Value step of -2 at the wrap.
Definition va.hpp:115
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
VocoderCarrier
Where the thing that speaks comes from.
Definition vocoder.hpp:140
@ external
The caller's carrier_ext input: a pad, a guitar, anything rich.
Definition vocoder.hpp:141
@ internal
The module's own band-limited oscillator — the DVP-1 lesson.
Definition vocoder.hpp:142
@ center
Definition multi_channel_meter.hpp:68
@ linear
Definition vca.hpp:42
VocoderWave
The internal carrier's shape. Both are polyBLEP-corrected.
Definition vocoder.hpp:146
VocoderT< float > Vocoder
Definition vocoder.hpp:839
VocoderT< double > Vocoder64
Definition vocoder.hpp:840
@ a
Definition formant_filter_bank.hpp:20
@ u
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
@ pulse
a ramp then a hold: sine becomes pulse-like
Definition phase_distortion.hpp:11
@ saw
the bend point slides toward zero: sine becomes saw-like
Definition phase_distortion.hpp:10
@ k
Definition input_events.hpp:40
@ x
Definition input_events.hpp:41
@ v
Definition input_events.hpp:41
Deterministic randomness for modulation: a counter-free PRNG, a Gaussian draw, a stateless purpose-ke...
First-order topology-preserving transform (TPT) filter.
The real-unit vocabulary shared by every DSP block in the catalog.
Virtual-analog oscillator: the classic static shapes, bandlimited by composing PhaseAccumulator (the ...
Control-driven amplifier with a linear or exponential response and built-in control lag.