Pulp 1.88.0
Cross-platform audio plugin framework
Loading...
Searching...
No Matches
pitch_shifter.hpp
Go to the documentation of this file.
1#pragma once
2
149
155#include <pulp/signal/rng.hpp>
157#include <pulp/signal/units.hpp>
158
159#include <algorithm>
160#include <cmath>
161#include <cstddef>
162#include <cstdint>
163
164namespace pulp::signal {
165
168enum class PedalMode : std::uint8_t {
173};
174
180enum class PitchInterp : std::uint8_t {
183};
184
192enum class ShiftSource : std::uint8_t {
195};
196
210template <typename SampleType = float>
212public:
213 // ── Calibration table (§6) ────────────────────────────────────────────
214 //
215 // Every constant below is a [design parameter] with a default and a range,
216 // per the HONEST GAP note in the file doc block, except where marked
217 // definitional. Tests compute their expectations from these rather than
218 // restating literals, so retuning one fails the case that documents it.
219
222 static constexpr double kWindowMsDefault = 40.0;
223 static constexpr double kWindowMsMin = 10.0;
224 static constexpr double kWindowMsMax = 100.0;
225
228 static constexpr double kGlideMsDefault = 60.0;
229 static constexpr double kGlideMsMin = 0.0;
230 static constexpr double kGlideMsMax = 2000.0;
231
234 static constexpr double kDetuneCentsDefault = 12.0;
235 static constexpr double kDetuneCentsMax = 50.0;
236
239 static constexpr double kDiveFloorSemisDefault = -48.0;
240 static constexpr double kDiveFloorSemisMin = -72.0;
241 static constexpr double kDiveFloorSemisMax = -24.0;
242
246 static constexpr double kHeelSemisDefault = 0.0;
247 static constexpr double kToeSemisDefault = 12.0;
248 static constexpr double kIntervalASemisDefault = 7.0;
249 static constexpr double kIntervalBSemisDefault = 12.0;
250
255 static constexpr double kShiftSemisMin = -48.0;
256 static constexpr double kShiftSemisMax = 24.0;
257
260 static constexpr double kSemitonesFloor = kDiveFloorSemisMin;
261 static constexpr double kSemitonesCeil = kShiftSemisMax;
262
265 static constexpr double kDetentTable[] = {-24.0, -12.0, -7.0, -5.0, 0.0,
266 5.0, 7.0, 12.0, 24.0};
267 static constexpr int kDetentCount = 9;
268
274 static constexpr double kDetentSnapSemis = 0.35;
275
281 static constexpr double kDetentReleaseRatio = 1.5;
282
287 static constexpr double kDetentReleaseLimitSemis = 1.0;
288
293 static constexpr int kInterpMargin = 4;
294
298 static constexpr double kDriftMaxDepthPercent = 0.5;
299
302 static constexpr std::uint32_t kDriftSeed = 0x57484D59u; // 'WHMY'
303
317 static constexpr double kDcBlockerCornerHz = 5.0;
318
326 static constexpr double kClickBound = 0.25;
327
336 static constexpr double kWorstCaseGain = 2.23606797749979; // √5
337 static_assert(kWorstCaseGain > 2.0, "the wet leg alone can reach 2x");
338
353 static constexpr double kDcBlockerPeakGain = 2.0;
354
362 double dc_blocker_magnitude_peak() const { return 2.0 / (1.0 + dc_pole()); }
363
365 static constexpr double kDefaultSampleRate = 48000.0;
366
370 static constexpr double default_mix_for(PedalMode mode) {
371 switch (mode) {
372 case PedalMode::harmony: return 0.5;
373 case PedalMode::detune: return 0.4;
375 case PedalMode::dive:
376 default: return 1.0;
377 }
378 }
379
389 static constexpr bool mode_uses_detents(PedalMode mode) {
390 return mode == PedalMode::whammy || mode == PedalMode::harmony;
391 }
392
393 // ── Lifecycle ─────────────────────────────────────────────────────────
394
396 dc_.set_pole(static_cast<SampleType>(dc_pole()));
397 drift_.set_seed(kDriftSeed);
398 drift_.set_depth_percent(0.0);
399 glide_.set_mode(SlewMode::linear);
400 glide_.set_rise_ms(kGlideMsDefault);
401 glide_.set_fall_ms(kGlideMsDefault);
402 update_window();
403 glide_.set_immediate(target_semitones());
404 semitones_ = target_semitones();
405 update_ratio();
406 }
407
413 void prepare(double sample_rate) {
414 sample_rate_ = (std::isfinite(sample_rate) && sample_rate > 0.0)
415 ? sample_rate
417
418 const int capacity =
419 static_cast<int>(
420 std::ceil(units::ms_to_samples(kWindowMsMax, sample_rate_))) +
421 2 * kInterpMargin;
422 line_.prepare(capacity);
423 max_read_samples_ =
424 static_cast<double>(capacity - kInterpMargin);
425
426 glide_.prepare(sample_rate_);
427 drift_.prepare(sample_rate_);
428 dc_.set_pole(static_cast<SampleType>(dc_pole()));
429 update_window();
430 reset();
431 }
432
436 void reset() {
437 line_.reset();
438 dc_.reset();
439 drift_.reset();
440 phase_ = 0.0;
441 window_a_ = window_b_ = window_samples_;
442 glide_.set_immediate(target_semitones());
443 semitones_ = target_semitones();
444 update_ratio();
445 }
446
448 void discard_history() noexcept {
449 line_.discard_history();
450 dc_.reset();
451 drift_.reset();
452 phase_ = 0.0;
453 window_a_ = window_b_ = window_samples_;
454 glide_.set_immediate(target_semitones());
455 semitones_ = target_semitones();
456 update_ratio();
457 }
458
459 // ── Control surface ───────────────────────────────────────────────────
460
462 void set_shift_source(ShiftSource source) { source_ = source; }
463 ShiftSource shift_source() const { return source_; }
464
467 void set_shift_semitones(double semitones) {
468 if (!std::isfinite(semitones)) return;
469 shift_semitones_ = clamp_finite(semitones, kShiftSemisMin, kShiftSemisMax);
470 }
471 double shift_semitones() const { return shift_semitones_; }
472
474 void set_pedal(double e01) {
475 if (!std::isfinite(e01)) return;
476 pedal_ = clamp_finite(e01, 0.0, 1.0);
477 }
478 double pedal() const { return pedal_; }
479
482 void set_pedal_mode(PedalMode mode) { mode_ = mode; }
483 PedalMode pedal_mode() const { return mode_; }
484
486 void set_targets(double heel, double toe) {
487 if (!std::isfinite(heel) || !std::isfinite(toe)) return;
488 heel_semis_ = clamp_finite(heel, kShiftSemisMin, kShiftSemisMax);
489 toe_semis_ = clamp_finite(toe, kShiftSemisMin, kShiftSemisMax);
490 }
491
493 void set_harmony(double a, double b) {
494 if (!std::isfinite(a) || !std::isfinite(b)) return;
495 interval_a_semis_ = clamp_finite(a, -24.0, 24.0);
496 interval_b_semis_ = clamp_finite(b, -24.0, 24.0);
497 }
498
499 void set_detune_cents(double cents) {
500 if (!std::isfinite(cents)) return;
501 detune_cents_ = clamp_finite(cents, -kDetuneCentsMax, kDetuneCentsMax);
502 }
503
504 void set_dive_floor_semis(double semitones) {
505 if (!std::isfinite(semitones)) return;
506 dive_floor_semis_ =
507 clamp_finite(semitones, kDiveFloorSemisMin, kDiveFloorSemisMax);
508 }
509
519 void set_window_ms(double ms) {
520 if (!std::isfinite(ms)) return;
521 window_ms_ = clamp_finite(ms, kWindowMsMin, kWindowMsMax);
522 update_window();
523 }
524 double window_ms() const { return window_ms_; }
525
529 void set_glide_ms(double up_ms, double down_ms) {
530 if (!std::isfinite(up_ms) || !std::isfinite(down_ms)) return;
531 glide_.set_rise_ms(clamp_finite(up_ms, kGlideMsMin, kGlideMsMax));
532 glide_.set_fall_ms(clamp_finite(down_ms, kGlideMsMin, kGlideMsMax));
533 }
534 double glide_up_ms() const { return glide_.rise_ms(); }
535 double glide_down_ms() const { return glide_.fall_ms(); }
536
538 void set_mix(double wet01) {
539 if (!std::isfinite(wet01)) return;
540 mix_ = clamp_finite(wet01, 0.0, 1.0);
541 }
542 double mix() const { return mix_; }
543
547 void set_detents(bool on) {
548 detents_ = on;
549 if (!on) detent_held_ = false;
550 }
551 bool detents() const { return detents_; }
552
553 void set_interp(PitchInterp interp) { interp_ = interp; }
554 PitchInterp interp() const { return interp_; }
555
559 void set_drift_depth(double d01) {
560 if (!std::isfinite(d01)) return;
561 drift_depth_ = clamp_finite(d01, 0.0, 1.0);
562 drift_.set_depth_percent(drift_depth_ * kDriftMaxDepthPercent);
563 }
564 double drift_depth() const { return drift_depth_; }
565
570 glide_.set_immediate(target_semitones());
571 semitones_ = target_semitones();
572 update_ratio();
573 }
574
575 // ── Reporting ─────────────────────────────────────────────────────────
576
579 double target_semitones() const {
580 if (source_ == ShiftSource::direct)
581 return std::clamp(shift_semitones_, kSemitonesFloor, kSemitonesCeil);
582 return std::clamp(detented(pedal_law(pedal_)), kSemitonesFloor,
584 }
585
588 double pedal_law(double e01) const {
589 const double e = std::clamp(e01, 0.0, 1.0);
590 switch (mode_) {
592 return lerp(interval_a_semis_, interval_b_semis_, e);
594 return (detune_cents_ / units::kCentsPerSemitone) * e;
595 case PedalMode::dive:
596 return lerp(0.0, dive_floor_semis_, e);
598 default:
599 return lerp(heel_semis_, toe_semis_, e);
600 }
601 }
602
604 double current_semitones() const { return semitones_; }
605
611 double current_ratio() const { return effective_ratio_; }
612
615 double warble_hz() const {
616 return window_ms_ > 0.0
617 ? std::abs(1.0 - effective_ratio_) * 1000.0 / window_ms_
618 : 0.0;
619 }
620
625 double tap_phase_pi(double hz) const {
626 return hz * window_ms_ / 1000.0;
627 }
628
630 double window_samples() const { return window_samples_; }
631
636 int latency_samples() const {
637 return static_cast<int>(
638 std::lround(window_ms_ * sample_rate_ / 2000.0));
639 }
640
641 // ── Audio ─────────────────────────────────────────────────────────────
642
645 SampleType process(SampleType x) {
646 if (!std::isfinite(static_cast<double>(x))) {
648 return SampleType{0};
649 }
650 const double dry = static_cast<double>(x);
651 const double wet = advance(dry);
652 double dry_gain = 0.0;
653 double wet_gain = 0.0;
654 crossfade_gains(mix_, CrossfadeGainLaw::EqualPower, dry_gain, wet_gain);
655 return static_cast<SampleType>(dry_gain * dry + wet_gain * wet);
656 }
657
660 SampleType process_wet(SampleType x) {
661 if (!std::isfinite(static_cast<double>(x))) {
663 return SampleType{0};
664 }
665 return static_cast<SampleType>(advance(static_cast<double>(x)));
666 }
667
668private:
669 static constexpr double kPi = 3.14159265358979323846;
670
671 static double lerp(double a, double b, double t) { return a + (b - a) * t; }
672
673 static double clamp_finite(double v, double lo, double hi) {
674 if (!std::isfinite(v)) return lo;
675 return std::clamp(v, lo, hi);
676 }
677
681 double detented(double raw) const {
682 if (!detents_ || !mode_uses_detents(mode_)) {
683 detent_held_ = false;
684 return raw;
685 }
686
687 const double release =
690
691 if (detent_held_) {
692 const double held = kDetentTable[detent_index_];
693 if (std::abs(raw - held) <= release) return held;
694 detent_held_ = false;
695 }
696
697 int nearest = 0;
698 double best = std::abs(raw - kDetentTable[0]);
699 for (int i = 1; i < kDetentCount; ++i) {
700 const double d = std::abs(raw - kDetentTable[i]);
701 if (d < best) {
702 best = d;
703 nearest = i;
704 }
705 }
706 if (best <= kDetentSnapSemis) {
707 detent_held_ = true;
708 detent_index_ = nearest;
709 return kDetentTable[nearest];
710 }
711 return raw;
712 }
713
714 void update_window() {
715 window_samples_ = units::ms_to_samples(window_ms_, sample_rate_);
716 if (window_a_ <= 0.0) window_a_ = window_samples_;
717 if (window_b_ <= 0.0) window_b_ = window_samples_;
718 }
719
720 void update_ratio() {
721 ratio_ = units::semitones_to_ratio(semitones_);
722 effective_ratio_ = ratio_;
723 }
724
727 double dc_pole() const {
728 constexpr double kTwoPi = 2.0 * kPi;
729 const double p = 1.0 - kTwoPi * kDcBlockerCornerHz / sample_rate_;
730 return std::clamp(p, 0.0, 0.9999);
731 }
732
737 double read_tap(double delay_samples) const {
738 // Not `std::clamp`: before `prepare` the two bounds cross, and a
739 // crossed `clamp` is undefined behaviour rather than a zero read.
740 //
741 // The low bound is what keeps the stencil off the buffer seam. It bites
742 // only for `p < kInterpMargin/L` — 0.2 % of a default window — where the
743 // tap's own crossfade weight is already below `sin²(π·0.002) ≈ 4e-5`
744 // (−88 dB), so the momentary ratio error it causes is inaudible by
745 // construction rather than by tolerance.
746 if (max_read_samples_ <= static_cast<double>(kInterpMargin)) return 0.0;
747 const double d = std::max(static_cast<double>(kInterpMargin),
748 std::min(delay_samples, max_read_samples_));
749 const int i = static_cast<int>(std::floor(d));
750 const double frac = d - static_cast<double>(i);
751 const double y0 = static_cast<double>(line_.read(i));
752 const double y1 = static_cast<double>(line_.read(i + 1));
753 if (interp_ == PitchInterp::linear)
754 return Interpolator::linear(frac, y0, y1);
755 const double ym1 = static_cast<double>(line_.read(i - 1));
756 const double y2 = static_cast<double>(line_.read(i + 2));
757 return Interpolator::lagrange(frac, ym1, y0, y1, y2);
758 }
759
760 static double frac1(double v) { return v - std::floor(v); }
761
763 double advance(double x) {
764 // An unprepared instance emits silence rather than faulting. The
765 // contract says `prepare` must precede `process`, but `DelayLineT::push`
766 // writes its buffer unguarded, so the penalty for a host that calls
767 // `process` early — which real hosts do — would be an out-of-bounds
768 // write rather than a diagnosable silence. Guarding the read while
769 // leaving the write to fault would be the worst of both.
770 if (max_read_samples_ <= static_cast<double>(kInterpMargin)) return 0.0;
771
772 line_.push(static_cast<SampleType>(x));
773
774 // Control cadence: pedal law → detents → glide → r. `semitones_to_ratio`
775 // is a `pow`, so it is recomputed only when the glide actually moved.
776 const double slewed = glide_.process(
778 if (slewed != semitones_) {
779 semitones_ = slewed;
780 update_ratio();
781 }
782 effective_ratio_ = ratio_;
783 if (drift_depth_ > 0.0)
784 effective_ratio_ *= static_cast<double>(drift_.next());
785 const double r = effective_ratio_;
786
787 // Two taps, half a window apart, each on its own latched window length.
788 const double pa = phase_;
789 const double pb = frac1(phase_ + 0.5);
790 const double tap_a = read_tap(pa * window_a_);
791 const double tap_b = read_tap(pb * window_b_);
792
793 // Eq. 3.4 through the house law. `1 − wA` for the second weight keeps
794 // `wA + wB ≡ 1` EXACT regardless of how `sin` rounds, which is what
795 // bounds the wet leg by the buffer peak.
796 const double s = std::sin(kPi * pa);
797 double w_b = 0.0;
798 double w_a = 0.0;
800 const double wet = static_cast<double>(
801 dc_.process(static_cast<SampleType>(w_a * tap_a + w_b * tap_b)));
802
803 // Advance the sawtooth (Eq. 3.3) and latch any pending window change at
804 // each tap's own zero crossing, where its delay and its weight are both
805 // zero. `window_a_` drives the increment, so tap A is always exactly on
806 // ratio; tap B is only ever off during the half cycle after a change.
807 const double dphi = window_a_ > 0.0 ? (1.0 - r) / window_a_ : 0.0;
808 const double pa_next = frac1(phase_ + dphi);
809 const double pb_next = frac1(pa_next + 0.5);
810 const bool rising = dphi > 0.0;
811 const bool wrapped_a = rising ? (pa_next < pa) : (pa_next > pa);
812 const bool wrapped_b = rising ? (pb_next < pb) : (pb_next > pb);
813 if (wrapped_a) window_a_ = window_samples_;
814 if (wrapped_b) window_b_ = window_samples_;
815 phase_ = pa_next;
816
817 return wet;
818 }
819
820 // ── Composed primitives ───────────────────────────────────────────────
821 DelayLineT<SampleType> line_{};
822 DcBlocker<SampleType> dc_{};
823 ConstantTimeSlewLimiterT<double> glide_{};
824 DriftT<double> drift_{};
825
826 // ── Configuration ─────────────────────────────────────────────────────
827 double sample_rate_ = kDefaultSampleRate;
831
832 double pedal_ = 0.0;
833 double shift_semitones_ = 0.0;
834 double heel_semis_ = kHeelSemisDefault;
835 double toe_semis_ = kToeSemisDefault;
836 double interval_a_semis_ = kIntervalASemisDefault;
837 double interval_b_semis_ = kIntervalBSemisDefault;
838 double detune_cents_ = kDetuneCentsDefault;
839 double dive_floor_semis_ = kDiveFloorSemisDefault;
840 double window_ms_ = kWindowMsDefault;
841 double mix_ = 1.0;
842 double drift_depth_ = 0.0;
843 bool detents_ = false;
844
845 // ── Running state ─────────────────────────────────────────────────────
846 double window_samples_ = 0.0;
847 double window_a_ = 0.0;
848 double window_b_ = 0.0;
849 double max_read_samples_ = static_cast<double>(kInterpMargin);
850 double phase_ = 0.0;
851 double semitones_ = 0.0;
852 double ratio_ = 1.0;
853 double effective_ratio_ = 1.0;
854
855 // Schmitt state for the detent capture. Mutable because `target_semitones`
856 // is the natural place to read the law from and callers expect it to be a
857 // query; the state it carries is the hysteresis memory, not a result.
858 mutable bool detent_held_ = false;
859 mutable int detent_index_ = 0;
860};
861
864
865} // namespace pulp::signal
Time-domain ratio pitch shifter: dual-tap crossfaded modulated delay.
Definition pitch_shifter.hpp:211
double window_samples() const
Window length in samples at the prepared rate.
Definition pitch_shifter.hpp:630
void set_shift_semitones(double semitones)
Direct shift target, used when shift_source() == ShiftSource::direct.
Definition pitch_shifter.hpp:467
static constexpr double kWindowMsMin
Definition pitch_shifter.hpp:223
static constexpr int kDetentCount
Definition pitch_shifter.hpp:267
double pedal() const
Definition pitch_shifter.hpp:478
void set_interp(PitchInterp interp)
Definition pitch_shifter.hpp:553
static constexpr bool mode_uses_detents(PedalMode mode)
Whether a mode's targets are intervals the detent table can express.
Definition pitch_shifter.hpp:389
double mix() const
Definition pitch_shifter.hpp:542
void set_shift_source(ShiftSource source)
Selects the pedal law or the direct semitone target. Default pedal.
Definition pitch_shifter.hpp:462
double glide_up_ms() const
Definition pitch_shifter.hpp:534
static constexpr double kDcBlockerCornerHz
Definition pitch_shifter.hpp:317
void set_detents(bool on)
Snap the pedal-law target table to musical intervals.
Definition pitch_shifter.hpp:547
static constexpr double kShiftSemisMax
Definition pitch_shifter.hpp:256
double window_ms() const
Definition pitch_shifter.hpp:524
static constexpr double kWindowMsDefault
Definition pitch_shifter.hpp:222
static constexpr double kDriftMaxDepthPercent
Definition pitch_shifter.hpp:298
static constexpr std::uint32_t kDriftSeed
Definition pitch_shifter.hpp:302
PitchInterp interp() const
Definition pitch_shifter.hpp:554
static constexpr double kDiveFloorSemisMin
Definition pitch_shifter.hpp:240
static constexpr double kDetentReleaseRatio
Definition pitch_shifter.hpp:281
double pedal_law(double e01) const
The §5 pedal law alone, before detents and before the glide.
Definition pitch_shifter.hpp:588
static constexpr double kGlideMsMax
Definition pitch_shifter.hpp:230
double current_semitones() const
The post-slew shift the block is applying right now, before drift.
Definition pitch_shifter.hpp:604
static constexpr double kGlideMsMin
Definition pitch_shifter.hpp:229
static constexpr double default_mix_for(PedalMode mode)
Per-mode conventional dry/wet default (§6).
Definition pitch_shifter.hpp:370
static constexpr double kDetuneCentsMax
Definition pitch_shifter.hpp:235
double target_semitones() const
The pre-slew target in semitones: the pedal law plus detents, or the direct value.
Definition pitch_shifter.hpp:579
double drift_depth() const
Definition pitch_shifter.hpp:564
bool detents() const
Definition pitch_shifter.hpp:551
static constexpr double kDiveFloorSemisDefault
Definition pitch_shifter.hpp:239
double glide_down_ms() const
Definition pitch_shifter.hpp:535
static constexpr double kDetentReleaseLimitSemis
Definition pitch_shifter.hpp:287
static constexpr double kIntervalASemisDefault
Definition pitch_shifter.hpp:248
SampleType process_wet(SampleType x)
The shifted leg alone, with no dry mixed in — for callers summing several shifted voices themselves a...
Definition pitch_shifter.hpp:660
static constexpr double kWindowMsMax
Definition pitch_shifter.hpp:224
ShiftSource shift_source() const
Definition pitch_shifter.hpp:463
SampleType process(SampleType x)
One sample through the dry/wet mix.
Definition pitch_shifter.hpp:645
double dc_blocker_magnitude_peak() const
The DC blocker's MAGNITUDE-response peak, 2/(1+p) at Nyquist.
Definition pitch_shifter.hpp:362
void set_dive_floor_semis(double semitones)
Definition pitch_shifter.hpp:504
void set_drift_depth(double d01)
Optional seeded pitch drift, 0 (default) .
Definition pitch_shifter.hpp:559
PitchShifterT()
Definition pitch_shifter.hpp:395
static constexpr double kDcBlockerPeakGain
Definition pitch_shifter.hpp:353
void prepare(double sample_rate)
Sizes the delay line for kWindowMsMax plus the stencil guard and prepares the composed primitives.
Definition pitch_shifter.hpp:413
static constexpr double kClickBound
Definition pitch_shifter.hpp:326
static constexpr double kShiftSemisMin
Definition pitch_shifter.hpp:255
static constexpr double kDiveFloorSemisMax
Definition pitch_shifter.hpp:241
void set_targets(double heel, double toe)
Whammy/dive endpoints in semitones.
Definition pitch_shifter.hpp:486
void set_pedal_mode(PedalMode mode)
Picks the pedal LAW only.
Definition pitch_shifter.hpp:482
void discard_history() noexcept
Constant-time counterpart to reset() for hostile audio input.
Definition pitch_shifter.hpp:448
static constexpr double kDetentTable[]
Definition pitch_shifter.hpp:265
static constexpr int kInterpMargin
Definition pitch_shifter.hpp:293
static constexpr double kDetentSnapSemis
Definition pitch_shifter.hpp:274
static constexpr double kWorstCaseGain
Definition pitch_shifter.hpp:336
static constexpr double kSemitonesCeil
Definition pitch_shifter.hpp:261
void set_glide_ms(double up_ms, double down_ms)
Independent rise/fall portamento on the SEMITONE signal, so a glide is perceptually even.
Definition pitch_shifter.hpp:529
void snap_to_target()
Jumps the glide to its current target with no portamento — what a harmony voice wants on a note-on,...
Definition pitch_shifter.hpp:569
void set_window_ms(double ms)
Crossfade window in ms.
Definition pitch_shifter.hpp:519
static constexpr double kIntervalBSemisDefault
Definition pitch_shifter.hpp:249
PedalMode pedal_mode() const
Definition pitch_shifter.hpp:483
int latency_samples() const
Honest processing throughput, not phase-aligned PDC: the delay sweeps [0, L], so the crossfaded outpu...
Definition pitch_shifter.hpp:636
void set_detune_cents(double cents)
Definition pitch_shifter.hpp:499
static constexpr double kDetuneCentsDefault
Definition pitch_shifter.hpp:234
double shift_semitones() const
Definition pitch_shifter.hpp:471
double current_ratio() const
The ratio the taps ACTUALLY ramped at on the last processed sample, drift included.
Definition pitch_shifter.hpp:611
double tap_phase_pi(double hz) const
q = ψ/π for an input partial at hz: the half-window separation of the two taps in half-cycles of that...
Definition pitch_shifter.hpp:625
static constexpr double kHeelSemisDefault
Definition pitch_shifter.hpp:246
static constexpr double kDefaultSampleRate
Definition pitch_shifter.hpp:365
void set_mix(double wet01)
Equal-power dry↔wet. 0 is the untouched input; 1 is wet only.
Definition pitch_shifter.hpp:538
void set_pedal(double e01)
Expression position, heel = 0 to toe = 1.
Definition pitch_shifter.hpp:474
static constexpr double kToeSemisDefault
Definition pitch_shifter.hpp:247
double warble_hz() const
f_warble for the current ratio and window (Eq.
Definition pitch_shifter.hpp:615
static constexpr double kGlideMsDefault
Definition pitch_shifter.hpp:228
void reset()
Zeroes the line, the phase, and the filter state, and rewinds the drift stream (series law 2).
Definition pitch_shifter.hpp:436
void set_harmony(double a, double b)
Harmony A (heel) and B (toe) intervals in semitones.
Definition pitch_shifter.hpp:493
static constexpr double kSemitonesFloor
Definition pitch_shifter.hpp:260
The one shared old→new crossfade gain law for the SIGNAL side.
High-quality sample interpolation for delay lines and resampling.
@ d
Definition pitch.hpp:22
constexpr double kTwoPi
Definition lfo_sampling.hpp:77
T semitones_to_ratio(T semitones)
Semitones → frequency ratio. +12 → 2, −12 → 0.5.
Definition units.hpp:105
constexpr double kCentsPerSemitone
Definition units.hpp:62
T ms_to_samples(T ms, T sample_rate)
Milliseconds → samples at sample_rate Hz.
Definition units.hpp:147
Definition additive_bank.hpp:218
PitchShifterT< float > PitchShifter
Definition pitch_shifter.hpp:862
@ linear
Definition mod_tools.hpp:106
@ rising
Definition rise_fall_generator.hpp:19
@ release
Definition envelope.hpp:62
PitchShifterT< double > PitchShifter64
Definition pitch_shifter.hpp:863
PitchInterp
Fractional-delay read quality for the two taps.
Definition pitch_shifter.hpp:180
@ cubic
4-point Lagrange. Brighter tap, ~4× the read cost.
Definition pitch_shifter.hpp:182
@ linear
2-point. Dattorro's recommendation for modulated delays.
Definition pitch_shifter.hpp:181
@ EqualPower
(cos(u·π/2), sin(u·π/2)) — power sum == 1.
Definition crossfade.hpp:43
@ EqualGain
(1−u, u) — amplitude sum == 1.
Definition crossfade.hpp:42
@ a
Definition formant_filter_bank.hpp:20
@ i
Definition formant_filter_bank.hpp:20
@ e
Definition formant_filter_bank.hpp:20
@ nearest
Snap to the nearest in-scale degree in either direction.
Definition harmony_engine.hpp:175
@ linear
2-point. Cheapest; audible imaging when pitching up.
Definition granular.hpp:133
ShiftSource
Which control feeds the shift target.
Definition pitch_shifter.hpp:192
@ pedal
set_pedal through the §5 law. The default.
Definition pitch_shifter.hpp:193
@ direct
set_shift_semitones. Detents and the pedal law are bypassed.
Definition pitch_shifter.hpp:194
void crossfade_gains(T u, CrossfadeGainLaw law, T &old_gain, T &new_gain) noexcept
Old/new blend gains for an ALREADY-SHAPED progress u (the caller applies any smoothstep/clamp first).
Definition crossfade.hpp:61
PedalMode
How the expression pedal maps to a shift target.
Definition pitch_shifter.hpp:168
@ detune
Sub-semitone offset scaled by the pedal. Dry on.
Definition pitch_shifter.hpp:171
@ whammy
Continuous heel→toe bend. Dry conventionally muted.
Definition pitch_shifter.hpp:169
@ dive
Dive-bomb toward a floor near (but never at) r = 0.
Definition pitch_shifter.hpp:172
@ harmony
Morph between two fixed intervals. Dry conventionally on.
Definition pitch_shifter.hpp:170
@ s
Definition input_events.hpp:41
@ r
Definition input_events.hpp:41
@ p
Definition input_events.hpp:40
@ v
Definition input_events.hpp:41
Deterministic randomness for modulation: a counter-free PRNG, a Gaussian draw, a stateless purpose-ke...
Compatibility include for the canonical modulation-toolkit slew limiter and sample-and-hold implement...
static SampleType linear(SampleType frac, SampleType y0, SampleType y1)
Linear interpolation between two samples.
Definition interpolator.hpp:33
static SampleType lagrange(SampleType frac, SampleType ym1, SampleType y0, SampleType y1, SampleType y2)
4-point Lagrange interpolation.
Definition interpolator.hpp:60
The real-unit vocabulary shared by every DSP block in the catalog.