Debugging
Probably the most important debugging tool is the backtrace. When
Nyquist encounters an error, it 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 (bt)
. This will print
out the last several function calls and their arguments, which is
usually sufficient to see what is going on.
In order for (bt)
to work, you must have a couple of global
variables set: *tracenable*
is ordinarily set to NIL
.
If it is true, then a backtrace is automatically printed when an
error occurs; *breakenable*
must be set to T
, as
it enables the execution to be suspended when an error is
encountered. If *breakenable*
is NIL
(false),
then execution stops when an error occurs but the stack is
not saved and you cannot get a backtrace. Finally, bt
is just a macro to save typing. The actual backtrace function
is baktrace
, which takes an integer argument telling how
many levels to print. All of these things are set up by default
when you start Nyquist.
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:
s-plot
, snd-print-tree
, and of course play
. The
snd-print-tree
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
play
or s-plot
), 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.
Another technique is to use low sample rates so that it is easier to plot results or look at samples directly. The calls:
(set-sound-srate 100) (set-control-srate 100)set the default sample rates to 100, which is too slow for audio, but useful for examining programs and results. The function
(snd-samples sound limit)will convert up to limit samples from sound into a Lisp array. This is another way to look at results in detail.
The trace
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:
(trace osc)and to stop tracing, type
(untrace osc)
.
If a variable needs a value or a function is undefined, you can fix
the error (by setting the variable or loading the function definition)
and keep going. Use (co)
, short for (continue)
to
reevaluate the variable or function and continue execution.
When you finish debugging a particular call, you can "pop"
up to the top level by typing (top)
, a short name for (top-level)
.
(grindef name)
(grindef 'prod)
.(args name)
grindef
, 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, (args 'lp)
prints "(LP S C)," which may help you
to remember that the arguments are a sound (S) followed by the cutoff (C)
frequency.
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.
(incf symbol)
setf
. Typically, symbol is a variable: "(incf i)
," but
symbol can also be an array element: "(incf (aref myarray i))
."(decf symbol)
incf
, above.)(push val lis)
(setf lis (cons val lis))
.(pop lis)
(setf lis (cdr lis))
. 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 first
or, equivalently, car
.
The following macros are useful control constructs.
(while test stmt1 stmt2 ...)
(return expr)
is evaluated, in which case the value of expr is returned.(when test action)
if
or cond
to implement
"if-then-else" and more complex conditional forms.
Sometimes it is important to load files relative to the current file. For example,
the lib/piano.lsp
library loads data files from the lib/piano
directory,
but how can we find out the full path of lib
? The solution is:
(current-path)
load
). Returns NIL if no file is being loaded.
Finally, there are some helpful math functions:
(real-random
from to)
FLONUM
between from and to. (See also rrandom
, which is equivalent to (real-random 0 1
)).(power x y)