1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249
|
<!DOCTYPE html>
<html><head><title>Time/Frequency Transformation</title>
<link rel="stylesheet" type="text/css" href="nyquiststyle.css">
<link rel="icon" href="nyquist-icon.png" />
<link rel="shortcut icon" href="nyquist-icon.png" />
</head>
<body bgcolor="ffffff">
<a href = "part10.html">Previous Section</a> | <a href = "part12.html">Next Section</a> | <a href = "title.html#toc">Table of Contents</a> | <a href = "indx.html">Index</a> | <a href = "title.html">Title Page</a>
<hr>
<a name = "121"><h2>Time/Frequency Transformation</h2></a>
<p>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.</p>
<p>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.</p>
<p>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 <code>:next</code> to the
object, you get back either NIL, indicating the end of the sequence,
or you get an array of FFT coefficients.</p>
<p>The Nyquist function <code>snd-fft</code> (mnemonic, isn't it?) returns one
of the frame sequence generating objects. You can pass any frame
sequence generating object to another function, <code>snd-ifft</code>, and
turn the sequence back into audio.</p>
<p>With <code>snd-fft</code> and <code>snd-ifft</code>, 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
<code>nyquist/lib/fft/fft_tutorial.htm</code><a name="index929"></a><a name="index930"></a><a name="index931"></a>, which is part of the
standard Nyquist release. The documentation for <code>snd-fft</code> and
<code>snd-ifft</code> follows.</p>
<dl>
<dt>
<code>snd-fft(<a name="index932"></a><a name="index933"></a><i>sound</i>, <i>length</i>,
<i>skip</i>, <i>window</i>)</code> [SAL]<br>
<code>(snd-fft <i>sound</i> <i>length</i> <i>skip</i> <i>window</i>)</code>
[LISP]</dt>
<dd>This function performs an FFT on the first samples in
<i>sound</i> and returns a Lisp array of <code>FLONUM</code>s. The function
modifies the <i>sound</i>, violating the normal rule that sounds are
immutable in Nyquist, so it is advised that you copy the sound using
<code>snd-copy</code> if there are any other references to <i>sound</i>. The
length of the FFT is specified by <i>length</i>, a <code>FIXNUM</code>
(integer) which must be a power of 2. After each FFT, the sound is
advanced by <i>skip</i> samples, also of type <code>FIXNUM</code>. Overlapping
FFTs, where <i>skip</i> is less than <i>length</i>, are allowed. If
<i>window</i> is not <code>NIL</code>, it must be a sound. The first
<i>length</i> samples of <i>window</i> are multiplied by <i>length</i> samples
of <i>sound</i> before performing the FFT. When there are no more samples
in <i>sound</i> to transform, this function returns <code>NIL</code>. 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.<br><br>
<dt><code>snd-ifft(<a name="index934"></a><a name="index935"></a><a name="index936"></a><i>time</i>, <i>srate</i>, <i>iterator</i>, <i>skip</i>, <i>window</i>)</code> [SAL]<br>
<code>(snd-ifft <i>time</i> <i>srate</i> <i>iterator</i> <i>skip</i>
<i>window</i>)</code> [LISP]</dt>
<dd>This function performs an IFFT on a sequence
of spectral frames obtained from <i>iterator</i> and returns a sound. The
start time of the sound is given by <i>time</i>. Typically, this would be
computed by calling <code>(local-to-global 0)</code>. The sample rate is
given by <i>srate</i>. Typically, this would be <code>*sound-srate*</code>, 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 <code>:next</code> to the <i>iterator</i> object, using XLISP's
primitives for objects and message passing. The object should return
an array in the same format as obtained from <code>snd-fft</code>, and the
object should return <code>NIL</code> 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 <i>skip</i> relative to the previous
frame. This must be an integer greater than zero and less than the
frame (FFT) size. If <i>window</i> is not <code>NIL</code>, 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 <i>iterator</i>, so it does not appear as a
parameter. This length is also the number of samples used from
<i>window</i>. Extra samples are ignored, and window is padded with zeros
if necessary, so be sure <i>window</i> is the right length. The resulting
sound is computed on demand as with other Nyquist sounds, so
<code>:next</code> messages are sent to <i>iterator</i> only when new frames
are needed. One should be careful not to reuse or modify <i>iterator</i>
once it is passed to <code>snd-ifft</code>. </dd></dl><a name = "122"><h3>Spectral Processing</h3></a><a name="index937"></a>
<p>There are a number of functions defined to make spectral processing
easier in XLISP and SAL. The general approach, as described above, is
to create an iterator object that returns spectral frames. To avoid
using the XLISP object system directly, a more functional interface is
defined, especially for SAL users.
The <code>sa-init</code> function creates an iterator, and
<code>sa-next</code> retrieves spectral frames. Various functions are also
provided to transform these into amplitude (magnitude) spectra, plot
them and perform other operations.</p>
<p>Some examples that use these spectral processing functions can be found
in the Nyquist extension “fftsal” (use the
NyquistIDE's Window : Nyquist Extensions menu item to download it; it
will then be in your <code>nyquist/lib/fftsal</code> directory. You can find
descriptions of the examples in <code>nyquist/lib/fftsal/spectral-process.lsp</code>
and <code>nyquist/lib/fftsal/spectral-process.sal</code>.</p>
<dl>
<dt>
<code>sa-init(<a name="index938"></a><a name="index939"></a>resolution:
<i>hz</i>, fft-dur: <i>dur</i>, skip-period: <i>skip</i>, window: <i>window-type</i>,
input: <i>input</i>)</code> [SAL]<br>
<code>(sa-init :resolution <i>hz</i> :fft-dur <i>dur</i>
:skip-period <i>skip</i> :window <i>window-type</i>
:input <i>input</i>)</code> [LISP]</dt>
<dd>Creates a spectral-analysis object
that can be used to obtain spectral data from a sound. All keyword
parameters are optional except <code>input</code>. The <code>resolution</code>
keyword parameter gives the width of each spectral bin in Hz. It may
be <code>nil</code> or not specified, in which case the resolution is
computed from <code>fft-dur</code>. The actual resolution may be finer than
the specified resolution because fft sizes are rounded to a power of
2. The <code>fft-dur</code> is the width of the FFT window in seconds. The
actual FFT size will be rounded up to the nearest power of two in
samples. If <code>nil</code>, <code>fft-dur</code> will be calculated from
<code>resolution</code>. If both <code>fft-size</code> and <code>resolution</code> are
<code>nil</code> or not specified, the default value is 1024 samples,
corresponding to a duration of 1024 / signal-sample-rate. If both
<code>resolution</code> and <code>fft-dur</code> are specified, the
<code>resolution</code> parameter will be ignored. Note that <code>fft-dur</code>
and <code>resolution</code> are reciprocals. The <code>skip-period</code>
specifies the time interval in seconds between successive spectra (FFT
windows). Overlapping FFTs are possible. The default value overlaps
windows by 50%. Non-overlapped and widely spaced windows that ignore
samples by skipping over them entirely are also acceptable. The
<code>window</code> specifies the type of window. The default is raised
cosine (Hann or "Hanning") window. Options include <code>:hann</code>,
<code>:hanning</code>, <code>:hamming</code>, <code>:none</code> or <code>nil</code>, where
<code>:none</code> and <code>nil</code> mean a rectangular window. The
<code>input</code> can be a string (which specifies a sound file to read) or
a Nyquist SOUND to be analyzed. The return value is an XLISP object
that can be called to obtain parameters as well as a sequence of
spectral frames. Normally, you will set a variable to this result and
pass the variable to <code>sa-next</code>, described below.<br><br>
<dt><code>sa-info(<a name="index940"></a><i>sa-obj</i>)</code> [SAL]<br>
<code>(sa-info <i>sa-obj</i>)</code> [LISP]</dt>
<dd>Prints information
about an <i>sa-obj</i>, which was created by <code>sa-init</code> (see
above). The return value is <code>nil</code>, but information is printed.<br><br>
<dt><code>sa-next(<a name="index941"></a><i>sa-obj</i>)</code> [SAL]<br>
<code>(sa-next <i>sa-obj</i>)</code> [LISP]</dt>
<dd>Fetches the next
spectrum from <i>sa-obj</i>, which was created by <code>sa-init</code> (see
above). The return value is an array of FLONUMs representing the
discrete complex spectrum.<br><br>
<dt><code>sa-magnitude(<a name="index942"></a><i>frame</i>)</code> [SAL]<br>
<code>(sa-magnitude <i>frame</i>)</code> [LISP]</dt>
<dd>Computes the magnitude
(amplitude) spectrum from a frame returned by <code>sa-frame</code>. The
<i>i</i><sup T>th</sup> bin is stored at index <i>i</i>. The size of the array is
the FFT size / 2 + 1.<br><br>
<dt><code>sa-normalize(<a name="index943"></a><i>frame</i>
[, <i>max</i>])</code> [SAL]<br>
<code>(sa-normalize <i>frame</i> [<i>max</i>])</code> [LISP]</dt>
<dd>Normalize
a copy of <i>frame</i>, a magnitude (amplitude) spectrum returned by
<code>sa-magnitude</code>. If <i>max</i> (a FLONUM) is provided, the spectrum
will be normalized to have a maximum value of <i>max</i>, which defaults
to 1.<br><br>
<dt><code>sa-plot(<a name="index944"></a><i>sa-obj</i>, <i>frame</i>)</code> [SAL]<br>
<code>(sa-plot <i>sa-obj</i> <i>frame</i>)</code> [LISP]</dt>
<dd>Plots a magnitude
(amplitude) spectrum from <i>frame</i> returned by <code>sa-magnitude</code>.
The <i>sa-obj</i> parameter should be the same value used to obtain the frame.<br><br>
<dt><code>sa-print(<a name="index945"></a><i>file</i>, <i>sa-obj</i>, <i>frame</i>,
cutoff: <i>cutoff</i>, threshold: <i>threshold</i>)</code> [SAL]<br>
<code>(sa-print <i>sa-obj</i> <i>file</i> <i>frame</i>
:cutoff <i>cutoff</i> :threshold <i>threshold</i>)</code> [LISP]</dt>
<dd>Prints an ASCII
plot of <i>frame</i>, a magnitude (amplitude) spectrum returned by
<code>sa-magnitude</code> (or <code>sa-normalize</code>). The <i>file</i> is either a
file opened for writing or <code>T</code> to print to the console. The
caller is responsible for closing the file (eventually). The
<i>sa-obj</i> parameter should be the same value used to obtain the
frame. If <i>cutoff</i>, a FLONUM, is provided, only the spectrum below
cutoff (Hz) will be printed. If <i>threshold</i>, a FLONUM, is provided,
the output may elide bins with values below the threshold.<br><br>
<dt><code>sa-get-bin-width(<a name="index946"></a><i>sa-obj</i>)</code>
[SAL]<br>
<code>(sa-get-bin-width <i>sa-obj</i>)</code> [LISP]</dt>
<dd>Returns
the width of a frequency bin as a FLONUM in Hz (also the separation
of bin center frequencies). The center frequency of the <i>i</i><sup T>th</sup>
bin is <code>i * bin-width</code>.<br><br>
<dt><code>sa-get-fft-size(<a name="index947"></a><i>sa-obj</i>)</code>
[SAL]<br>
<code>(sa-get-fft-size <i>sa-obj</i>)</code> [LISP]</dt>
<dd>Returns a FIXNUM,
the size of the FFT, a power of 2. <br><br>
<dt><code>sa-get-fft-dur(<a name="index948"></a><i>sa-obj</i>)</code>
[SAL]<br>
<code>(sa-get-fft-dur <i>sa-obj</i>)</code> [LISP]</dt>
<dd>Returns a FIXNUM,
the duration of the FFT window.<br><br>
<dt><code>sa-get-fft-window(<a name="index949"></a><i>sa-obj</i>)</code>
[SAL]<br>
<code>(sa-get-fft-window <i>sa-obj</i>)</code> [LISP]</dt>
<dd>Returns a symbol
representing the type of window used, <code>:hann</code>, <code>:hamming</code> or
<code>:none</code>.<br><br>
<dt><code>sa-get-skip-period(<a name="index950"></a><i>sa-obj</i>)</code>
[SAL]<br>
<code>(sa-get-skip-period <i>sa-obj</i>)</code> [LISP]</dt>
<dd>Returns the
skip size in seconds (a FLONUM).<br><br>
<dt><code>sa-get-fft-skip-size(<a name="index951"></a><i>sa-obj</i>)</code>
[SAL]<br>
<code>(sa-get-fft-skip-size <i>sa-obj</i>)</code> [LISP]</dt>
<dd>Returns the
skip size in samples (a FIXNUM).<br><br>
<dt><code>sa-get-sample-rate(<a name="index952"></a><i>sa-obj</i>)</code>
[SAL]<br>
<code>(sa-get-sample-rate <i>sa-obj</i>)</code> [LISP]</dt>
<dd>Returns the
sample rate of the sound being analyzed (a FLONUM) in Hz.
</dd></dl><hr>
<a href = "part10.html">Previous Section</a> | <a href = "part12.html">Next Section</a> | <a href = "title.html#toc">Table of Contents</a> | <a href = "indx.html">Index</a> | <a href = "title.html">Title Page</a>
</body></html>
|