The basic technique to “Normalize” a sound, is to find the peak level, then scale the sound by an appropriate multiple of the inverse of that value.
For example, to normalize a mono track to 0 dB (0 dB has a linear value of 1.0)

(setq peak-s (peak s NY:ALL))
(scale (/ 1.0 peak-s) s)

The problem with this is that (peak) reads “s” into RAM memory in order to calculate the peak value. If the selection being processed (“s”) is very large, this can exhaust the system memory and cause Audacity to crash very badly. Even if Audacity does not crash, the system may need to pass data from RAM to the hard drive (swap file) which will cause Audacity to become extremely slow and unresponsive to the degree that it appears to have frozen.

The most simple way to avoid this is to limit the number of samples that are read.

NY:ALL is a pre-initialised variable with a value of 1000000000 (1 billion).
This is a big enough number to cope with very long track selections, but depending on the sample format and the amount of available RAM may allow more samples to be read than can be held in RAM.

To protect against running out of RAM, a smaller number could be used, for example;

(setq bignum 1000000)
(setq peak-s (peak s bignum))
(scale (/ 1.0 peak-s) s)

In this case, only the first “bignum” (1 million) samples will be read.
This is fine as long as either the selection is less than 1 million samples duration, or, the maximum peak occurs within the first 1 million samples.

Roger Dannenberg has suggested a workaround that allows long tracks to be normalized (full details here:
The solution is to first calculate the peak value, but set “s” to nil to avoid retaining samples:

;; Get the input signal s, but set s to nil to avoid retaining samples.
(defun get-signal ()
  (let ((local-s s))
     (setf s nil)

;; Compute the peak value of s
(setf peak-value (peak (get-signal) ny:all))


;; Save the peak-value on *scratch* for later use.
;;    Assume that the effect name (normalize-part1) can be safely used
;;    as a property symbol without any conflicts.
(putprop '*scratch* peak-value 'normalize-part1)

;; Return a string:
(format nil "The peak value is: ~A" peak-value)

Because “s” has been set to “nil” the sound passed from Audacity to Nyquist no longer exists, so it is not possible to normalize the sound at this time.
However because we have saved the value as a property of scratch, we can run a second plug-in to read the value and apply the normalization:

(setf peak-value (get '*scratch* 'normalize-part1))

(cond ((floatp peak-value)
       (mult s (/ (db-to-linear db) peak-value)))
       "No peak-value found -- run normalize-part1 to compute the peak value"))

Two important issues here:

  1. Many existing Audacity Nyquist plug-ins will cause Audacity to freeze or crash if long selections are processed due to normalizing and running out of RAM.
  2. The current workaround in Audacity is to use a two pass approach, which is very inconvenient for both plug-in authors and (more importantly) for users.

For users of stand-alone Nyquist there are other approaches that may be used and are described here: These methods are not applicable to the problem in Audacity which is specifically about normalizing the sound “s”.

In the longer term I think that the problem of Normalizing needs to be achieved through the Audacity implementation of Nyquist.
Currently, the sound from a track is passed to Nyquist in the global variable “s”.
The length of “s” (in samples) is set as the global “LEN”.
My proposal is that the peak value is also passed to Nyquist, either as a global variable, or as a property of “s”.
I think the latter solution is more elegant and offers opportunities of further development, for example moving the value of LEN to a property of “s”, or adding the track name as a property, or perhaps even passing envelope points to Nyquist as an array that is a property of “s”.
(see also )

A more versatile and comprehensive solution would be an s-read replacement that can read the relevant audio segment from audacity as suggested by Richard Ash but it sounds like this would be very difficult to implement.

Hi, Steve

As a real Nyquist newbie, I may be missing something obvious. I tried both of the first two examples in the current beta and in 1.2.6 (I should have enough memory for what I want to do). Tried first as a plug in on a stereo file and it didn’t work. Tried again on a stereo file just using ‘Nyquist Prompt’ from the ‘Effects’ menu. Failed again. Then, I inadvertantly tried the routines on a mono file. Worked in both versions from the prompt (although 1.2.6 treated me to a Nyquist output window first with lists of blocks of samples).

What am I missing? Should the codes below work on stereo files? If so, what is the cockpit error that I have in applying it to stereo files.


Those code snippets only work on “sounds” (mono sounds). A stereo sound is not a “sound” but an array containing two sounds.

The “s” variable is a “global variable” in Effect (process) type Nyquist scripts that passes the audio (either a sound in the case of a mono track, or an array of sounds in the case of a stereo track) from the track selection in Audacity to Nyquist.

If “s” is a stereo sound, the sound from the left channel can be accessed with (aref s 0) and the left channel with (aref s 1). (look up “aref” in the Nyquist or XLisp manual).

All of my code examples are written for recent versions of Audacity 1.3.x. Some examples will work in Audacity 1.2.x but Nyquist has had some major updates since then and many features are unavailable in Audacity 1.2.x
For working with Nyquist in Audacity I would highly recommend that you upgrade to Audacity 1.3.13 (you can get it here: )