Spectral Processor Normalisation - what am I doing wrong?

Hi,

Here’s
specprocnorrm_mr.ny (904 Bytes)
a simple spectral processor, based on a comment in this forum. I’m having trouble getting the ‘normalize’ function to work. I’m sure I’m doing something wrong, but can’t see it. Any ideas, anyone?

Thanks in advance.

Martin

I often find it easiest to initially develop plug-ins for mono only, then once it is working, expand it to also work with stereo. Taking that approach, let’s look at your “normalize” function:

You have:

(defun normalize (signal)
(setf x (if (arrayp signal)
(max (peak (aref signal 0) ny:all) (peak (aref signal 1) ny:all))
(peak signal ny:all)))
(scale (/ norm-level x) signal))

which can be read more easily with appropriate indentation:

(defun normalize (signal)
  (setf x (if (arrayp signal)
              (max (peak (aref signal 0) ny:all)
                   (peak (aref signal 1) ny:all))
              (peak signal ny:all)))
  (scale (/ norm-level x) signal))

(if (arrayp signal)” is saying; “if the signal is an array [stereo sounds are arrays], then do the first thing, otherwise [signal is mono] do something else

So for a mono signal, we could rewrite it as:

(defun normalize (signal)
  (setf x (peak signal ny:all))
  (scale (/ norm-level x) signal))

So “signal” is the mono signal that we pass to the function, “x” is the peak level of the signal, but what is “norm-level”?
The reason that your "normalize"function fails, is because you have not defined what “norm-level” is.
This can be seen from the first line of the debug output (run the effect with the Debug button to see the debug output):

error: unbound variable - NORM-LEVEL
if continued: try evaluating symbol again
Function: #<Closure-NORMALIZE: #42eebc0>
Arguments:
  #<Sound: #407c758>
Function: #<FSubr-IF: #42f6698>
Arguments:
  (ARRAYP S)
  (VECTOR (NORMALIZE (LOWPASS8 (MULT MUMU (HIGHPASS8 (AREF S 1) 30) (HZOSC MIRR)) FREQ)) (NORMALIZE (LOWPASS8 (MULT MUMU (HIGHPASS8 (AREF S 0) 30) (HZOSC MIRR)) FREQ)))
  (NORMALIZE (LOWPASS8 (MULT MUMU (HIGHPASS8 S 30) (HZOSC MIRR)) FREQ))
1>

How to do it correctly:

Let’s say that we have a signal with a peak level of “0.8” [such a signal may be easily generated with “Generate menu > Tone” or “Generate > Noise”].
We can calculate the peak level of the sound like this:

(setf peak-level (peak <sound> ny:all))

where:
“” is the mono audio that we are processing,
“peak” is the function that we are using http://www.cs.cmu.edu/~rbd/doc/nyquist/part8.html#index727
“peak-level” is the variable that takes the value returned by the “peak” function.

If we are using the new plug-in syntax (default in all recent versions of Audacity), then for a mono track, the variable “track” represents the selected audio, so we can write:

(setf peak-level (peak *track* ny:all))

Running this in the Nyquist Prompt (“legacy syntax” should be disabled / not selected), then for our generated tone, we should see a return value very close to 0.8.

Now let’s say that we want to scale this to a peak amplitude of 0.5.
To do this, we need to scale the audio by a factor of: “ / ”

The “scale” function multiplies each sample value by the specified amount, so we could write:

(scale *track* (/ 0.5 0.8))

or identically:

(mult *track* (/ 0.5 0.8))

Running the above code, you should see that the amplitude is now 0.5.

Putting this into a function, we can write the general case:

(defun normalize (signal target)
  (setf old-level (peak signal ny:all))
  (mult signal (/ target old-level)))

and to use this function, we need to “call the function”, and pass it two parameters; the signal, and the target level. For example:

(setf target-amplitude 0.5)

(defun normalize (signal target)
  (setf old-level (peak signal ny:all))
  (mult signal (/ target old-level)))

(normalize *track* target-amplitude)

or we could set the target level with a slider:

(setf target-amplitude 0.5)

(defun normalize (signal target)
  (setf old-level (peak signal ny:all))
  (mult signal (/ target old-level)))

(normalize *track* target-amplitude)

Thanks for this detailed and informative answer. I now see that I copied and pasted the function without including the ‘norm-level’ control from earlier in the code. Doh! I need that Captain Piccard ‘Slap Head’ image… Code in haste, repent at leisure.