Nyquist provides functions for FFT and inverse FFT operations on streams of audio data. Because sounds can be of any length, but an FFT operates on a fixed amount of data, FFT processing is typically done in short blocks or windows that move through the audio. Thus, a stream of samples is converted in to a sequence of FFT frames representing short-term spectra.
Nyquist does not have a special data type corresponding to a sequence of FFT frames. This would be nice, but it would require creating a large set of operations suitable for processing frame sequences. Another approach, and perhaps the most “pure” would be to convert a single sound into a multichannel sound, with one channel per bin of the FFT.
Instead, Nyquist violates its “pure” functional model and resorts to objects
for FFT processing. A sequence of frames is represented by an XLISP object. Whenever you
send the selector :next
to the object, you get back either NIL, indicating the
end of the sequence, or you get an array of FFT coefficients.
The Nyquist function snd-fft
(mnemonic, isn't it?) returns one of the frame sequence
generating objects. You can pass any frame sequence generating object to another function,
snd-ifft
, and turn the sequence back into audio.
With snd-fft
and snd-ifft
, you can create all sorts of interesting processes. The main
idea is to create intermediate objects that both accept and generate sequences of frames.
These objects can operate on the frames to implement the desired spectral-domain
processes. Examples of this can be found in the file
demos/fft_tutorial.htm
,
which is part of the standard Nyquist release. The documentation for snd-fft
and
snd-ifft
follows.
snd-fft(sound, length, skip, window)
[SAL](snd-fft sound length skip window)
[LISP]FLONUM
s.
The function modifies the sound, violating the normal rule that sounds are immutable in Nyquist, so
it is advised that you copy the sound using snd-copy
if there are any other references to
sound. The length of the FFT is specified by length, a FIXNUM
(integer) which must
be a power of 2. After
each FFT, the sound is advanced by skip samples, also of type FIXNUM
. Overlapping FFTs,
where skip is less than length, are allowed.
If window is not NIL
, it must be a sound.
The first length samples of window are multiplied by length samples of sound before
performing the FFT. When there are no more samples in sound to transform,
this function returns NIL
. The coefficients in the returned array, in order, are the DC coefficient,
the first real, the first imaginary, the second real, the second imaginary, etc.
The last array element corresponds to the real coefficient at the Nyquist frequency.snd-ifft(time, srate, iterator, skip, window)
[SAL](snd-ifft time srate iterator skip window)
[LISP](local-to-global 0)
. The sample rate is given by srate. Typically, this would
be *sound-srate*
, but it might also depend upon the sample rate of the sound from which the
spectral frames were derived. To obtain each frame, the function sends the message :next
to the
iterator object, using XLISP's primitives for objects and message passing. The object should return
an array in the same format as obtained from snd-fft
, and the object should return NIL
when the end of the sound is reached. After each frame is inverse transformed into the time domain, it is
added to the resulting sound. Each successive frame is added with a sample offset specified by skip
relative to the previous frame. This must be an integer greater than zero and less than the frame (FFT) size.
If window is
not NIL
, it must be a sound. This window signal is multiplied by the inverse transformed frame
before the frame is added to the output sound. The length of each frame should be the same power of 2.
The length
is implied by the first array returned by iterator, so it does not appear as a parameter. This length
is also the number of samples used from window. Extra samples are ignored, and window is padded
with zeros if necessary, so be sure window is the right length. The resulting sound is computed on
demand as with other Nyquist sounds, so :next
messages are sent to iterator only when new
frames are needed. One should be careful not to reuse or modify iterator once it is passed to
snd-ifft
.