File: part4.html

package info (click to toggle)
nyquist 3.12%2Bds-4
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 58,036 kB
  • sloc: ansic: 74,355; lisp: 20,485; java: 9,390; cpp: 6,695; sh: 207; xml: 58; makefile: 40
file content (433 lines) | stat: -rw-r--r-- 22,713 bytes parent folder | download | duplicates (2)
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
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
<html><head><title>Behavioral Abstraction</title>
<link rel="stylesheet" type="text/css" href="nyquiststyle.css">
</head>
<body bgcolor="ffffff">
<a href = "part3.html">Previous Section</a> | <a href = "part5.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 = "26"><h2>Behavioral Abstraction</h2></a><a name="index141"></a>

In Nyquist, all functions are subject to
transformations<a name="index142"></a>.  You can think of transformations as
additional parameters to every function, and functions are free to use these
additional parameters in any way.  The set of transformation parameters is
captured in what is referred to as the <i>transformation
environment<a name="index143"></a></i>.  (Note that the term
<i>environment</i> is heavily overloaded in computer science.  This is yet
another usage of the term.)
<p>
Behavioral abstraction is the ability of functions to adapt their behavior
to the transformation environment.  This environment may contain certain
abstract notions, such as loudness, stretching a sound in time, etc.  These
notions will mean different things to different functions.  For example, an
oscillator should produce more periods of oscillation in order to stretch
its output.  An envelope, on the other hand, might only change the
duration of the sustain portion of the envelope in order to stretch.
Stretching a sample could mean resampling it to change its duration by the
appropriate amount.
<p>
Thus, transformations in Nyquist are not simply operations on signals.  For
example, if I want to stretch a note, it does not make sense to compute the
note first and then stretch the signal.  Doing so would cause a drop in the
pitch.  Instead, a transformation modifies the <i>transformation
environment</i> in which the note is computed.  Think of transformations as
making requests to functions.  It is up to the function to carry out the
request.  Since the function is always in complete control, it is possible
to perform transformations with "intelligence;" that is, the function can
perform an appropriate transformation, such as maintaining the desired pitch
and stretching only the "sustain" portion
of an envelope to obtain a longer note.
<p>
<a name = "27"><h3>The Environment</h3></a><a name="index144"></a>

The transformation environment consists of a set of special variables.
These variables should not be read directly and should <i>never</i> be set
directly by the programmer.  Instead, there are functions to read them, and
they are automatically set and restored by
transformation operators, which will be described below.
<p>
The transformation environment consists of the following elements.  Although
each element has a "standard interpretation," the designer of an
instrument or the composer of a complex behavior is free to interpret the
environment in any way.  For example, a change in <code>*loud*</code> may change
timbre more than amplitude, and <code>*transpose*</code> may be ignored by
percussion instruments:
<p>
<dl>
<dt>
 <code>*warp*<a name="index145"></a></code><dd>Time transformation, including time shift,
time stretch, and continuous time warp.  The value of <code>*warp*</code> is
interpreted as a function from logical (local score) time to physical
(global real) time.  Do not access <code>*warp*</code> directly.  Instead, use
<code>local-to-global(<i>t</i>)</code> to
 convert from a logical (local) time to real (global) time.  Most often,
you will call <code>local-to-global(0)</code>.  Several transformation operators
operate on <code>*warp*</code>, including <i>at</i> (<code>@</code>), <i>stretch</i> (<code>~</code>),
and <code>warp</code>. See also <code>get-duration()</code> and <code>get-warp()</code>.<br><br>
<dt> <code>*loud*<a name="index146"></a></code><dd>Loudness, expressed in decibels.  The default
(nominal) loudness is 0.0 dB (no change).  Do not access <code>*loud*</code>
directly.  Instead, use <code>get-loud()</code> to get the current value of
<code>*loud*</code> and either <code>loud</code> or <code>loud-abs</code> to modify it.<br><br>
<dt> <code>*transpose*<a name="index147"></a></code><dd>Pitch transposition, 
expressed in
semitones.  (Default: 0.0).  Do not access <code>*transpose*</code> directly.
Instead, use <code>get-transpose()</code> to get the current value of
<code>*transpose*</code> and either <code>transpose</code> or <code>transpose-abs</code> to
modify it.<br><br>
<dt> <code>*sustain*<a name="index148"></a></code><dd>The "sustain,"
"articulation," "duty factor," or amount by which to 
separate or overlap sequential notes.  For
example, staccato might be expressed with a <code>*sustain*</code> of 0.5, while very
legato playing might be expressed with a <code>*sustain*</code> of 1.2.
Specifically, <code>*sustain*</code> stretches the duration of notes (sustain)
without affecting the inter-onset time (the rhythm).  Do not access
<code>*sustain*</code> directly.  Instead, use <code>get-sustain()</code> to get the
current value of <code>*sustain*</code> and either <code>sustain</code> or
<code>sustain-abs</code> to modify it.<br><br>
<dt> <code>*start*<a name="index149"></a></code><dd>Start 
time of a clipping region.  <i>Note:</i>
unlike the previous elements of the environment, <code>*start*</code> has a
precise interpretation: no sound should be generated before <code>*start*</code>.
This is implemented in all the low-level sound functions, so it can
generally be ignored.  You can read <code>*start*</code> directly, but use
<code>extract</code> or <code>extract-abs</code> to modify it.  <b><i>Note 2:</i></b> Due
to some internal confusion between the specified starting time and 
the actual starting time of a signal after clipping, <code>*start*</code> 
is not fully implemented.<br><br>
<dt> <code>*stop*<a name="index150"></a></code><dd>Stop time of clipping 
region.  By analogy to
<code>*start*</code>, no sound should be generated after this time.
<code>*start*</code> and
<code>*stop*</code> allow a composer to preview a small section of a work 
without computing it from beginning to end.  You can read <code>*stop*</code> 
directly, but use <code>extract</code> or <code>extract-abs</code> to modify it. 
 <b><i>Note:</i></b> Due to some internal confusion between the specified 
starting time and the actual starting time of a signal after 
clipping, <code>*stop*</code> is not fully implemented.<br><br>
<dt><code>*control-srate*<a name="index151"></a></code><dd>Sample 
rate of control signals.  This environment
element provides the default sample rate for control signals.  There is no
formal distinction between a control signal and an audio signal.  
You can read <code>*control-srate*</code> directly, but 
use <code>control-srate</code> or <code>control-srate-abs</code> to modify it.<br><br>
<dt> <code>*sound-srate*<a name="index152"></a></code><dd>Sample 
rate of musical sounds.  This environment element provides the 
default sample rate for musical sounds.  You can 
read <code>*sound-srate*</code> directly, but use <code>sound-srate</code> 
or <code>sound-srate-abs</code> to modify it.
</dl>
<p>
<a name = "28"><h3>Sequential Behavior</h3></a><a name="index153"></a>
Previous examples have shown the use of <code>seq</code>, the sequential behavior
operator.  We can now explain <code>seq</code> in terms of transformations.
Consider the simple expression:
<p><pre>
play seq(my-note(c4, q), my-note(d4, i))
</pre></p>

The idea is to create the first note at time 0, and to start the next
note when the first one finishes.  This is all accomplished by manipulating
the environment.  In particular, <code>*warp*</code> is modified so that what is
locally time 0 for the second note is transformed, or warped, to the logical
stop time of the first note.
<p>
One way to understand this in detail is to imagine how it
might be executed: first, <code>*warp*</code> is set to an initial value that has no
effect on time, and <code>my-note(c4, q)</code> is evaluated.  A sound is returned and
saved.  The sound has an ending time, which in this case will be <code>1.0</code>
because the duration <code>q</code> is <code>1.0</code>.  This ending time, <code>1.0</code>,
is used to construct a new <code>*warp*</code> that has the effect of shifting
time by 1.0.  The second note is evaluated, and will start
at time 1.  The sound that is
returned is now added to the first sound to form a composite sound, whose
duration will be <code>2.0</code>.  <code>*warp*</code> is restored to its initial value.
<p>
Notice that the semantics of <code>seq</code> can be expressed in terms of
transformations.  To generalize, the operational rule for <code>seq</code> is:
evaluate the first behavior according to the current <code>*warp*</code>.
Evaluate each successive behavior with <code>*warp*</code> modified to shift the
new note's starting time to the ending time of the previous behavior.
Restore <code>*warp*</code> to its original value and return a sound which is the
sum of the results.  
<p>
In the Nyquist implementation, audio samples are only computed when they are
needed, and the second part of the <code>seq</code> is not evaluated until the
ending time (called the logical stop time) of the first part.  It is still
the case that when the second part is evaluated, it will see <code>*warp*</code>
bound to the ending time of the first part.  
<p>
A language detail: Even though Nyquist defers evaluation of the second part of the <code>seq</code>, the expression can reference variables according to ordinary 
Lisp/SAL scope rules.  This is because the <code>seq</code> captures the expression in a closure, which retains all of the variable bindings.
<p>
<a name = "29"><h3>Simultaneous Behavior</h3></a><a name="index154"></a>
Another operator is <code>sim</code>, which invokes multiple behaviors at the same
time.  For example,
<p><pre>
play 0.5 * sim(my-note(c4, q), my-note(d4, i))
</pre></p>

will play both notes starting at the same time.
<p>
The operational rule for <code>sim</code> is: evaluate each behavior at the
current <code>*warp*</code> and return the sum of the results. (In SAL, the
<code>sim</code> function applied to sounds is equivalent to adding them
with the infix <code>+</code> operator. The following section
illustrates two concepts: first, a <i>sound</i> is not a
<i>behavior</i>, and second, the <code>sim</code> operator and the <code>at</code> 
transformation can be used to place sounds in time.
<p>
<a name = "30"><h3>Sounds vs. Behaviors</h3></a><a name="index155"></a>
The following example loads a sound from a file in the current directory and stores it in <code>a-snd</code>:
<p><pre>
<i>; load a sound
;</i>
set a-snd = s-read(strcat(current-path(), "demo-snd.aiff"))

<i>; play it
;</i>
play a-snd
</pre></p>

<p>
One
might then be tempted to write the following:
<p><pre>
play seq(a-snd, a-snd)  <i>;WRONG!</i>
</pre></p>

Why is this wrong? Recall
that <code>seq</code> works by modifying <code>*warp*</code>, not by operating on
sounds.  So, <code>seq</code> will proceed by evaluating <code>a-snd</code> with
different values of <code>*warp*</code>.  However, the result of evaluating
<code>a-snd</code> (a variable) is always the same sound, regardless of the
environment; in this case, the second <code>a-snd</code> <i>should</i> start at time
<code>0.0</code>, just like the first. In this case, after the first sound ends,
 Nyquist is unable to "back up" to time zero, so in fact, this <i>will</i> 
play two sounds in sequence, but that is a result of an implementation 
detail rather than correct program execution. In fact, a future version of
Nyquist might (correctly) stop and report an error when it detects that the
second sound in the sequence has a real start time that is before the 
requested one.
<p>
How then do we obtain a sequence of two sounds properly?  
What we really need here is a
behavior that transforms a given sound according to the current
transformation environment.  That job is performed by <code>cue</code>.  For
example, the following will behave as expected, producing a sequence of two
sounds:
<p><pre>
play seq(cue(a-snd), cue(a-snd))
</pre></p>

This example is correct because the second expression will shift the sound
 stored in <code>a-snd</code> to start at the end time of the first expression.
<p>
The lesson here is very important: <b><i>sounds are not behaviors!</i></b>  Behaviors
are computations that generate sounds according to the transformation
environment.  Once a sound has been generated, it can be stored, copied,
added to other sounds, and used in many other operations, but sounds are
<i>not</i> subject to transformations.  To transform a sound, use <code>cue</code>,
<code>sound</code>, or <code>control</code>.  The differences between these operations
are discussed later.  For now, here is a "cue sheet" style score that
plays 4 copies of <code>a-snd</code>:
<p>
<p><pre>
<i>; use sim and at to place sounds in time
;</i>
play sim(cue(a-snd) @ 0.0,
         cue(a-snd) @ 0.7,
         cue(a-snd) @ 1.0,
         cue(a-snd) @ 1.2)
</pre></p>

<p>
<a name = "31"><h3>The At Transformation</h3></a><a name="index156"></a>
The second concept introduced by the previous example is the <code>@</code>
operation, which shifts the <code>*warp*</code> component of the environment.  For
example,
<p><pre>
cue(a-snd) @ 0.7
</pre></p>

can be explained operationally as follows: modify <code>*warp*</code> by shifting
it by <code>0.7</code> and evaluate <code>cue(a-snd)</code>.  Return the resulting sound
after restoring <code>*warp*</code> to its original value.  Notice how <code>@</code>
is used inside a <code>sim</code> construct to locate copies of <code>a-snd</code> in
time.  This is the standard way to represent a note-list or a cue-sheet in
Nyquist.
<p>
This also explains why sounds need to be <code>cue</code>'d in order to be shifted
in time or arranged in sequence.  If this were not the case, then <code>sim</code>
would take all of its parameters (a set of sounds) and line them up to start
at the same time.  But <code>cue(a-snd) @ 0.7</code> is just a sound, so
<code>sim</code> would "undo" the effect of <code>@</code>, making all of the sounds
in the previous example start simultaneously, in spite of the <code>@</code>!
Since <code>sim</code> respects the intrinsic starting times of sounds, a special
operation, <code>cue</code>, is needed to create a new sound with a new starting
time.
<p>
<a name = "32"><h3>The Stretch Transformation</h3></a><a name="index157"></a>
In addition to At (denoted in SAL by the <code>@</code> operator, the Stretch
transformation is very important. It appeared in the introduction, and
it is denoted in SAL by the <code>~</code> operator (or in LISP by the <code>stretch</code>
special form). Stretch also operates on the <code>*warp*</code> component of
the environment. For example,
<p><pre>
osc(c4) ~ 3
</pre></p>

does the following: modify <code>*warp*</code>, scaling the degree of 
"stretch" by 3, and evaluate <code>osc(c4)</code>. The <code>osc</code> behavior
uses the stretch factor to determime the duration, so it will return
a sound that is 3 seconds long. Restore <code>*warp*</code> to its original
value. Like At, Stretch only affects behaviors. <code>a-snd ~ 10</code> is
equivalent to <code>a-snd</code> because <code>a-snd</code> is a sound, not a
behavior. Behaviors are functions that compute sounds according to
the environment and return a sound.
<p>
<a name = "33"><h3>Nested Transformations</h3></a><a name="index158"></a>
Transformations can be combined using nested expressions.  For example,
<p><pre>
sim(cue(a-snd),
    loud(6.0, cue(a-snd) @ 3))
</pre></p>

scales the amplitude as well as shifts the second entrance of <code>a-snd</code>.
<p>
Why use <code>loud</code> instead of simply multiplying <code>a-snd</code> by some
scale factor? Using <code>loud</code> gives
the behavior the chance to implement the abstract
property <i>loudness</i> in an appropriate way, e.g. by including timbral 
changes. In this case, the behavior is <code>cue</code>, which implements
<i>loudness</i> by simple amplitude scaling, so the result is equivalent
to multiplication by <code>db-to-linear(6.0</code>).
<p>
Transformations can also be applied to groups of behaviors:
<p><pre>
loud(6.0, sim(cue(a-snd) @ 0.0,
              cue(a-snd) @ 0.7))
</pre></p>

<p>
<a name = "34"><h3>Defining Behaviors</h3></a><a name="index159"></a>
Groups of behaviors can be named using <code>define</code> (we already saw this
in the definitions of <code>my-note</code> and <code>env-note</code>).  Here is another example
of a behavior definition and its use.  The definition has one parameter:
<p><pre>
define function snds(dly)
  return sim(cue(a-snd) @ 0.0,
             cue(a-snd) @ 0.7,
             cue(a-snd) @ 1.0,
             cue(a-snd) @ (1.2 + dly))

play snds(0.1)
play loud(0.25, snds(0.3) ~ 0.9)
</pre></p>

In the last line, <code>snds</code> is transformed: the transformations will apply
to the <code>cue</code> behaviors within <code>snds</code>.  The <code>loud</code>
transformation will scale the sounds by <code>0.25</code>, and the <i>stretch</i>
(<code>~</code>) will
apply to the shift (<code>@</code>) amounts <code>0.0</code>, <code>0.7</code>, <code>1.0</code>,
and <code>1.2 + dly</code>.  The sounds themselves (copies of <code>a-snd</code>) will
not be stretched because <code>cue</code> never stretches sounds.
<p>
Section <a href = "part8.html#97">"Transformations"</a> describes the full set of transformations.
<p>
<a name = "35"><h3>Overriding Default Transformations</h3></a>
In Nyquist, behaviors are <i>the</i> important abstraction mechanism.
A behavior represents a class of related functions or sounds. For example,
a behavior can represent a musical note. When a note is stretched, it 
usually means that the tone sustains for more oscillations, but if the
"note" is a drum roll, the note sustains by more repetitions of the
component drum strokes. The concept of sustain is so fundamental that
we do not really think of different note durations as being different
instances of an abstract behavior, but in a music programming language,
we need a way to model these abtract behaviors. As the tone and drum
roll examples show, there is no one right way to "stretch," so the
language must allow users to define exactly what it means to stretch.
By extension, the Nyquist programmer can define how all of the 
transformations affect different behaviors.
<p>
To make programming easier, almost all Nyquist sounds are constructed
from primitive behaviors that obey the environment in obvious ways:
Stretch transformations make things longer and At transformations shift
things in time. But sometimes you have to override the default behaviors.
Maybe the attack phase of an envelope should not stretch when the note
is stretched, or maybe when you stretch a trill, you should get more
notes rather than a slower trill.
<p>
To override default behaviors, you almost always follow the same 
programming pattern: first, capture the environment in a local variable;
then, use one of the absolute transformations to "turn off" the
environment's effect and compute the sound as desired. The following
example creates a very simple envelope with a fixed rise time to
illustrate the technique.
<p><pre>
define function two-phase-env(rise-time)
  begin
    with dur = get-duration(1)
    return pwl(rise-time, 1, dur) ~~ 1.0
  end
</pre></p>

To "capture the environment in a local variable," a <code>with</code>
construct is used to create the local variable <code>dur</code> and set
it to the value of <code>get-duration(1)</code>, which answers the question:
"If I apply use the environment to stretch something whose nominal
duration is 1, what is the resulting duration?" (Since time transformations
can involve continuous time deformations, this question is not as 
simple as it may sound, so please use the provided function rather
than peeking inside the <code>*warp*</code> structure and trying to do it
yourself.) Next, we "turn off" stretching using the <code>stretch-abs</code>
form, which in SAL is denoted by the <code>~~</code> operator. 
Finally, we are ready to compute the envelope using <code>pwl</code>. Here,
we use absolute durations. The first breakpoint is at <code>rise-time</code>,
so the attack time is given by the <code>rise-time</code> parameter. The 
<code>pwl</code> decays back to zero at time <code>dur</code>, so the overall
duration matches the duration expected from the environment encountered
by this instance of <code>two-phase-env</code>. Note, however, that since
the <code>pwl</code> is evaluated in a different environment established
by <code>~~</code>, it is not stretched (or perhaps more accurately, it is
stretched by 1.0). This is good because it means <code>rise-time</code> will
not be stretched, but we must be careful to extend the envelope to 
<code>dur</code> so that it has the expected duration.
<p>
<a name = "36"><h3>Sampling Rates</h3></a>
<a name="index160"></a>
The global environment contains <code>*sound-srate*</code> and
<code>*control-srate*</code>, which determine the sample rates of sounds and
control signals.  These can be overridden at any point by the
transformations <code>sound-srate-abs</code> and <code>control-srate-abs</code>; for
example,
<p><pre>
sound-srate-abs(44100.0, osc(c4))
</pre></p>

will compute a tone using a 44.1Khz sample rate even if the default rate
is set to something different.
<p>
<a name="index161"></a>
As with other components of the environment, you should <i>never</i> change
<code>*sound-srate*</code> or <code>*control-srate*</code> directly.
  The global environment is determined by two additional
variables: <code>*default-sound-srate*</code> and <code>*default-control-srate*</code>.
You can add lines like the following to your <code>init.lsp</code> file to change
the default global environment:
<p><pre>
(setf *default-sound-srate* 44100.0)
(setf *default-control-srate* 1102.5)
</pre></p>

You can also do this using preferences in NyquistIDE. 
If you have already started Nyquist and want to change the defaults, the
preferences or the following functions can be used:
<p><pre>
exec set-control-srate(1102.5)<a name="index162"></a>
exec set-sound-srate(22050.0)<a name="index163"></a>
</pre></p>

These modify the default values and reinitialize the Nyquist environment.
<p>
<hr>
<a href = "part3.html">Previous Section</a> | <a href = "part5.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>