Nyquist

Nyquist [Dannenberg, 1993] is a music representation language that is as a matter of fact not a Music-N language. Nevertheless, as it can be understood as an evolution of the basic Music-N paradigm, we have decided to include it in this category. Nyquist is distributed as Open Source software.

Nyquist can be seen as the natural evolution from Artic, Canon and Fugue. All of them have been designed by Roger B. Dannenberg and use functional programming for describing temporal behavior. Using some general mechanisms composers can create temporal structures such as notes, chords, phrases, trills, and synthesis elements such as granular synthesis, envelopes and vibrato functions. Previous implementations had great limitations that made them unpractical, for instance Canon did not handle sampled audio and Fugue needed vast amounts of memory and was hard to extend.

Nyquist does not need to preprocess an orchestra or patch. Lisp-based Nyquist programs can construct new patches on the fly and users can execute synthesis commands interactively. Nyquist can work both in real-time and non-real-time modes.

Nyquist uses a declarative and functional style in which expressions are evaluated to create and modify sounds. For instance, for summing two sinusoids the following expression should be introduced: (s-add (osc) (osc)).

In Fugue space was allocated for the entire result when adding two signals, this was only practical for small sounds. In Nyquist the addition and synthesis is performed incrementally so that at any time there are only a few blocks of samples in memory. This approach is similar to Music-N languages such as CSound and, in fact, Music-N unit generators are very similar to Nyquist functions. The main difference is that in Music-N the order of execution is explicit while in Nyquist it is deduced from data dependencies. Also Nyquist sounds are more flexible and can be considered as values that can be assigned to variables or passed as parameters.

A Nyquist expression can be represented as a graph of ``suspended computation'' that is, a structure that represents a computation waiting to happen. When samples are needed, the suspension recognizes it needs samples from the different nodes and asks for a block of samples (note that this works by pull or lazy evaluation). Since the order is determined at evaluation the computation graph may change dynamically. In particular, when a new note is played the graph is expanded.

In Nyquist samples are stored in a linked list of sample blocks that is accessed sequentially by following list pointers. Each reader of a sound uses a sound object header to remember the current position in the list and other state information. Incremental lazy evaluation is still used, placing the suspension at the end of the list. When a reader needs to read beyond the last block of the list the suspension is asked to compute a new block which is inserted between the end of the list and the suspension. All readers in the list see and share the same samples regardless of when they are produced or which reader reads first. To prevent lists from exhausting storage resources, reference counting is used to move blocks from the head of the list to a free list from which they can be allocated for reuse.

Nyquist can add sounds with different start times so signal addition must be efficient in the frequent case where one signal is zero. When one operand is zero, the sound block from the other operand can be simply linked into the result with no multiplication or zero filling. Most signal generators produce a signal only over some interval and Nyquist semantics say that the sound is zero outside the interval. A signal that goes to zero is represented by a list node that points to itself, creating a virtually infinite list of zeros. When a suspension detects that its future output will be zero, it links its sound list to this terminal node and then deletes itself.

Nyquist sounds also have logical stop times (LST). A seq operator allows to add sound together setting the start time of one sound to the LST of the other. The LST may be earlier or later than the terminating time (e.g. in a note it may correspond to the release time, leaving out the decay).

Nyquist allows various transformations such as shifting in time, scaling, and stretching. The sound headers contain transformation information: to scale a sound, the header is copied and the copy scale-factor field is modified. A drawback is that all operators must apply the transformation to raw samples. Different strategies are used in order to minimize the cost.

Sample rate is specified in the header of each sound and Nyquist allows arbitrarily mixed sample rates. It is the responsibility of the suspension to interpolate samples linearly when rate conversion is necessary.

Multichannel signals are represented by Lisp arrays and Nyquist operators are generalized in the expected ways.

2004-10-18