Generate Sine / Fat Sine with Envelope at specific Hz frequencies

Dear community,

I was using a javascript library (tone.js) to create some tones at specific Hz frequencies for my current art project, which worked great so far.
For technical reasons I can no longer use tone.js and I need to generate the needed tones in another way and save them in an audio format in order to use them in my audiovisual installation. Somebody online suggested I could create those with the free cakewalk/bandlab version and some free synth VST plugins. Sadly I was not able to do so, as all plugins did not have the option for entering a specific Hz frequency. Further online research led me to Audacity and Nyquist, which is really great by the way!

As I am not very proficient in Nyquist, I would appreciate any help with what I am trying to accomplish (I came quite close so far).

I would like to achieve the following, creating tones with the following specs/params (which are the ones I used with tone.js to create my tones):

oscillator type: fat sine

envelope:
attack : 0.0
decay : 0.5
sustain: 0.1
release: 2.4

attack curve : linear
release curve: exponential

So far I got this in Nyquist:

(defun hztone (hz)
   (mult (hzosc hz)
         (env 0.0 0.5 2.4 1.0 1.0 1.0)))

(play (hztone 432))

Questions:

  1. Any way to use a fat sine oscillator?

  2. Why can’t I define the “sustain” parameter? I see there is an “overall duration” parameter, which I though if I set it to the sum of A+D+R+S it could work as well without a separate S parameter, but adjusting the above Nyquist script by adding a last parameter inside the (env …) line did output always the same tone, no matter what the other params were, so I removed the “overall duration” again…

  3. Is there any way to define the attack and release curves?

  4. I suppose the ADS levels I can keep to 1.0? This option was not set with tone.js.


    All best and thanks in advance!

Deeno

A “sine wave” is a precisely defined waveform: https://en.wikipedia.org/wiki/Sine_wave
What do you mean by “fat sine” ?

Which of these are amplitudes and which are durations?
What is the total duration of the tone?

This makes it a little bit tricky because Nyquist has different commands for linear control signals and exponential control signals.
The basic linear version is PWL.
The basic exponential version is PWE.

You can create a linear envelope that goes from an amplitude of 1, down to 0.1 in 0.5 seconds and then stays at 0.1 for a “sustain” period of 0.5 seconds like this:

(pwl 0 1 0.5 0.1 1 0.1 1)

One thing to be cautious about with exponential envelopes is that they use a logarithmic scale, which means that an amplitude of zero is impossible (a true exponential decay will decay to zero in infinite time).
There are two ways around this problem. The easiest is to simply decay to a very small value (say 0.005) and then stop.
This will produce an exponential decay from a level of 0.1 to 0.005 in 2.4 seconds:

(pwe 0 0.1 2.4 0.005)

Now, to join these two envelopes together, you can make them into a sequence with the SEQ command:

(seq
  (pwl 0 1 0.5 0.1 1 0.1 1)
  (pwe 0 0.1 2.4 0.005))

The total duration will be 3.4 seconds, so we want to generate a tone of the correct length:

(osc (hz-to-step 440) 3.4)

And to apply the envelope to the tone, we simply multiply (“scale”) the tone by the envelope:

(mult
  (seq
    (pwl 0 1 0.5 0.1 1 0.1 1)
    (pwe 0 0.1 2.4 0.005))
  (osc (hz-to-step 440) 3.4))

and the full code that can be run in the Nyquist Prompt:

;type generate

(mult
  (seq
    (pwl 0 1 0.5 0.1 1 0.1 1)
    (pwe 0 0.1 2.4 0.005))
  (osc (hz-to-step 440) 3.4))

Dear Steve!
This is so great, I can’t thank you enough!
Your examples and some lookup at the pwl docs made me create the envelope exactly as I need it.

About the fat sine:
It is some kind of new oscillator type, added to tone.js.

From the tone.js docs:
A fat oscillator is an abstraction around multiple, slightly detuned oscillators.

Plus there is some info here https://www.reddit.com/r/synthesizers/comments/60rl4z/dumb_question_what_makes_an_oscillator_fat/ but I don’t know to which extend this is applicable/helpful, as I am not a synth/oscillator/music expert… Back to tone.js:

I am using the default settings with a modulation frequency of 0.5:
https://tonejs.github.io/docs/r12/FatOscillator

DEFAULTS
{
frequency  : 440 , // I GUESS THIS IS WHERE MY SETTING OF MODULATION FREQUENCY IS ADDED INSTEAD OF THE DEFAULT 440
detune  : 0 ,
phase  : 0 ,
spread  : 20 ,
count  : 3 ,
type  : sawtooth
}

Any idea if this is somehow replicable with Nyquist?

All best,
Deeno

Nyquist is extremely powerful when it comes to sound synthesis, but often you will need to construct the synthesizer functions yourself from the provided primitives.

Example - 3 slightly detuned sine oscillators:

;type generate

(defun fat1 (hz spread duration)
  (setf spread (/ spread 200.0))
  (let* ((pitch1 (hz-to-step hz))
         (pitch2 (+ pitch1 spread))
         (pitch3 (- pitch1 spread)))
    (mult (/ 3.0)
      (sim (osc pitch1 duration)
           (osc pitch2 duration *sine-table* 45)
           (osc pitch3 duration *sine-table* 90)))))

;Test it:
(fat1 440 20 3.5)

Dear Steve, thank you for the reply.

As I am just starting with Nyquist, I still have some questions:

  1. would I need to adjust your example (exchange sine-table with saw-table) as I am using “sawtooth” in tone.js?
  2. what does the 45 & 90 do next to sine-table, is this some kind of a default, when taken my default settings in tone.js into account?
  3. same for …/ spread 200.0…?
  4. how would I finally combine your last example with my envelope code, just adding it after it (with some minor alterations)?
;type generate

(mult
  (seq
    (pwl 0 1 0.5 0.5 0.6 0.5)
    (pwe 0 0.5 2.4 0.0001)
  )
  (osc (hz-to-step 432) 3.0)
)

All best,
Deeno

Yes. Nyquist has the following pre-defined wavetables:
sine-table
tri-table
saw-table

See also: Nyquist Functions

That’s the starting phase of the waveform.
Try generating some simple sine tones with different phase settings, and observe the start of the waveform (zoom in close)

I divided the value of “spread” by 200 so that you don’t need to enter tiny values. One “step” is equivalent to a semitone.

Think about how a map is scaled - each measurement on the ground is multiplied by a scaling factor (such as 1/25000) to give the length on the map.
In this case, we want to scale the waveform by making it proportional to the “envelope”. In other words, we multiply the waveform by the envelope.

The tri-table sounds nice :slight_smile:

;type generate

(defun fat1 (hz spread duration)
  (setf spread (/ spread 200.0))
  (let* ((pitch1 (hz-to-step hz))
         (pitch2 (+ pitch1 spread))
         (pitch3 (- pitch1 spread)))
    (mult (/ 3.0)
      (sim (osc pitch1 duration)
           (osc pitch2 duration *tri-table* 45)
           (osc pitch3 duration *tri-table* 90)))))

(setf fat-tone (fat1 440 20 3.5))

(setf envelope
     (seq
       (pwl 0 1 0.5 0.1 1 0.1 1)
       (pwe 0 0.1 2.4 0.005)))

(mult fat-tone envelope)