General Programming Questions

Hi, everyone. So I just joined this site for some help in learning Nyquist. My Computer Science class requires that we pick up a new programming language that we’ve never seen before and write a program with it and make a presentation on the program as well as the language. Since I’ve had fun messing around with Audacity in the past, I decided to ask my Professor if I could do Nyquist. I’ve already done a certain amount of research on my own, so I’m left with a few specific questions.

First of all, is there any program that might help me auto-format Nyquist plugins (I’m assuming it would probably not be Nyquist specific, it would more likely be used for lisp). For example Codeblocks has a feature that will auto-indent C++ and C programs. I’m having trouble reading a lot of code from plugins, because most of the plugins I’ve tried to take a look at try to fit as much of the code on to one line as possible, to the point where it’s hard to tell which parts are comments and which parts are code.


Secondly I’m having trouble using envelope features. I’ve tried running examples from the Nyquist 2.37 Manual, and I can’t get it right:

(defun mkwave ()
(setf *table* (sim (scale 0.5 (build-harmonic 1.0 2048))
(scale 0.25 (build-harmonic 2.0 2048))
(scale 0.125 (build-harmonic 3.0 2048))
(scale 0.062 (build-harmonic 4.0 2048))))
(setf *table* (list *table* (hz-to-step 1) T)))

(cond ((not (boundp ’*mkwave*))
(mkwave)
(setf *mkwave* t)))

First I tried running this in audacity’s “Nyquist Prompt…” effect. No luck. I just got a “Nyquist did not return audio” message. Then I tried pasting it into a plugin mkwave.ny which I placed in my plugins folder…Audacity can’t even find it apparently:

;nyquist plug-in
;version 3
;type process
;name "Mkwave..."
;action "How do envelopes work?"
;info "example program to play around with, to try to understand envelopes..."


(defun mkwave ()
(setf *table* (sim (scale 0.5  (build-harmonic 1.0 2048))
(scale 0.25  (build-harmonic 2.0 2048))
(scale 0.125 (build-harmonic 3.0 2048))
(scale 0.062 (build-harmonic 4.0 2048))))
(setf *table* (list *table* (hz-to-step 1) T)))



(cond ((not (boundp ’*mkwave*))
(mkwave)
(setf *mkwave* t)))

I’m also having the same kind of difficulty producing notes from the manual’s example code.


I did have some luck using the prebuilt sine-table, saw-table, and tri-table functions, though I’d like to know a little bit more. I tried the following code in Nyquist Prompt:

(vector
   (mult (aref s 1) (osc (hz-to-step 3) 1.0  *sine-table* 90))
   (mult (aref s 0) (osc (hz-to-step 3) 1.0 *sine-table* 0)))

It worked pretty much as expected, but my understanding is slightly lower than what I’d like it to be.
Take the left channel and give it an oscillation [???] … make oscillation the shape of sine curve, with a starting phase of 90 degrees.
Do the same for the right channel, but this time the sine wave starts off at 0 degrees.
Increasing the number after hz-to-step seems to make the period shorter (resulting in quicker and choppier increase/decrease in amplitude).

Something along those lines?

I can’t seem to figure out though what the 1.0 part is doing (before the sine table and after the (hz-to-step), I just took that from other example codes, to see what was going on. The only thing that I can seem to determine is that the number cannot be less than or equal to zero, or else the code will not work at all. Other than that I can’t really see much of a difference. My guess is that it has something to do with how much the amplitude is increased/decreased rather than how often, but I can’t tell for sure.

I also wanted to look at the code for the “phaser…” plug-in because that appears to be doing something entirely different than this. (When viewing in spectrum form I’ve noticed you can actually see a sine-wave cut out of the stereo tracks.) I can’t seem to find the phaser plugin in my plugin directory. I’m assuming there might be a good reason for this.

I think that’s more or less the bulk of it for now. Later I might want to know about dealing with frequencies. I’d kind of like to write an auto bass-boost/treble-cut this user selected channel and treble-boost/base cut the other channel. I think I have some idea on how to select channel 1 or channel 0 via controls. This would be in a similar manner to what you can do with the Equalization plugin and it’s built in curves, but it would perform four steps at once, instead of one. So if anyone can point me towards a frequency tutorial, that’d be great.

Any and all help is greatly appreciated (especially any help with envelopes).

There’s a lot of questions so I’ll reply in multiple posts.

The really useful parts are syntax highlighting and parentheses matching.
If you’re on Windows you can use Notepad++, or if on Linux you could use Scite. These both support parentheses matching and syntax highlighting for Lisp. (I don’t know what’s available for MacOS X). I find that Lisp syntax highlighting is close enough to be useful even though the Nyquist specific functions do not get coloured correctly.

Spacing is really really important for Lisp based languages, but for learning Nyquist/Lisp it is possibly better to do it manually than have an IDE do it for you. Apparently auto indenting Lisp is possible with vi and Emacs but I find it easier to use a more basic editor such as those already suggested. There is a guide to indenting Lisp here: http://dept-info.labri.u-bordeaux.fr/~idurand/enseignement/PFS/Common/Strandh-Tutorial/indentation.html For good examples of spacing and indenting, look at examples by Roger Dannenberg or Edgar-rft. The plug-ins by David R Sky have particular bad spacing, but David was blind (and now sadly departed).

For working with Nyquist in Audacity use Audacity 1.3.13. Nyquist support is more limited in earlier versions and several bugs have been fixed.
Get Audacity 1.3.13 from here: Audacity ® | Downloads

Running Nyquist code in Audacity:

There are several ways to do this. The simplest way is to enter commands into the Nyquist Prompt effect.
An important consideration here is that the Nyquist Prompt is an effect.

You may have noticed that there are 3 types of plug-in, process, generate and analyze.
When you run code in the Nyquist Prompt it will always run as a process type effect.

One of the important distinctions about process types is that they must have a track selection to act on [they apply an effect to something]. So to run the Nyquist Prompt, create an Audacity track [ Tracks > Add New > Audio Track or Ctrl+shift+N] and select part of it.

The track is currently empty but Audacity cannot pass empty space to Nyquist, so it passes silence to Nyquist with the same duration and sample rate as the track selection. This silence is not important to us at present, but the duration is. The selection has an absolute duration [however big the selection is] and a duration in “local time”. The duration of the selection for process plug-ins in local time is always 1.0.

Having made a selection in an audio track, let’s generate something.
Open the Nyquist prompt and enter the following code:

(osc 60)

All this does is to generate a tone at MIDI note 60 and duration 1.0. The important things for us are:
The default duration of 1.0 is in local time, so the audio that is returned to the track will be the same length as the selection.
That it does so by using table (a wave table) to define the waveform. By default table is a sine table, so the generated waveform is a sine wave.

(defun mkwave ()
  (setf *table* 
    (sim 
      (scale 0.5 (build-harmonic 1.0 2048))
      (scale 0.25 (build-harmonic 2.0 2048))
      (scale 0.125 (build-harmonic 3.0 2048))
      (scale 0.062 (build-harmonic 4.0 2048))))      
  (setf *table* (list *table* (hz-to-step 1) T)))

(cond ((not (boundp '*mkwave*))
       (mkwave)
       (setf *mkwave* t)))

On its own, this code will not appear to do anything.
What it is doing is:

There’s a function (mkwave) that creates a wavetable and assigns it to the global variable table

The ‘cond’ special form checks to see if mkwave exists [if the variable is bound] and then either calls the function or sets mkwave to ‘true’. This just ensures that the function (mkwave) is called only once.

As you will see from the link, table is used by osc and other oscillators.
So that we can see this code do something, add the following line to the end of the code:

(osc 60)

Here’s the whole thing together for easy Copy/Paste

(defun mkwave ()
  (setf *table* 
    (sim 
      (scale 0.5 (build-harmonic 1.0 2048))
      (scale 0.25 (build-harmonic 2.0 2048))
      (scale 0.125 (build-harmonic 3.0 2048))
      (scale 0.062 (build-harmonic 4.0 2048))))      
  (setf *table* (list *table* (hz-to-step 1) T)))

(cond ((not (boundp '*mkwave*))
       (mkwave)
       (setf *mkwave* t)))

(osc 60)

Zoom in close on the generated waveform and you will see that it is not a sine wave, but now uses the wave-table that was generated by the function (mkwave).

I’ve just noticed in your code that you have used a “backquote” character instead of a “single quote” character on line:

(cond ((not (boundp ’*mkwave*))

it should be:

(cond ((not (boundp '*mkwave*))

It’s hard to see the difference, but the single quote character is probably the lower case character on the “@” key.

That’s about right :slight_smile:

The outer most function (vector) creates a two dimensional array. (stereo sounds are handled as an array of two sounds. The first element is the left channel and the second element is the right channel. Audacity currently supports a maximum of 2 channels in a track).

The first element in the array is:

(mult (aref s 1) (osc (hz-to-step 3) 1.0  *sine-table* 90))

Looking again at the outermost function we have (mult) which “multiplies” numbers or sounds.
In this case it is multiplying the sound (aref s 1) by (osc (hz-to-step 3) 1.0 sine-table 90))

Taking these one at a time:

(aref s 1) is the right channel of the selected audio (“s” is a global variable used by Audacity to pass to Nyquist, either a sound from a mono track, or an array of two sounds from a stereo track.)

(osc (hz-to-step 3) 1.0 sine-table 90)) is a simple oscillator function.
As we can see in the manual: Nyquist Functions the syntax is:
(osc pitch [duration table phase])
where “Pitch” is the “step value” (same as MIDI note value).
The 1.0 in the code example is the duration [local time].

The function (hz-to-step 3) is used for convenience to convert 3Hz into its MIDI note number (step number)

Yeah, getting your code indention down is a pretty good thing to learn. XLISP can be pretty
tough to read if you don’t and you can spend oodles of time trying to track down that missing
parenthesis.

It’s really similar to spreadsheet programming, which I’ve been doing a LOT of lately. :frowning:

I got so far with adding parenthesis section of code to formulas until I figured out you could
split the code into unique cells, then spread the code out between cells. THEN, you hide the
columns with the non-output code, making a much easier to understand and read final
formula. I use this technique sometimes as (for some reason) you can have multiple code
lines in a single cell. :sunglasses:

As for “envelopes”, I think those are just time points and values, depending on what you’re
using it for. It’s basically just a list (time value time value time value). Not sure why they’re
called “envelopes” though.

Because it “envelops” something? (like an “envelope” envelops a letter)

Quite true.
I suppose the reason for the creation of technical terms such as envelope has to do with
relating the specialty of the field. One could argue that such terms tend to distract both
novice and casual learners from ever achieving a full understanding of the topic, that it
leads to a kind of “elitism” of knowledge. But then, perhaps it’s a form of “cool” in some
circles.

I, myself, (unfortunately) went to college, as an English Literature major and once took
delight the amount of technical English terminology I had acquired. Subjective pronouns
and such.

Today though, I’ve begun to think that some abstract terminology is a real hinderance to
learning English. I mean, why call it an “adjective”, why not something that makes more
sense, like a “descriptor”. It describes an object (aka noun).

Anyways, I forgot to add that (in Nyquist), some PWL functions need samples for times
(such as SND-PWL), while others need percentages for time (such as PWL-LIST), which
are the two I generally use when creating a filter sweep or basic waveform to be
oscillated.

The trouble with using SND-PWL to create (what I call a “filter graph” aka envelope)
is that you have to use it twice when filtering stereo tracks.

The author of Nyquist has provided some pretty decent PWL functions that (I believe)
take time markers (starting at 0.000000 … the beginning of the selection of audio,
not the beginning of Audacity timeline), but some require that you don’t include the
first time unit (or maybe it’s the last).

I figured it out by looking a Nyquist source code (it’s written in XLISP) that’s in the
Audacity program’s folder (at least in 126). It’s a good read, though you’ll have to
format it yourself.

All of the Piece-wise Approximations come down to variations of the snd-pwl function. This is the “primitive” on which the others are based. Note that Mr Dannenberg recommends that snd-pwl is not intended to be used directly, but should be accessed via one of its helper functions such as pwl.
The variations (pwl, pwlv, pwlvr-list,… and the rest) are documented in the manual here: Nyquist Functions