Frequency Shifter

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)

The code looks generally fine, though as you say it’s not optimised, but does work very nicely. :ugeek:

As a matter of coding style, Lispers generally prefer to avoid long lines. For example, rather than:

(if (and (= filtering 1) (> shift 0)) (setf f0 (/ (- (/ *sound-srate* 2) shift) *sound-srate*)) (setf f0 nil))
(if (and (= filtering 1) (< shift 0)) (setf f1 (/ (abs shift) *sound-srate*)) (setf f1 nil))
(let* ((env (truncate len)))
...

you could do something like:

(let ((env (truncate len))
      (half-sr (/ *sound-srate* 2))
      f0 f1)
  (when (= filtering 1)
    (cond
      ((> shift 0) (setf f0 (/ (- half-sr shift) *sound-srate*)))
      ((< shift 0) (setf f1 (/ (abs shift) *sound-srate*)))))
...

But there’s some nasty spaghetti code. For example, when reading the code, working out what “mode” is (highlighted in green) requires jumping all over the code…

spaghetti.png

Also here:

(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)))
        val)
    (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:

(defun dofilter (cf bw type)
  ;; type: 0 (low pass) or 1 (highpass)
  ;; Calculate kernel length (must be even)
  ;; http://www.dspguide.com/ch16/2.htm
  (setf klength (/ 4.0 bw))
  (setf halfk (round (/ klength 2)))
  ...

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.

Bug:

(setf orig (mult (hzosc (abs shift) *table* (mult (/ (abs shift) shift) -90)) sound))

When “shift” = 0, this gives a divide by zero error.

I’ve not yet figured out why, but I’m seeing strange results when shift is negative and filter size is large.
Example:
Apply shift = -100, Filter size = 100, to a 1000 Hz sine wave produces this:

spectrum.png
Any idea why this is happening?

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 :wink:

Just now seeing this.

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.

also I really appreciate the feedback.
You can very well tell that I’m a novice at this, but you have to start somewhere lol

Can someone tell me what this plugin does? Does it take audio from mid range and move it to bass or treble?