This plugin is a combination of the spectral delete plugin as well as the hilbert transform plugin that I also posted recently.
this plugin gives you direct control of the window size of the impulse responses. higher values are smaller window sizes, and lower values larger window sizes. the minimum possible value before the response reaches the 4000000 sample limit is exactly 0.001000000251 (though this is way beyond practical use, and takes a decent amount of time to render).
it also comes in two different modes. the standard mode behaves like any regular frequency shifter.
SSB tuning style (SSB being short for single-sideband) emulates a single-sideband tuner on a shortwave radio. This means that any frequency foldovers that normally occur in standard mode are filtered out with a sinc filter.
As I stated on the Hilbert Transformer plugin page, I’m still mostly inexperienced at creating nyquist plugins, so there is very much a possibility that this plugin isn’t as optimized or well-coded as it could possibly be.
It seems to work very well though. frequency-shifter.ny (3.72 KB)
(defun calc-hilbert-kernel (size)
;; Generate windowed Hilbert kernel impulse
(when (oddp size)
(error "Size of Hilbert filter must be even"))
(let ((ar (make-array (1+ size)))
(do ((i 0 (1+ i))
(j size (1- j))
(x (- halfk) (1+ x)))
((> i j))
(setf val (* (Hilbert x)(blackmanHarris i size)))
(setf (aref ar i) val)
(setf (aref ar j) (* -1 val)))
(dotimes (i size ar)
(setf (aref ar i)(aref ar i)))))
In the line: (x (- halfk) (1+ x))), “halfk” is not defined in the function, so the reader has to search the code to see what it is and where it comes from. (the same issue exists in the spectral delete effect, highlighting the dangers of cut and pasting code).
It turns out that it comes from an unrelated function:
but this is very risky as there’s no easy way to see if halfk has been modified elsewhere in the code, which would create a bug.
Try to minimise the scope of variables. Sometimes it’s impossible to avoid variables with global scope (all “;control” variables have global scope), but where variables do have global scope, treat them as constants so as to avoid unexpected bugs.
It would be better to use sine-table rather than table.
In the current implementation of Nyquist, the default table is sine-table, and that is unlikely to change in future versions, but that’s not to say it won’t ever change, and by explicitly stating sine-table your intention is clear. (explicit is better than implicit
the reason this is happening is because the filter is so wide that it’s ineffectively eliminating the opposite sideband, so it leaves a sort of ringmoddy effect.
Lots of explaining ahead.
You could think of frequency shifting as like a filtered ringmod. When you ringmod something, you are basically shifting two copies of the audio; one up, and one down. You can think of these two copies as halves. Ringmodding a 1000 hz sine wave to 100 hz results in a 900 hz and 1100 hz sine wave combined. using the hilbert transform function, you can filter out one of the copies, so you can get either 900 or 1100 hz depending on whether the input is negative or not. The filter size controls the window size of the hilbert function, so a wider window size means a filter with a sharper cutoff and vice versa. (Though the way that the current filter size control works more from the perspective of the cutoff, where a larger filter size means a smaller window size and a wider cutoff.)
So a really really wide filter size will leave a large chunk of that other half of the audio unfiltered out. It’s a lot easier to understand if you have knowledge about shortwave radio, but it’s a bit hard to explain thoroughly on its own.
here’s an image of four 1 to 22050 hz chirps, the first three frequency shifted down by 5000 hz with increasing filter sizes for each, and the last one being simply ringmodded to 5000 hz.