waveform designer for music

What I meant was, do you want the waveform centred vertically. I will assume that you do.

So here’s a general method that can be used to produce waveforms of any desired shape:

To start, draw out the waveform on a piece of paper - graph paper would be ideal:
octagonal-waveform.png
Note the positions of the red dots. These dots define the “shape”.
From the above image we can see that the dots are at:

0.0, -1.0
0.5, -1.0
1.0, 0.5 (approx)
2.5, 1.0
4.0, 0.5 (approx)
4.5, -1.0
5.0, -1.0

If necessary we can use trigonometry to work out the approximate values more precisely (see https://www.mathsisfun.com/sine-cosine-tangent.html) giving us points at:
(0.0, -1.0) (0.5, -1.0) (1.086, 0.414) (2.5, 1.0) (3.914, 0.414) (4.5, -1.0) (5.0, -1.0)

In Nyquist, we can store these values in a list like this:

(setf points (list 0 -1 0.5 -1 1.086 0.414 2.5 1 3.914 0.414 4.5 -1 5 -1))

In Nyquist there is a function called PWLV-LIST which creates a waveform from a list of points. The first point is assumed to start at “time = 0”, so removing the first zero from the list we have a list of points like this:

(setf points (list -1 0.5 -1 1.086 0.414 2.5 1 3.914 0.414 4.5 -1 5 -1))

We can now create a single cycle waveform using this list like this:

(setf points (list -1 0.5 -1 1.086 0.414 2.5 1 3.914 0.414 4.5 -1 5 -1))
(pwlv-list points)

To get a reliable length of the waveform, we need to add a couple of additional commands. Firstly we need to tell Audacity that we want to treat this code as a generator, so we start our script with:

;type generate

and then we tell Nyquist to use absolute times (seconds) by wrapping the generating command in “ABS-ENV” (absolute environment), giving us:

;type generate
(setf points (list -1 0.5 -1 1.086 0.414 2.5 1 3.914 0.414 4.5 -1 5 -1))
(abs-env
  (pwlv-list points))

This tells Nyquist to create a waveform that is 5 seconds duration, using the points from our list. By default, PWLV-LIST (like the other “PWL” commands) produces a low sample rate sound, where the sample rate is 1/20th of the track sample rate. The effect of this is that when the waveform is returned to a track, the length will be 1/20th of the length that we defined, so it will actually produce a single cycle waveform that is 5/20 = 0.25 seconds duration. This will be fine for our purpose, but before going on, try running the above code in the Nyquist Prompt effect. You should get a waveform like this:
single-cycle.png
Are you with me so far? Did that work for you?

Moving on. We don’t actually want a single cycle, what we want is a continuous tone. To get a continuous tone, we can use a single-shot waveform as a “lookup table”. To do that, we need to make a lookup table from the single cycle waveform using the MAKETABLE command.
Once we have the lookup table, we can use it with the oscillator command “OSC”.

;type generate
(setf points (list -1 0.5 -1 1.086 0.414 2.5 1 3.914 0.414 4.5 -1 5 -1))
(setf lookuptable
  (maketable
    (abs-env
      (pwlv-list points))))
(osc 60 30 lookuptable)

A bit of explanation about the final “OSC” command: The first parameter is the note to produce, and is the MIDI note number. The second parameter is the duration of the tone in seconds. http://www.cs.cmu.edu/~rbd/doc/nyquist/part8.html#index380
If you wish to define the pitch in Hz rather than as a note number, you can use the command “HZ-TO-STEP”.

Here’s the final code to generate a 440 Hz waveform for 30 seconds:

;type generate
(setf points (list -1 0.5 -1 1.086 0.414 2.5 1 3.914 0.414 4.5 -1 5 -1))
(setf lookuptable
  (maketable
    (abs-env
      (pwlv-list points))))
(osc (hz-to-step 440) 30 lookuptable)