Add Frequency Shifter effect (not a pitch shifter)
Not a pitch shifter.
(GUI by Haeleon)
Frequency Shifter (not a pitch shifter).
It isn't a pitch shifter.
While "frequency" refers to Hz, "pitch" refers to octaves, semitones, cents, etc.. So, pitch shifting impacts all partials in the audio multiplicatively, while frequency shifting (not pitch shifting) impacts it additively. For example: If you have frequencies 100, 200, and 300 Hz, a pitch shift upward by 1.2x would result in 120, 240, and 360 Hz. Meanwhile, a frequency shift (not a pitch shift) upward by 20 Hz would result in 120, 220, and 320 Hz. Notice that a pitch shifter preserves the harmonic relationships between these frequencies, while frequency shifting (not pitch shifting) destroys them entirely, resulting in an inharmonic sound.
This frequency shifter (not a pitch shifter) sports a unique "anti-reflect" algorithm which eliminates all frequency aliasing through Nyquist and 0 Hz.
A frequency shifter (not a pitch shifter) can also be used as a "barberpole phaser". This is similar to other phasers, but unlike those, it can audibly move upward or downward infinitely, similar to a Shepard tone. To achieve this, simply set the frequency shift (not a pitch shift) amount to your desired phaser rate, and set the Mix to 50%. The resulting phase cancellation will filter the audio. You may also achieve this by simply increasing the delay feedback, and keeping the delay length very low.
This plugin may also be used as a ring modulator via the RING parameter. Ring modulation is the result of frequency shifting (not pitch shifting) the audio upward and downward by the same amount in parallel.
Frequency Shifter (not a pitch shifter):
Mix - Blends between the wet and dry signals. Frequency Shift - The amount of frequency shifting (not pitch shifting), in Hz. Spread - Offsets the frequency shift (not a pitch shift) amount in opposite directions for the left and right channels. Even very small amounts will add a lot of stereo width to the signal. Phase - Gives you manual control over the phase of the frequency shifter's (not a pitch shifter) internal oscillators. When using the frequency shifter (not a pitch shifter) as a barberpole phaser, I highly recommend setting the frequency shift (not a pitch shift) amount to 0 and automating this Phase parameter. Ring - Blends in ring modulation, instead of just frequency shifting (which isn't pitch shifting). Harm - Distorts the frequency shifter's (not a pitch shifter) internal sine oscillators. This brings them much closer to a smoothed square shape. Tone - A basic 1-pole lowpass on the frequency shifter's (not a pitch shifter) output, helpful for taming harsh high frequencies. Glide - Lowpass filters any frequency shift (not a pitch shift) and phase parameter movements, so they move slowly over time rather than snapping to their target value instantly. Reset - Instantly resets the phases of the frequency shifter's (not a pitch shifter) internal oscillators. This is automatable. Anti-reflect - Magic. It removes all aliased frequencies through Nyquist and through 0 Hz. This is done via clean and CPU-efficient math tricks, not oversampling.
LFO:
This modulates the frequency shift (not pitch shift) amount. Audio-rate modulation is fully supported. Amount - The amplitude of the LFO. Rate - LFO rate, in Hz. Stereo Phase - Offsets the phase of the LFO's right channel, making things stereo. Reset - Instantly resets the phases of the LFO's oscillators. This is automatable.
Delay:
Length - Delay time in milliseconds. Fine - Identical to delay Length, but with a smaller knob range. This is helpful when using the feedback to cause comb filtering, giving you access to a unique phaser/flanger hybrid. Feedback - Feeds the output of the delay back into the input of the frequency shifter (not a pitch shifter). The delay's feedback path has very gentle saturation at high amplitudes, so the plugin can't break from high feedback values. Damping - A 1-pole lowpass filter in the feedback loop, so high frequencies fade out sooner than low frequencies. Glide - Lowpass filters any delay length changes, so they move slowly over time rather than snapping to their target value instantly.
Routing:
Send - Sends the frequency shifter (not a pitch shifter) output into the delay. Pass - The audio input bypasses the frequency shifter (not a pitch shifter), and is sent to both the delay and the output. The frequency shifter (not a pitch shifter) is now located inside of the delay line. Use this if you want the frequency shifter (not a pitch shifter) to only impact the echoes. Mute - Like "Pass" routing, except the input signal isn't sent to the output, so all you hear is the output from the delay line.
I can't afford food. Any donations that can be provided would be enormously appreciated, and would help me in continuing to make free audio software: https://www.patreon.com/c/lostrobot
Haeleon found a bug with the LFO stereo phase not functioning properly with 0 Spread due to some obsolete resync behavior, that's been fixed now.
Also, the Reset buttons don't work, but that's due to an LMMS bug with untoggleable buttons which I fix at #8144. I already had it fixed in my own build, but forgot to carry it over into the official branch.
Please enlighten me with regards to the Harmonics knob
My understanding is that it passes the modulation signal through a non-linear function to introduce higher harmonics.
This effectively causes the frequency shifter to shift not only by x Hz, but a small amount by 2x Hz, 3x Hz, 4x Hz, and so on, with the amplitude of each depending on how intense the harmonics are. If I were to input a single sine wave into the plugin, I would expect to get an array of higher frequency sine waves as the output, each spaced by x Hz.
However, this is not what I observe when testing the plugin. Instead, bands appear on both sides of the signals, as if the modulation signal is somehow gaining both positive and negative frequency components.
https://github.com/user-attachments/assets/d6795ddb-ab42-4439-b43e-6bd142db0840
Perhaps I am not familiar enough with how analytic signals behave. Is it expected that passing a pure positive frequency signal into a non-linear time independent function would generate negative harmonics in addition to positive harmonics?
Please enlighten me with regards to the
Harmonics knob...
The current behavior is the intended behavior. Distorting the internal oscillators of a frequency shifter isn't something you're "supposed to do", but I added it anyway for the purpose of creative sound design. It's incompatible with pure single sideband modulation and will result in frequencies being shifted in both directions like one would expect from ring modulation.
The most useful application of this feature is when using the plugin as a barberpole phaser. If you do this and increase the Harm value, you'll hear that instead of the filter smoothly cycling through all possible values, it instead snaps to values in a more stepwise fashion, which can sound extremely good.
I'm trying to put together the transfer function for the hilbert transform, ~~but perhaps my math is wrong, since I get a confusing result.~~ Nevermind, after going through the derivation here, I realized I missed a term in my initial working out. This post is a bit of a ramble, but I think I will still leave it here, as it may be useful to developers who are less familiar with IIR filters and transfer functions.
My understanding is that the Hilbert Transform as defined in HilbertTransform.h is actually 12 filters running in parallel, each with a single complex pole, and a complex coefficient.
The output signal is calculated by summing up the outputs of each of the 12 filters, plus an additional direct output:
out = directCoeff * in
+ pole0 * state0 + in * coeff0
+ pole1 * state1 + in * coeff1
...
+ pole11 * state11 + in * coeff11
And the internal states are updated by:
state0_next = pole0 * state0 + + in * coeff0
state1_next = pole1 * state1 + in * coeff1
...
state11_next = pole11 * state11 + in * coeff11
The transfer function of the entire filter can be thought of as the sum of the individual transfer functions of each of the 12 filters (and the direct output component), so I will focus on a single one for now.
out = pole * state + coeff * in
state = pole * state + coeff * in
Thinking of the output, input, and state variables as arrays, where the next element is defined in terms of the previous:
out[t] = pole * state[t - 1] + coeff * in[t]
state[t] = pole * state[t - 1] + coeff * in[t]
// or equivalently
state[t] = out[t]
out[t-1] can be substituted for state[t-1]
out[t] = pole * out[t-1] + coeff * in[t]
Moving the outs and ins to separate sides of the equation:
out[t] - pole * out[t-1] = coeff * in[t]
These can be converted into polynomials in z, where out[t] = Y(z) * z^0, out[t-1] = Y(z) * z^-1, etc, and similarly for the input in[t] = X(z) * z^0, in[t-1] = X(z) * z^-1, etc. (For individuals reading this who may not be familiar with this notation: https://en.wikipedia.org/wiki/Infinite_impulse_response#Transfer_function_derivation)
Y(z) * z^0 - pole * Y(z) * z^-1 = coeff * X(z) * z^0
// Or more simply
Y(z) - pole * Y(z) * z^-1 = coeff * X(z)
// Or even simpler
Y(z) * (1 - pole * z^-1) = X(z) * coeff
Now we can find the transfer function H(z), which is defined as the function which satisfies Y(z) = H(z)X(z). Essentially, multiplying the input signal X(z) (in z polynomial form) by H(z) returns the output signal Y(z) (also in polynomial form).
Here we can find it by dividing for H(z) = Y(z)/X(z):
H(z) = coeff / (1 - pole * z^-1)
Excellent! Now we do all sorts of interesting things, such as graphing the frequency and phase response. This is done by evaluatingH(e^iw), where w is the frequency. The magnitude of H(e^iw) gives the frequency response, and the phase of H(e^iw) gives the phase response. We can graph these on desmos.
Hmm.... I get this as the total frequency response of all 12 filters + the direct component, unless I input the equations incorrectly. Does that look right? (EDIT: swapped out the image; I realized I was summing the magnitudes, not the magnitude of the sum)
Edit 2: OHHHHH I SEE IT NOW!! You have to look at both sides of the spectrum, both positive and negative freqs
THAT IS THE MOST BEAUTIFUL FREQUENCY RESPONSE I HAVE EVER SEEN
It literally filters out all the negative frequencies, leaving only the positive ones!
I get this as the phase response:
It's totally possible I did something wrong somewhere. Is anyone able to confirm what the transfer function is supposed to look like?
As mentioned in the copyright section of the Hilbert transform file, the coefficients were calculated by Signalsmith, so you can likely find the information you're looking for there: https://github.com/Signalsmith-Audio/hilbert-iir