Audacity Support Forum / Audacity and Nyquist / Nyquist Reference Manual / Nyquist Examples and Tutorials


Steven Jones / Pedro Morales / David R. Sky / Nyquist and Emacs / Audacity Wiki


Changing the Volume of Stereo Tracks Independently


To be able to change the volumes of both channels of a stereo track independently, we first have to split up the stereo track into two mono signals, then process both mono signals independently from each other, and afterwards we need to re-combine the two mono signals to become a stereo signal again, before the sound can be given back to the Audacity stereo track.

This sounds a lot more complicated than the first example but fortunately Nyquist helps us with many build-in functions.


Nyquist Stereo Sounds


From the first example we already know how to change the volume with the 'scale' function, so we only need to find a way how to split and re-combine stereo signals into single mono channels and back to stereo again.

Here the important thing to know is that Nyquist handles sounds with more than one channel as arrays. A stereo sound in Nyquist is represented as an array with two 'slots' [also called 'fields' in other programming languages]. If a sound from an Audacity stereo track is given to Nyquist, the 's' variable is an array.

The Nyquist function to test wether a variable is an array or not is:

Some simple tests with 'arrayp'

Load a mono or a stereo track into Audacity or create a track with any of the generators from the Audacity 'Generate' menu, then mark the whole track or only a part part of it, and open the 'Nyquist Prompt' from the Audacity 'Effect' menu. Into the text field you type:

(print (arrayp s))

Important: Please take care to type the correct ordering of opening and closing parens, otherwise the Nyquist interpreter will not be able to understand your code. The parens tell the interpreter which function comes first. The function in the innermost parens in Lisp ALWAYS gets computed first. So this way the return value from (arrayp s) will be given to the 'print' function, which in turn will print the result to the Nyquist 'debug' window.

Important: In the 'Nyquist Prompt' window, do NOT klick the 'OK' button, klick the 'Debug' button instead, otherwise you won't be able to see the result. This way you will simultaneously learn how to use the Nyquist debugging tools.

After clicking the 'Debug' button, first a window 'Nyquist did not return a sound' [or similar] appears, were you just klick 'OK'. Then, directly afterwards, a second window 'Nyquist output' [the debugger window] appears with the printed result of the 'arrayp' function:

T
  -  meaning 'true', the sound is an array [= a stereo sound]
NIL
  -  meaning 'false', the sound is NOT an array [= a mono sound]

Now that we know how to find out wether the 's' variable contains a mono or stereo sound we can use 'arrayp' in an if-then-else construct. In contrast to most other programming languages in Lisp there is only an 'if' function, but no 'then' and 'else' keywords, instead the ordering what to compute in which case is done with the parentheses.

We can use this to write Nyquist code for handling mono and stereo signals automatically like this:

(if (arrayp s)
    (stereo-function)
  (mono-function))

Note: Here you can see that Lisp code usually does not get read and understood by humans with counting the ordering of the parens, but by the different indentation of the lines. It is also very helpful to use an editor that is capable to 'match' parens. E.g. if you move the cursor to an opening or closing paren, the other related opening or closing paren will be automatically highlighted. This will prevent you from going nuts with counting.


Splitting and Re-combining Stereo Signals


Now that we know how the principles of how to recognize and handle mono and stereo signals we still need functions to split and re-combine stereo arrays.

Note: To keep the code simple, the following examples only work with Audacity stereo tracks, with mono tracks you will get Nyquist errors ['Nyquist did not return a sound', etc.] Click 'Debug' instead of 'OK' in the 'Nyquist prompt' window if you want to read the detailed Nyquist error messages. At the end we will show how to avoid such errors.

Splitting a stereo signal

The Nyquist function to reference single slots of arrays is:

where the first slot of the array is index 0 [zero].

With an Audacity stereo sound, the slot with the index 0 is always the left channel and the slot with the index 1 is always the right channel:

(aref s 0)  ; left channel
(aref s 1)  ; right channel

Note: everything after the first semicolon until the end of the line is a comment and therefore ignored by Nyquist.

Re-combining two mono signals

Now we still need a function to re-combine both mono channels back into a stereo array. Therefore the Nyquist 'vector' function is very handy:

A 'vector' is a one-dimensional array. The 'vector' function creates an array with as many slots as 'expressions' are given as arguments. If we give two sounds as arguments [the two mono channels] we will get back an array of two sounds, what in Nyquist equals to a stereo sound.

The 'stereo to mono and back again' code in Nyquist would look like this:

(vector
  (aref s 0)   ; left channel
  (aref s 1))  ; right channel

Now remember how we changed the volume in the first example:

(scale 0.5 s)

We now take advantage of one of the most important principles of Lisp, always to compute the innermost parens first. Look again at the 'vector' code from above. All you have to do is to replace the two innermost occurrences of 's' [the arguments to 'aref'] with the code from the first example:

(vector
  (aref (scale 0.5 s) 0)   ; left channel
  (aref (scale 0.5 s) 1))  ; right channel
The result of course still makes not much difference to the first example, but now you can write:
(vector
  (aref (scale 0.5 s) 0)   ; left channel
  (aref (scale 2.0 s) 1))  ; right channel

Now the left channel will be returned with half the volume while the right channel will be returned with the double volume. This way now both channels can be amplified or attenuated independently from each other.


Handling Mono and Stereo Tracks automatically


We still have the problem that our stereo function raises errors with mono tracks. Some chapters above we had learned the principles how to recognize and handle mono and stereo tracks automatically:

(if (arrayp s)
    (stereo-function)
  (mono-function))

In the next version we will to extend our stereo function with an 'arrayp' if-thep-else construct and a window with an error message, which gets displayed with mono tracks:

(if (arrayp s)
    (vector
      (aref (scale 0.5 s) 0)   ; left channel
      (aref (scale 2.0 s) 1))  ; right channel
  "This function only works with stereo tracks.")  ; error message

Note: If Nyquist gives back a string instead of a sound to Audacity, the 'Nyquist did not return a sound' window will be replaced by a window displaying the Nyquist string.

It is also possible to give back the unmodified sound in the 's' variable, so no error will happen and no window will be displayed, Audacity will just re-insert the unmodified sound at the same place where it came from:

(if (arrayp s)
    (vector
      (aref (scale 0.5 s) 0)   ; left channel
      (aref (scale 2.0 s) 1))  ; right channel
  s)  ; unmodified sound

  Back to top


Steven Jones / Pedro Morales / David R. Sky / Nyquist and Emacs / Audacity Wiki


Audacity Support Forum / Audacity and Nyquist / Nyquist Reference Manual / Nyquist Examples and Tutorials