File: part14.html

package info (click to toggle)
nyquist 3.24%2Bds-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 58,156 kB
  • sloc: ansic: 74,757; lisp: 18,169; java: 10,942; cpp: 6,688; sh: 175; xml: 58; makefile: 40; python: 15
file content (230 lines) | stat: -rw-r--r-- 12,107 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
<!DOCTYPE html>
<html><head><title>Developing and Debugging in Nyquist</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 = "part13.html">Previous Section</a> | <a href = "part15.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 = "154"><h2>Developing and Debugging in Nyquist</h2></a><a name="index1139"></a><a name="index1140"></a>
<p>There are a number of tools, functions, and techniques that can help to debug Nyquist programs. Since these are described in many places 
throughout this manual, this chapter brings together many suggestions and techniques for developing code and debugging. You <i>really</i> 
should read this chapter before you spend too much time with Nyquist. Many problems that you will certainly run into are addressed here.</p>
<a name = "155"><h3>Debugging</h3></a>
<p>Probably the most important debugging tool is the backtrace. There are
two kinds of backtrace: one for SAL, and one for Lisp.</p>

<p>SAL mode is actually just an XLISP function (<code>sal</code>) that reads
input and evaluates it. When SAL encounters an error, it normally
prints a trace of the SAL stack (all the active functions written in SAL),
exists the current command, and reads the next command.</p>

<p>If you call XLISP functions from SAL, including most Nyquist sound 
processing functions, and an error occurs within these XLISP functions,
you will only see the SAL function that called the XLISP functions
listed in the stack trace. Sometimes you need more details.</p>

<p>When Nyquist encounters an error when it is not running SAL, it 
normally suspends execution and prints an 
error message. To find out where in the program the error occurred 
and how you got there, start by typing <code>(bt)</code>. This will print 
out the last several function calls and their arguments, which is 
usually sufficient to see what is going on.</p>

<p>In order for <code>(bt)</code> to work, you must have a couple of global 
variables set: <code>*tracenable*</code> is ordinarily set to <code>NIL</code>.  
If it is true, then a backtrace is automatically printed when an 
error occurs; <code>*breakenable*</code> must be set to <code>T</code>, as 
it enables the execution to be suspended when an error is 
encountered. If <code>*breakenable*</code> is <code>NIL</code> (false), 
then execution stops when an error occurs but the stack is 
not saved and you cannot get a backtrace. Finally, <code>bt</code> 
is just a macro to save typing.  The actual backtrace function 
is <code>baktrace</code>, which takes an integer argument telling how 
many levels to print.  All of these things are set up by default 
when you start Nyquist.</p>

<p>To get this XLISP backtrace behavior when SAL encounters an error,
you need to have <code>*breakenable*</code> set while SAL is running. The
best way to do this is to run within the NyquistIDE program,
open the Preferences dialog, and choose the desired settings, e.g.
&ldquo;Enable XLISP break on SAL error.&rdquo;</p>

<p>Since Nyquist sounds are executed with a lazy evaluation scheme, some
errors are encountered when samples are being generated.  In this
case, it may not be clear which expression is in error. Sometimes, it
is best to explore a function or set of functions by examining
intermediate results. Any expression that yields a sound can be
assigned to a variable and examined using one or more of:
<code>s-plot</code>, <code>snd-print-tree</code>, and of course <code>play</code>. The
<code>snd-print-tree</code> function prints a lot of detail about the inner
representaion of the sound. Keep in mind that if you assign a sound
to a global variable and then look at the samples (e.g. with 
<code>play</code> or <code>s-plot</code>), the samples will be retained in
memory. At 4 bytes per sample, a big sound may use all of your 
memory and cause a crash.</p>

<p>Another technique is to use low sample rates so that it is easier to 
plot results or look at samples directly. The calls:
</p>
<pre>
set-sound-srate(100)
set-control-srate(100)
</pre>

<p>
set the default sample rates to 100, which is too slow for audio, but useful for examining programs and results. The function
</p>
<pre>
snd-samples(<i>sound</i>, <i>limit</i>)
</pre>

<p>
will convert up to <i>limit</i> samples from <i>sound</i> into a Lisp 
array. This is another way to look at results in detail.</p>

<p>The <code>trace</code> function is sometimes useful.  It prints the name of
a function and its arguments everytimg the function is called, and the
result is printed when the function exits.  To trace the osc function,
type:
</p>
<pre>
trace(osc)
</pre>

<p>
and to stop tracing, type <code>untrace(osc)</code>.</p>

<p>If a variable needs a value or a function is undefined, and if <code>*breakenable*</code> was set, you will get a prompt where you can fix
the error (by setting the variable or loading the function definition)
and keep going.  At the debug (or break) prompt, your input must 
be in XLISP, not SAL syntax. Use <code>(co)</code>, short for <code>(continue)</code> to
reevaluate the variable or function and continue execution.</p>

<p>When you finish debugging a particular call, you can &ldquo;pop&rdquo; 
up to the top level by typing <code>(top)</code>, a short name for <code>(top-level)</code>.
There is a button named "Top" in the NyquistIDE that takes you back to the
top level (ready to accept XLISP expressions), 
and another button named "SAL" that puts you back in SAL mode.</p>
<a name = "156"><h3>Useful Functions</h3></a><dl>
<dt>
<code>grindef(<a name="index1141"></a><a name="index1142"></a><i>name</i>)</code> [SAL]<br>

<code>(grindef <i>name</i>)</code> [LISP]</dt>
<dd>Prints
a formatted listing of a lisp function. This is often useful to quickly inspect
a function without searching for it in source files. Do not forget to quote the
name, e.g. <code>(grindef 'prod)</code>.<br><br>
<dt><code>args(<a name="index1143"></a><a name="index1144"></a><i>name</i>)</code> [SAL]<br>

<code>(args <i>name</i>)</code> [LISP]</dt>
<dd>Similar
to <code>grindef</code>, this function prints the arguments to a function. This may
be faster than looking up a function in the documentation if you just need a
reminder. For example, <code>(args 'lp)</code> prints &ldquo;(LP S C),&rdquo; which may help you
to remember that the arguments are a sound (S) followed by the cutoff (C) 
frequency.
</dd></dl>
<p>The following functions are useful short-cuts that might have been included in 
XLISP. They are so useful that they are defined as part of Nyquist.</p>
<dl>
<dt>
<code>incf(<a name="index1145"></a><a name="index1146"></a><i>symbol</i>)</code> [SAL]<br>

<code>(incf <i>symbol</i>)</code> [LISP]</dt>
<dd>Increment <i>symbol</i>
by one. This is a macro, and <i>symbol</i> can be anything that can be set by
<code>setf</code>. Typically, <i>symbol</i> is a variable: &ldquo;<code>(incf i)</code>,&rdquo; but
<i>symbol</i> can also be an array element: &ldquo;<code>(incf (aref myarray i))</code>.&rdquo;<br><br>
<dt><code>decf(<a name="index1147"></a><a name="index1148"></a><i>symbol</i>)</code> [SAL]<br>

<code>(decf <i>symbol</i>)</code> [LISP]</dt>
<dd>Decrement <i>symbol</i>
by one. (See <code>incf</code>, above.)<br><br>
<dt><code>push(<a name="index1149"></a><i>val</i>, <i>lis</i>)</code> [SAL]<br>

<code>(push <i>val</i> <i>lis</i>)</code> [LISP]</dt>
<dd>Push <i>val</i> onto <i>lis</i> (a Lisp
list). This is a macro that is equivalent to writing (in Lisp) 
<code>(setf <i>lis</i> (cons <i>val</i> <i>lis</i>))</code>.<br><br>
<dt><code>pop(<a name="index1150"></a><i>lis</i>)</code> [SAL]<br>

<code>(pop <i>lis</i>)</code> [LISP]</dt>
<dd>Remove (pop) the first item from <i>lis</i> (a
Lisp list). This is a macro that is equivalent to writing (in Lisp)
<code>(setf <i>lis</i> (cdr <i>lis</i>))</code>. Note that the remaining list is returned,
not the head of the list that has been popped. Retrieve the head of the list
(i.e. the top of the stack) using <code>first</code> or, equivalently, <code>car</code>.
</dd></dl>
<p>The following macros are useful control constructs.</p>
<dl>
<dt>
<code>while(<a name="index1151"></a><i>test</i>, <i>expr1</i>, <i>expr2</i>, <span style="font-style:normal">...</span>)</code> [SAL]<br>

<code>(while <i>test</i> <i>expr1</i> <i>expr2</i> <span style="font-style:normal">...</span>)</code> [LISP]</dt>
<dd>A conventional
&ldquo;while&rdquo; loop. If <i>test</i> is true, evaluate expressions
 (<i>expr1</i>, <i>expr2</i>, etc.) and repeat. If <i>test</i> is false, return. This
 expression evaluates to NIL unless the expression <code>(return <i>expr</i>)</code>
 is evaluated, in which case the value of <i>expr</i> is returned. In SAL, the 
 loop statement is preferred.<br><br>
<dt><code>when(<a name="index1152"></a><i>test</i>, <i>action</i>)</code> [SAL]<br>

<code>(when <i>test</i> <i>action</i>)</code> [LISP]</dt>
<dd>A conventional &ldquo;if-then&rdquo; 
statement. If <i>test</i> is true, <i>action</i> is evaluated and returned. Otherwise,
NIL is returned. (Use <code>if</code> or <code>cond</code> to implement
 &ldquo;if-then-else&rdquo; and more complex conditional forms.
</dd></dl>
<p>It is often necessary to load a file <i>only if</i> it has not already been
loaded. For example, the <code>pianosyn</code> library loads somewhat slowly, so if
some other file already loaded it, it would be good to avoid loading it
again. How can you load a file once? Nyquist does not keep track of files
that are loaded, but you must be loading a file to define some function,
so the idea is to tell Nyquist "I require <i>function</i> from <i>file</i>"; if
the function does not yet exist, Nyquist satisfies the requirement by loading
the file.
<dl>
<dt>
<code>require-from(<a name="index1153"></a><a name="index1154"></a><i>fnsymbol</i>, 
<i>filename</i> [, <i>path</i>])</code> [SAL]<br>

<code>(require-from <i>fnsymbol</i> <i>filename</i> [<i>path</i>])</code> [LISP]</dt>
<dd>Tests whether <i>fnsymbol</i>, an unquoted 
function name, is defined. If 
not, <i>filename</i>, a <code>STRING</code>,
is loaded. Normally <i>fnsymbol</i> is a function that will be called from
within the current file, and <i>filename</i> is the file that defines 
<i>fnsymbol</i>. The <i>path</i>, if a <code>STRING</code>, is prepended to <i>filename</i>.
If <i>path</i> is <code>t</code> (true), then the directory of the current file is
used as the path.
</dd></dl></p>

<p>Sometimes it is important to load files relative to the current file. For example,
the <code>lib/piano.lsp</code> library loads data files from the <code>lib/piano</code> directory,
but how can we find out the full path of <code>lib</code>? The solution is:
<dl>
<dt>
<code>current-path(<a name="index1155"></a><a name="index1156"></a><a name="index1157"></a>)</code> [SAL]<br>

<code>(current-path)</code> [LISP]</dt>
<dd>Returns the full path name of the file that is
currently being loaded (see <code>load</code>). Returns NIL if no file is being loaded.
</dd></dl> </p>

<p>Finally, there are some helpful math functions:
<dl>
<dt>
<code>real-random(<a name="index1158"></a><a name="index1159"></a><a name="index1160"></a><i>from</i>, <i>to</i>)</code> [SAL]<br>

<code>(real-random <i>from</i> <i>to</i>)</code> [LISP]</dt>
<dd>Returns a random <code>FLONUM</code> between <i>from</i> and <i>to</i>. (See also <code>rrandom</code>, which is equivalent to <code>(real-random 0 1</code>)).</p>
<dt><code>power(<a name="index1161"></a><a name="index1162"></a><i>x</i>, <i>y</i>)</code> [SAL]<br>

<code>(power <i>x</i> <i>y</i>)</code> [LISP]</dt>
<dd>Returns <i>x</i> raised to
the <i>y</i> power.
</dd></dl><hr>
<a href = "part13.html">Previous Section</a> | <a href = "part15.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>