The per-voice pitch ramp that glides the sampler's read rate across a control block.
More...
|
| | PitchRamp (std::span< const std::int32_t > ramp_exp) noexcept |
| | Creates a ramp over the shared exponential table, which must outlive it.
|
| std::int32_t | increment_of (int value) const noexcept |
| | The 16.16 sampler increment a domain value decodes to.
|
| void | arm (double from_ratio, double to_ratio) noexcept |
| | Arms the ramp to glide from one ratio to another across the coming block.
|
| bool | is_active () const noexcept |
| | Whether the ramp still has ground to cover.
|
| std::int32_t | next_slot () noexcept |
| | Advances one slot and returns the increment that serves the next eight samples.
|
The per-voice pitch ramp that glides the sampler's read rate across a control block.
The engine does not step a voice's pitch once per 10 ms tick. voice_pitch_block_init records the pitch entering the block at voice+0xb8 and the pitch leaving it at +0xbc, and ramp_env_step_eval then walks between them while the block renders, writing a fresh sampler increment every 8 samples. Without that glide a voice whose pitch moves far inside one block reads its wave from a measurably different place forever after: the sub-sample residue the glide leaves behind is frozen into the sampler's phase, which selects a different interpolator row for the rest of the note. Drum tone 1946 is the case that exposed it — its pitch envelope drops 6.671 semitones inside block 0, the engine gains 1.693481 samples across that block, and the resulting phase of 45448 costs about 1.1 dB and a visibly brighter top octave.
The ramp domain. Both endpoints are formed as 0x38000 + (milli_semitones << 9) / 375, so 0x38000 is unity and one octave is exactly 16384 units — the same octave size g_ramp_exp_tbl uses for the TVF's f, which is why both stages share the table.
| void ts::PitchRamp::arm |
( |
double | from_ratio, |
|
|
double | to_ratio ) |
|
inlinenoexcept |
Arms the ramp to glide from one ratio to another across the coming block.
The step is ((to - from) * rate) >> 13 exactly as voice_set_ramp_target_0 forms it, with the engine's maximum rate of 0xfff — which makes the ramp cover the interval in two steps, the behaviour measured on every voice examined. A ramp whose endpoints coincide stays inactive, which is the common case and costs nothing.