|
| | HarmonyEngineT () |
| void | prepare (double sample_rate) |
| | Sizes the tracker, the alignment delay, and both shifters.
|
| void | reset () |
| void | discard_history () noexcept |
| | Constant-time audio fault recovery.
|
| void | set_key (int root_pitch_class) |
| void | set_scale (ScaleType scale) |
| void | set_off_scale_policy (OffScalePolicy policy) |
| void | set_voice_interval (int voice, int steps) |
| | Voice interval in SCALE STEPS, not semitones.
|
| int | voice_interval (int voice) const |
| void | set_voice_detune_cents (int voice, double cents) |
| double | voice_detune_cents (int voice) const |
| void | set_voice_level_db (int voice, double db) |
| double | voice_level_db (int voice) const |
| void | set_voice_enabled (int voice, bool on) |
| bool | voice_enabled (int voice) const |
| void | set_dry_level_db (double db) |
| double | dry_level_db () const |
| void | set_glide_ms (double ms) |
| | Cents-domain portamento time.
|
| double | glide_ms () const |
| void | set_humanize_cents (double cents) |
| | Seeded humanise depth in cents, 1σ.
|
| double | humanize_cents () const |
| void | set_crossfade_ms (double ms) |
| | The shifter's crossfade window, shared by both voices.
|
| double | crossfade_ms () const |
| void | set_interp (PitchInterp interp) |
| int | latency_samples () const |
| | The delay the whole block imposes, and the delay the dry path is given.
|
| int | tracker_latency_samples () const |
| | The tracker window W, on its own.
|
| int | shifter_latency_samples () const |
| | The shifter's own throughput delay, on its own.
|
| double | tracked_f0_hz () const |
| bool | voiced () const |
| const DiatonicMapping & | voice_mapping (int voice) const |
| | The mapping the engine last computed for a voice, including whether the input was off-scale or the shift hit the octave clamp.
|
| double | voice_cents (int voice) const |
| | The voice's post-glide offset in cents, and the ratio derived from it.
|
| double | voice_ratio (int voice) const |
| int | voice_shift_semitones (int voice) const |
| double | mute_gain () const |
| | The wet-mute gate, 0 while unvoiced and 1 while voiced.
|
| const YinTrackerT< SampleType > & | tracker () const |
| const DiatonicMapT< SampleType > & | diatonic_map () const |
| SampleType | process (SampleType x) |
|
| static constexpr int | kMaxVoices = 2 |
| | Voice slots.
|
| static constexpr double | kCrossfadeMsDefault = 20.0 |
| | The shifter's crossfade window.
|
| static constexpr double | kCrossfadeMsMin = 10.0 |
| static constexpr double | kCrossfadeMsMax = 50.0 |
| static constexpr double | kGlideMsDefault = 60.0 |
| | Cents-domain portamento, applied whenever a target moves — a key change, a scale change, an interval change, or the input crossing a scale-degree boundary.
|
| static constexpr double | kGlideMsMax = 500.0 |
| static constexpr double | kVoiceMuteMs = 10.0 |
| | Wet-mute ramp applied when the tracker reports an unvoiced frame, so noise and silence do not get harmonized.
|
| static constexpr double | kDetuneMaxCents = 50.0 |
| | Per-voice detune. [design parameter] default 0, range ±50 cents.
|
| static constexpr double | kHumanizeDefault = 0.0 |
| | Optional seeded humanise drift, as a 1σ depth in cents.
|
| static constexpr double | kHumanizeMaxCents = 15.0 |
| static constexpr int | kV1IntervalDefault = 2 |
| | Voice interval defaults, in scale steps: a 3rd above and a 5th above.
|
| static constexpr int | kV2IntervalDefault = 4 |
| static constexpr double | kLevelMinDb = -60.0 |
| | Level range for the dry and voice mixers, in dB.
|
| static constexpr double | kLevelMaxDb = 6.0 |
| static constexpr double | kLevelMaxLinear = 1.9952623149688795 |
| static constexpr double | kWorstCaseGain |
| | The closed-form feed-forward bound at the declared parameter ceiling.
|
| static constexpr double | kMuteFloor = 1e-5 |
| | Below this the mute gate is treated as fully closed, so an unvoiced passage costs nothing and cannot leak a denormal tail.
|
template<typename SampleType = float>
class pulp::signal::HarmonyEngineT< SampleType >
The diatonic harmonizer: YinTrackerT + DiatonicMapT + one PitchShifterT per voice.
RT contract: as the file banner. prepare allocates; nothing else does.
template<typename SampleType = float>
The delay the whole block imposes, and the delay the dry path is given.
This is max(W, shifter_latency) rather than the tracker's W alone, and the difference matters: the wet legs have their OWN throughput delay through the shifter, so delaying the dry by W and leaving the wet alone would leave the two misaligned by |W − shifter_latency| — 720 samples, 15 ms, at the shipped defaults. Instead the shifters are fed from a pre-delay of latency_samples() − shifter_latency so BOTH legs come out at latency_samples(). At the defaults the max is W, so the reported number is the tracker window exactly, as specified.
A ratio shifter's output is not a delayed copy of its input, so wet alignment is exact only in the sense that the two legs' window centres coincide — which is the strongest statement resampling permits.
template<typename SampleType = float>
Cents-domain portamento, applied whenever a target moves — a key change, a scale change, an interval change, or the input crossing a scale-degree boundary.
[design parameter] default 60 ms, range 0 .. 500.
template<typename SampleType = float>
Initial value:=
static constexpr double kLevelMaxLinear
Definition harmony_engine.hpp:418
static constexpr int kMaxVoices
Voice slots.
Definition harmony_engine.hpp:380
static constexpr double kDcBlockerPeakGain
The DC blocker's TIME-DOMAIN peak gain: the L1 norm of its impulse response, which is exactly 2 for e...
Definition pitch_shifter.hpp:353
The closed-form feed-forward bound at the declared parameter ceiling.
There is no feedback path, so this is arithmetic rather than an invariant to discover: 10^(6/20) · (dry + 2·voice·dc_l1).
This was 3.0, from "unity dry plus two unity voices" — each wet voice bounded by 1 because the tap crossfade is convex. But each voice's wet leg passes a DC blocker, whose worst-case SAMPLE gain is the L1 norm of its impulse response, exactly 2 — not the 2/(1+p) = 1.000327 magnitude peak the derivation used. Inherited from PitchShifterT, along with the error: a magnitude-response peak bounds a steady sinusoid, not a single sample. Understating a registry headroom figure by 4.4 dB is the expensive direction.