I still have no full and perfect explanation, but some partial things that really make sense.
Paul Graham: The reason why Lisp has no “pointer” data type ist that internally all Lisp objects are nothing but pointers.
Audacity Source Code Investigations
The last Nyquist Lisp value that is returned by the last evaluated Lisp expression in the Nyquist Prompt text field or in the plugin code is stored in a C variable. The C variable is then tested if the returned value points to a string, list (of labels), a number, or a sound object, and the appropriate action is taken by C code. After that the C variable is set to NULL (= Lisp NIL). Because the C variable contains a pointer to a Lisp object, as a side-effect the last value returned by Nyquist is set to NIL, too. In normal Nyquist plugins this has no further consequences, because the plugin code is already finished at this point.
If the last value returned by Nyquist is a a sound object, the Lisp type of the object is still a sound afterwards (SOUNDP returns true), but the pointer inside the sound object now points to NIL, and not to a block of samples anymore. Nyquist recognizes such a sound object as an “empty” sound. The standard use for a NIL pointer in a sound object is that if two sounds of different length are computed in parallel, then the sound, that is finished first, sets its sample-pointer to NIL, so the calling function knows that the “end of samples” of the sound object is reached and no unecessary samples will be produced.
All this in combination has some very strange side-effects with the SCRATCH variable:
If the last value returned by Nyquist is a pointer to the SCRATCH symbol, and the SYMBOL-VALUE of the SCRATCH symbol points to a sound object, then the SYMBOL-VALUE of the SCRATCH symbol wil become an empty sound object, because the pointer inside the sound object is set to NIL by the “nyx.c” code in the Audacity Nyquist interface.
Some examples what this means in practice:
Generate a one-second audio track with “Generate > Tone”, then from the Nyquist prompt:
(progn
(setf *scratch* (snd-copy s))
s)
This stores a copy of the pointer to S in SCRATCH, then it returns a pointer to S to the C code in the Audacity Nyquist interface… This has to the consequence that the pointer to S is set to NIL in the Audacity Nyquist interface, but the copy of the original pointer is still stored in SCRATCH.
Test if the sound in SCRATCH is a non-empty sound with the following code from the Nyquist prompt:
(print (snd-length *scratch* 100000))
This should return 44100 = 1 second of samples with a samplerate of 44.1kHz.
Create a one-second silent track with “Generate > Silence”, then from the Nyquist prompt, insert a copy of the sound in SCRATCH:
(snd-copy *scratch*)
This returns a copy of the pointer to SCRATCH, not a direct pointer to SCRATCH, so only the value of the copy of the pointer is set to NIL, the SYMBOL-VALUE of SCRATCH still contains the full sound:
(print (snd-length *scratch* 100000)) => 44100
Now do the same by returning a direct pointer to SCRATCH:
() *scratch*
Now the SYMBOL-VALUE of SCRATCH has become an empty sound object:
(print (snd-length *scratch* 100000)) => 0
In the case of the RMS-ENVELOPE examples from the text boxes above, the situation is one step more complicated:
(setf *scratch* (rms-envelope s))
Here the RMS-ENVELOPE signal is stored in SCRATCH, but the SETF macro returns a pointer to the SYMBOL-VALUE of its first argument, what in this case is a direct pointer to SCRATCH, so after this code has finished, the sound in SCRATCH will be set to an empty sound by the C code in the Audacity Nyquist interface.
(progn
(setf *scratch* (rms-envelope s))
s)
Here the RMS-ENVELOPE signal is stored in SCRATCH, and then a pointer to the S variable is returned, so the sound in S will be set to an empty sound by the C code in the Audacity Nyquist interface, but the sound stored in SCRATCH will survive.
All this is IMO not really good behaviour, but useful to know. Only the last value returned by Nyquist is set to NIL by the Audacity Nyquist interface. You can store sounds in SCRATCH and they will survive, as long as no pointer to SCRATCH is returned as the last value by Nyquist.