Nyquist is a separate thing from Audacity.
Audacity is an application that is (mostly) written in C++.
Nyquist is a programming language (based on XLISP).
Audacity usually handles sounds by loading a sequence of sample values into an array buffer and passing that buffer to the processing code, and on success, passing another buffer. The audio data is PCM (http://en.wikipedia.org/wiki/Pulse-code_modulation), meaning that it is a series of values that represent the amplitude at intervals of 1/sample-rate.
For processing, Audacity always uses 32-bit float values for the samples, where 0 dB (full-scale) is the numeric range +1.0 to -1.0.
Nyquist usually handles sounds as a specific data type. Just as you may have floats, integers, characters, strings… you can have “sounds”.
It is not normally necessary to worry about the internal structure of a “sound”, but since you asked:
Sounds have 5 components:
- sample rate
- signal start time
- signal stop time
- logical stop time
Multichannel sounds are represented as an array of sounds, so a stereo sound is an array with two elements - and each element is a sound (one for each channel).
Nyquist receives the sound from Audacity as the value of the global variable track (older versions of Audacity used the global variable S).
In Nyquist, sounds can be converted to individual sample values, but this is a horribly inefficient way of working. Nyquist is an interpreted language and looping through millions of sample values will be (as Robert indicated) extremely slow.
A faster way (though still not ideal) way to handle sounds is to fetch sample values as an array (grab a whole load of samples in one go rather than sample by sample).
The most efficient way to work with sounds is to work with the sound data type directly. Many of Nyquist’s functions operate on “sounds”.
Nyquist has several loop structures.
Two of the most common are:
(Note that these are standard XLISP commands)
XLISP, (hence Nyquist) also have loop structures that work with “lists”
(all can be found in this index: http://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-ref/xlisp-ref-index.htm)
XLISP (hence Nyquist) make much use of lists (“LISP” derives from “LISt Processing”) and lists are powerful and versatile structures in all LISP based languages (http://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-man/xlisp-man-016.htm)
Nyquist also has additional loop structures for dealing with sounds and behaviours, including
SND-FETCH and SND-FETCH-ARRAY work sequentially, so can be easily incorporated into loop structures http://www.cs.cmu.edu/~rbd/doc/nyquist/part8.html#index263
When working with “sounds” (sound objects), looping through the samples is handled internally by the Nyquist functions (which are written in highly optimised C code). This is very much faster than looping through sample values in interpreted LISP loops.
Here’s an example for looping through samples and halving the value of each sample.
It is limited to a maximum of 10,000 samples so that it does not hang your machine for hours.
This code can be run in the Nyquist Prompt effect (Audacity 2.1.0) and requires a mono track selection:
(it will probably take about 30 seconds to process 10,000 samples)
(let* ((out (s-rest 0)) ;initialise a 'null' sound
(sr *sound-srate*) ;the sample rate
(a1 (make-array 1))) ;an aray with one element
; fetch sample values one at a time
(do* ((val (snd-fetch *track*)(snd-fetch *track*))
(count 0 (1+ count))
(t0 0 (/ count sr))) ;advance time from 0 by sample periods
;until 10000 or no more samples, then return 'out'
((or (= count 10000)(not val)) out)
;; body of loop
(setf (aref a1 0) (* val 0.5))
;; push the sample onto end of the 'out' sound
(snd-from-array t0 sr a1)))))
Here’s an example where we grab an array full of samples. This is a lot faster and can be safely used on mono tracks several minutes long:
(let ((ln (truncate len)) ;length of the selection in samples
(out (s-rest 0)) ;initialise a 'null' sound
(max-step 10000)) ;max size of array
(do* ((processed 0 (+ processed step))
(step (min ln 100000)(min ln 100000))
(ln (- ln step)(- ln step))
(t0 0 (/ processed *sound-srate*)))
;keep going 'till step size = 0, then return 'out'
((= step 0) out)
;grab an array of sample values
(setf snd-array (snd-fetch-array *track* step step))
(dotimes (j step snd-array)
;multiply array values by 0.5
(setf (aref snd-array j)(* 0.5 (aref snd-array j))))
;;convert array to sound and add to 'out'
(snd-from-array t0 *sound-srate* snd-array)))))
and the best way to halve the value of each sample. This uses the sound object directly and lets Nyquist handle the looping in C code.
This also works on stereo tracks:
(mult *track* 0.5)
Much of the art of Nyquist programming is working out how to abstract a DSP idea so that it can be accomplished efficiently using sound objects and efficient higher level functions rather than literal and laborious sample by sample processing.