public sealed class PitchVocoderEffectPitch shifting in the frequency domain, where a sustained note can survive it.
No guide page documents this yet — the page shows what the code says about itself.
Remarks
Why there are two pitch shifters, stated more carefully than it usually is. PitchShiftEffect reads the same buffer at two rates and crossfades between them. The received wisdom is that this warbles on sustained tones and that a phase vocoder fixes it — and measured against a held sawtooth, that is simply not true: the crossfade put 0.00% of its energy off the harmonic grid where this put 1.45%. A two-tap shifter reading a stationary periodic signal is very nearly exact, because both taps sit on the same repeating waveform.
Where it does fall down is material that does not repeat. Speech, vibrato, a note bending, anything whose partials are moving — there the two taps are reading genuinely different waveforms and the crossfade between them is a smear. That is what this is for, and it is a judgement about how something sounds rather than a number a test can settle, so it is written here rather than asserted somewhere.
So: reach for PitchShiftEffect for steady material, for anything that has to be sample-accurate, and when a window of latency is unacceptable. Reach for this one for voices and for music.
What this does instead. Transform each hop, and for every bin work out what frequency the partial in it actually is — the phase moved further between hops than the bin's centre frequency would explain, and the excess says by how much. Move those partials to their new frequencies, then advance each one's phase by what its new frequency demands rather than by what it happened to arrive with. That last step is the whole technique: it keeps the partials of a note coherent from hop to hop instead of letting them drift apart, which is what "phase vocoder" names.
It costs a window of latency, and there is no version that does not. Nothing can be said about a frequency until enough of a cycle has been seen to measure it. Latency is that, in frames — about 43 ms at the default size and 48 kHz. For dialogue, monsters and music it is nothing; for a sound that has to land on a frame it is disqualifying, and that is when the time-domain one is still the right answer.
Transients still need help, and get it. Phase coherence is what a phase vocoder is for, and it is exactly wrong at an onset — a drum hit or a hard consonant has no steady partials to keep coherent, and carrying the previous frame's phase across it smears the attack into the classic watery vocoder sound. So a jump in spectral energy resets the accumulated phase to what actually arrived, which puts the transient back where it was. TransientSensitivity is how big a jump has to be.
Fields and properties (8)
public float SemitonesHow far to shift, in semitones. Twelve is an octave up.
public int FftSizeHow many points each transform covers. A power of two.
public float TransientSensitivityHow much of a jump in energy counts as a transient, as a ratio against the last frame.
public float MixHow much of the shifted signal to mix with the original.
public bool EnabledWhether it does anything. A bypassed effect costs one branch a block.
public float RatioThe shift as a frequency ratio.
public int LatencyHow many frames behind the input the output is.
public IReadOnlyList<string> PropertiesThe knobs this effect will answer to, by name.
Methods (5)
public void Prepare(in AudioFormat format, int maxFrames)Sizes and clears every buffer the effect needs.
public void Process(Span<float> buffer, int frameCount, int channels)Processes a block in place.
public void Reset()Throws away everything the effect remembers about what came before.
public bool TrySetProperty(string name, float value)Sets one of the effect's knobs by name, for automation.
public bool TryGetProperty(string name, out float value)Reads one of the effect's knobs by name.
Used by (1)
- PitchVocoderTestsVixen.Audio.Tests