Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
vocoder.hpp
Go to the documentation of this file.
1#pragma once
2
121
124#include <pulp/signal/osc/va.hpp>
125#include <pulp/signal/rng.hpp>
126#include <pulp/signal/svf.hpp>
128#include <pulp/signal/units.hpp>
129#include <pulp/signal/vca.hpp>
130
131#include <algorithm>
132#include <array>
133#include <cmath>
134#include <cstddef>
135#include <cstdint>
136
137namespace pulp::signal {
138
140enum class VocoderCarrier : std::uint8_t {
143};
144
146enum class VocoderWave : std::uint8_t { saw, pulse };
147
150template <typename SampleType = float>
151class VocoderT {
152public:
155
156 // ── Structural constants ──────────────────────────────────────────────
157
161 static constexpr int kMaxBands = 20;
162 static constexpr int kMinBands = 10;
163 static constexpr std::size_t kBandSlots = static_cast<std::size_t>(kMaxBands);
164
174 static constexpr double kDcBlockHz = 10.0;
175
180 static constexpr double kMaxSampleRate = 192000.0;
181
182 // ── Band splitting ────────────────────────────────────────────────────
183
194 static constexpr double kCascadeBWFactor = 0.6435942529055827;
195
200 static constexpr double kPreEmphasis = 0.95;
201
202 // ── Follower floors ───────────────────────────────────────────────────
203
210 static constexpr double kRippleCycles = 2.0;
211
214 static constexpr double kAttackCycles = 0.5;
215
216 // ── Voiced / unvoiced detection ───────────────────────────────────────
217
222 static constexpr double kZcrLo = 1500.0;
224 static constexpr double kZcrHi = 4500.0;
227 static constexpr double kZcrWindowMs = 20.0;
228
234 static constexpr double kSibilanceCornerHz = 3500.0;
235
239 static constexpr double kUvEnter = 0.6;
240 static constexpr double kUvLeave = 0.4;
241
244 static constexpr double kSensSpan = 0.5;
245
249 static constexpr double kUvSmoothMs = 8.0;
250
254 static constexpr double kEnergyEpsilon = 1e-12;
255
256 // ── Carrier ───────────────────────────────────────────────────────────
257
260 static constexpr double kUnvoicedNoise = 0.9;
261
267 static constexpr std::uint32_t kNoiseSeed = 0x9E3779B9u;
268
278 static constexpr double kDefaultPitchHz = 110.0;
279 static constexpr double kMinPitchHz = 20.0;
280 static constexpr double kMaxPitchHz = 2000.0;
281
282 // ── Output ────────────────────────────────────────────────────────────
283
287 static constexpr double kOutputHeadroomTrim = 0.5;
288
293 static constexpr double kWorstCaseGain = 2.0;
294
295 // ── Parameter bounds (the baked-params table, in one place) ───────────
296
297 static constexpr double kFreqLoMinHz = 60.0;
298 static constexpr double kFreqLoMaxHz = 400.0;
299 static constexpr double kFreqHiMinHz = 3000.0;
300 static constexpr double kFreqHiMaxHz = 12000.0;
301 static constexpr double kAttackMinMs = 0.1;
302 static constexpr double kAttackMaxMs = 50.0;
303 static constexpr double kReleaseMinMs = 2.0;
304 static constexpr double kReleaseMaxMs = 200.0;
305 static constexpr double kPulseWidthMin = 0.05;
306 static constexpr double kPulseWidthMax = 0.95;
307 static constexpr double kFormantShiftMinSt = -24.0;
308 static constexpr double kFormantShiftMaxSt = 24.0;
309 static constexpr double kOutputTrimMinDb = -24.0;
310 static constexpr double kOutputTrimMaxDb = 12.0;
311
312 // ── Lifecycle ─────────────────────────────────────────────────────────
313
314 VocoderT() { rebuild(); }
315
317 void prepare(double sample_rate) {
318 sample_rate_ = (std::isfinite(sample_rate) && sample_rate > 0.0) ? sample_rate : 48000.0;
319 rebuild();
320 reset();
321 }
322
323 void reset() {
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);
330 rng_.reset();
331
332 zcr_bits_.fill(0u);
333 zcr_position_ = 0;
334 zcr_count_ = 0;
335 zcr_hz_ = 0.0;
336 previous_sign_ = true;
337
338 unvoiced_ = 0.0;
339 unvoiced_latched_ = false;
340 freeze_edge_ = false;
341
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;
352 }
353 }
354
356 int latency_samples() const { return 0; }
357
358 // ── Configuration ─────────────────────────────────────────────────────
359
362 void set_band_count(int n) {
363 band_count_ = std::clamp(n, kMinBands, kMaxBands);
364 rebuild();
365 }
366
369 void set_band_range_hz(double f_lo, double f_hi) {
370 if (!std::isfinite(f_lo) || !std::isfinite(f_hi)) return;
371 freq_lo_ = std::clamp(f_lo, kFreqLoMinHz, kFreqLoMaxHz);
372 freq_hi_ = std::clamp(f_hi, kFreqHiMinHz, kFreqHiMaxHz);
373 if (freq_hi_ <= freq_lo_ * 2.0) freq_hi_ = freq_lo_ * 2.0;
374 rebuild();
375 }
376
377 void set_carrier_source(CarrierSource source) { carrier_source_ = source; }
378 CarrierSource carrier_source() const { return carrier_source_; }
379
381 internal_wave_ = wave;
382 oscillator_.set_shape(wave == InternalWave::saw ? osc::VaShape::saw
384 }
385
386 void set_internal_pulse_width(SampleType w01) {
387 if (!std::isfinite(static_cast<double>(w01))) return;
388 pulse_width_ = std::clamp(static_cast<double>(w01), kPulseWidthMin, kPulseWidthMax);
389 oscillator_.set_pulse_width(pulse_width_);
390 }
391
394 void set_internal_pitch_hz(SampleType hz) {
395 if (!std::isfinite(static_cast<double>(hz))) return;
396 pitch_hz_ = std::clamp(static_cast<double>(hz), kMinPitchHz, kMaxPitchHz);
397 }
398
399 double internal_pitch_hz() const { return pitch_hz_; }
400
401 void set_noise_mix(SampleType m01) { noise_mix_ = clamp_unit(m01, noise_mix_); }
402
404 void set_attack_ms(SampleType ms) {
405 if (!std::isfinite(static_cast<double>(ms))) return;
406 attack_ms_ = std::clamp(static_cast<double>(ms), kAttackMinMs, kAttackMaxMs);
407 apply_ballistics();
408 }
409
411 void set_release_ms(SampleType ms) {
412 if (!std::isfinite(static_cast<double>(ms))) return;
413 release_ms_ = std::clamp(static_cast<double>(ms), kReleaseMinMs, kReleaseMaxMs);
414 apply_ballistics();
415 }
416
417 void set_unvoiced_sensitivity(SampleType s01) {
418 unvoiced_sensitivity_ = clamp_unit(s01, unvoiced_sensitivity_);
419 }
420
421 void set_sibilance_mix(SampleType m01) { sibilance_mix_ = clamp_unit(m01, sibilance_mix_); }
422
426 void set_formant_shift_semitones(SampleType st) {
427 if (!std::isfinite(static_cast<double>(st))) return;
428 formant_shift_st_ =
429 std::clamp(static_cast<double>(st), kFormantShiftMinSt, kFormantShiftMaxSt);
430 update_shift();
431 }
432
436 void set_formant_freeze(bool on) { freeze_ = on; }
437 bool formant_freeze() const { return freeze_; }
438
439 void set_output_trim_db(SampleType db) {
440 if (!std::isfinite(static_cast<double>(db))) return;
441 output_trim_db_ =
442 std::clamp(static_cast<double>(db), kOutputTrimMinDb, kOutputTrimMaxDb);
443 output_trim_ = units::db_to_linear(output_trim_db_);
444 }
445
455 void set_dry_wet(SampleType w01) { dry_wet_ = clamp_unit(w01, dry_wet_); }
456
457 // ── Audio ─────────────────────────────────────────────────────────────
458
459 void process(SampleType modulator, SampleType carrier_ext, SampleType& out_dry) {
460 // Both inputs feed recursive filters. A non-finite sample is a recovery
461 // boundary: reject it before it can latch the filterbank, oscillator,
462 // voicing detector, or freeze state.
463 if (!std::isfinite(static_cast<double>(modulator)) ||
464 !std::isfinite(static_cast<double>(carrier_ext))) {
465 reset();
466 out_dry = SampleType{0};
467 return;
468 }
469 const double modulator_in = static_cast<double>(modulator);
470 const double blocked = modulator_dc_.process(modulator_in);
471
472 // Pre-emphasis lifts the upper bands so consonants earn a fair envelope
473 // level. Modulator only — never the carrier, never the output.
474 const double emphasised = blocked - kPreEmphasis * pre_emphasis_state_;
475 pre_emphasis_state_ = blocked;
476
477 update_zero_crossings(blocked);
478
479 // ── Analysis bank ─────────────────────────────────────────────────
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;
489 if (band.center_hz > kSibilanceCornerHz) energy_high += band.envelope;
490 }
491
492 // ── Voiced / unvoiced ─────────────────────────────────────────────
493 const double zcr_norm =
494 std::clamp((zcr_hz_ - kZcrLo) / (kZcrHi - kZcrLo), 0.0, 1.0);
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,
498 0.0, 1.0);
499 // Schmitt on the raw decision, then a one-pole on the latched result.
500 // That order is what lets `u` actually reach 0 and 1: the raw cue tops
501 // out well below 1 even for full-band noise (its high-frequency energy
502 // ratio is bounded by how much of the bank sits above the sibilance
503 // corner), so smoothing the raw value would leave `u` permanently
504 // mid-scale. The hysteresis decides, and the one-pole keeps the
505 // decision from arriving as a click.
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_);
509
510 // ── Freeze, then shift ────────────────────────────────────────────
511 // Freeze first so a frozen vowel can still be swept with the formant
512 // control — the "hold a vowel and morph ooh→aah" gesture needs the
513 // shift to act on held levels, not to be locked out by them.
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_;
517
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_);
522
523 // ── Carrier ───────────────────────────────────────────────────────
524 // One noise draw per call, unconditionally, so the generator's position
525 // does not depend on the carrier source or on the voicing decision.
526 // A render is then bit-identical for a given (params, input) no matter
527 // which branches ran.
528 const double noise = rng_.next_bipolar<double>();
529 const double increment = pitch_hz_ / sample_rate_;
530 const double oscillator = oscillator_.next(increment);
531
532 const double raw_carrier = carrier_source_ == CarrierSource::internal
533 ? oscillator
534 : static_cast<double>(carrier_ext);
535
536 // Internal: noise blends into the whole carrier. External: the carrier
537 // is the user's and stays intact in the lower bands, with noise
538 // substituted only above the sibilance corner so consonants read
539 // without smearing the pad the caller chose.
540 const double unvoiced_noise = unvoiced_ * kUnvoicedNoise;
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;
547
548 // DC-block the SUMMED carrier, after the blend rather than before it —
549 // a pulse carrier's DC moves with its width, and the noise term needs
550 // the same treatment as the oscillator it replaces. Both blockers run
551 // every sample whatever the source, so neither one's state depends on
552 // which branch was taken.
553 const double carrier_low = carrier_dc_low_.process(mixed_low);
554 const double carrier_high = carrier_dc_high_.process(mixed_high);
555
556 // ── Synthesis bank ────────────────────────────────────────────────
557 double sum = 0.0;
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);
564 }
565
566 double wet = kOutputHeadroomTrim * sum;
567 // Gated by `u` as well as by the user control: an ungated sibilance
568 // path leaks breath through every sustained vowel.
569 wet += sibilance_mix_ * unvoiced_ * sibilance_hp_.process_highpass(blocked);
570 wet *= output_trim_;
571
572 const double output = (1.0 - dry_wet_) * blocked + dry_wet_ * wet;
573 if (!std::isfinite(output)) {
574 reset();
575 out_dry = SampleType{0};
576 return;
577 }
578 out_dry = static_cast<SampleType>(output);
579 }
580
581 // ── Observability ─────────────────────────────────────────────────────
582 //
583 // The quantities this module is judged on — band centres, per-band
584 // envelopes, the voicing decision, the gains actually applied — are not
585 // visible in the output, so they are exposed. The acceptance suite still
586 // proves the bank against audio (it measures both filterbanks with coherent
587 // DFTs of the accessor's own output), rather than letting the module agree
588 // with itself.
589
590 int band_count() const { return band_count_; }
591 double band_ratio() const { return ratio_; }
592 double band_q() const { return q_band_; }
593 double section_q() const { return q_section_; }
594 double bands_per_octave() const { return 1.0 / std::log2(ratio_); }
595 double shift_bands() const { return shift_bands_; }
596
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; }
600
603 double analysis_band(int k) const { return in_range(k) ? bands_[index(k)].analysis_out : 0.0; }
604
606 double band_envelope(int k) const { return in_range(k) ? bands_[index(k)].envelope : 0.0; }
607
610 double synthesis_gain(int k) const { return in_range(k) ? bands_[index(k)].gain : 0.0; }
611
613 double unvoiced() const { return unvoiced_; }
614
616 double zcr_hz() const { return zcr_hz_; }
617
621 double zcr_window_ms() const {
622 return 1000.0 * static_cast<double>(zcr_length_) / sample_rate_;
623 }
624
625private:
626 struct Band {
627 SvfT<double> analysis_a;
628 SvfT<double> analysis_b;
629 SvfT<double> synthesis_a;
630 SvfT<double> synthesis_b;
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;
645 double held = 0.0;
646 double gain = 0.0;
647 };
648
649 static constexpr std::size_t kZcrMaxSamples =
650 static_cast<std::size_t>(40.0 * 0.001 * kMaxSampleRate); // widest window, highest rate
651 static constexpr std::size_t kZcrWords = (kZcrMaxSamples + 63u) / 64u;
652
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); }
655
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;
659 }
660
663 void rebuild() {
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));
667
668 const double root = std::sqrt(ratio_);
669 q_band_ = 1.0 / (root - 1.0 / root);
670 q_section_ = kCascadeBWFactor * q_band_;
671
672 // A centre above Nyquist is not a band, so the span is held below it.
673 // At every rate the parameter table allows this is inactive; it exists
674 // so a 22.05 kHz render degrades to a narrower bank instead of to
675 // garbage coefficients.
676 const double ceiling = 0.45 * sample_rate_;
677 for (std::size_t k = 0; k < kBandSlots; ++k) {
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}) {
684 section->set_mode(SvfT<double>::Mode::bandpass);
685 section->set_sample_rate(sample_rate_);
686 section->set_resonance(q_section_);
687 section->set_frequency(band.center_hz);
688 }
689 band.follower.set_mode(BallisticsFilterT<double>::Mode::peak);
690 band.follower.prepare(sample_rate_);
691 }
692
693 apply_ballistics();
694 update_shift();
695
696 sibilance_hp_.prepare(sample_rate_);
697 sibilance_hp_.set_cutoff(kSibilanceCornerHz);
698
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);
703
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))));
707
708 // 10→90 % time on the voicing decision, using the SAME constant the
709 // composed follower uses, so both "10→90 % time" figures in this file
710 // mean exactly the same thing rather than differing by a rounding.
711 unvoiced_coefficient_ =
712 1.0 - std::exp(-kTenToNinety / (kUvSmoothMs * 0.001 * sample_rate_));
713
714 oscillator_.set_shape(internal_wave_ == InternalWave::saw ? osc::VaShape::saw
716 oscillator_.set_pulse_width(pulse_width_);
717 vca_.set_response(VcaResponse::linear);
718 output_trim_ = units::db_to_linear(output_trim_db_);
719 }
720
724 void apply_ballistics() {
725 for (std::size_t k = 0; k < kBandSlots; ++k) {
726 Band& band = bands_[k];
727 const double center = std::max(band.center_hz, 1.0);
728 band.attack_ms = std::max(attack_ms_, 1000.0 * kAttackCycles / center);
729 band.release_ms = std::max(release_ms_, 1000.0 * kRippleCycles / center);
730 band.follower.set_attack_ms(band.attack_ms);
731 band.follower.set_release_ms(band.release_ms);
732 }
733 }
734
738 void update_shift() {
739 shift_bands_ = formant_shift_st_ / (12.0 * std::log2(ratio_));
740 }
741
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;
752 const double b =
753 (lower + 1 >= 0 && lower + 1 < count) ? source_[static_cast<std::size_t>(lower + 1)] : 0.0;
754 return a + frac * (b - a);
755 }
756
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;
763
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) != 0u) --zcr_count_;
767 if (crossing) {
768 zcr_bits_[word] |= bit;
769 ++zcr_count_;
770 } else {
771 zcr_bits_[word] &= ~bit;
772 }
773 if (++zcr_position_ >= zcr_length_) zcr_position_ = 0;
774
775 zcr_hz_ = static_cast<double>(zcr_count_) * sample_rate_ /
776 static_cast<double>(zcr_length_);
777 }
778
784 static constexpr double kTenToNinety = 2.2;
785
786 static constexpr double kPiConstant = 3.14159265358979323846;
787
788 std::array<Band, kBandSlots> bands_{};
789 std::array<double, kBandSlots> source_{};
790 std::array<std::uint64_t, kZcrWords> zcr_bits_{};
791
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_{};
797 VcaT<double> vca_{};
798 Xorshift32 rng_{kNoiseSeed};
799
800 double sample_rate_ = 48000.0;
801 double freq_lo_ = 120.0;
802 double freq_hi_ = 7000.0;
803 double ratio_ = 1.0;
804 double q_band_ = 1.0;
805 double q_section_ = 1.0;
806
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;
817 double pitch_hz_ = kDefaultPitchHz;
818 double pulse_width_ = 0.5;
819
820 double pre_emphasis_state_ = 0.0;
821 double unvoiced_ = 0.0;
822 double unvoiced_coefficient_ = 0.0;
823 double zcr_hz_ = 0.0;
824
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;
829
830 int band_count_ = 16;
831 CarrierSource carrier_source_ = CarrierSource::internal;
832 InternalWave internal_wave_ = InternalWave::saw;
833 bool previous_sign_ = true;
834 bool unvoiced_latched_ = false;
835 bool freeze_ = false;
836 bool freeze_edge_ = false;
837};
838
841
842} // namespace pulp::signal
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
Definition svf.hpp:16
@ 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
@ fallback
Definition health_read_result.hpp:30
@ 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.