neat little challenge

Hello folks

In the topic “Strange behavior” https://forum.audacityteam.org/t/strange-behavior/24769/1 we’ve tried to solve an issue with the ramp function. Since this is a primitive that’s primarily used for teaching purposes, Iwanted to assure that the amended version is compatible with the tutorials in the documentation.
Alas, thou thinkst and your system blinks in the debug window…
So, here my challenge for you: Bring the following first example from http://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/manual/part4.html to work in the Nyquist-prompt. The original lines are:

(defun tone-seq () (seqrep (i 16) (stretch 0.25 (osc-note c4))))
(defun pitch-rise () (stretch 4.0 (scale 16 (ramp))))
(defun chromatic-scale () (transpose (pitch-rise) (tone-seq)))
; now you could use (setf s (chromatic-scale))
;or (s-save (chromatic-scale) ny:all "" :play t)
; if you want to hear the result instntly,
;without saving or modifying the selection

this task seems very simple at first glance no challenge at all, but the devil is in the details. For a proper solution (don’t bother aabout rammp) until june, 13th I am offering a 1000-word translation from english into german or a free guitar lesson.
Good luck

I will repeat the example here because in the HTML version of the Nyquist manual the text is pretty much messed-up and very hard to read.

As a first example, let us use transpose to create a chromatic scale. First define a sequence of tones at a steady pitch:

(defun tone-seq ()
  (seqrep (i 16)
    (stretch 0.25 (osc-note c4))))

Now define a linearly increasing ramp to serve as a transposition function:

(defun pitch-rise ()
  (stretch 4.0 (scale 16 (ramp))))

This ramp has a duration of 4 seconds, and over that interval it rises from 0 to 16 (corresponding to the 16 semitones we want to transpose). Now, PITCH-RISE is used to transpose TONE-SEQ:

(defun chromatic-scale ()
  (transpose (pitch-rise) (tone-seq)))

If you copy these three functions into the text field of the Audacity Nyquist prompt then they will not work as expected because the Audacity Nyquist prompt works with a different default duration setting than the original Nyquist version from Roger Dannenberg.

This has to the consequence that for example:

(stretch 0.25 (osc-note c4))

This code will not produce a note with a length of a quarter second but instead it produces a note with a duration of the quarter of the length of the current Audacity selection. A similar problem will happen with the STRETCH in PITCH-RAISE that will stretch it’s argument not to four seconds, but instead it it stretches to four times the duration of the Audacity selection.

To make the three functions work as intended in the Nyquist manual you must wrap the call to CHROMATIC-SCALE into an ABS-ENV, so if you copy the following code into the text field of the Audacity Nyquist prompt then it will produce a chromatic scale of sixteen quarter-second notes, transposed by a ramp with a total length of four seconds:

(defun tone-seq ()
  (seqrep (i 16)
    (stretch 0.25 (osc-note c4))))

(defun pitch-rise ()
  (stretch 4.0 (scale 16 (ramp))))

(defun chromatic-scale ()
  (transpose (pitch-rise) (tone-seq)))

(abs-env (chromatic-scale))

I must confess that I only know this because I had complained to Roger Dannenberg some years ago that the time-warp examples from the Nyquist manual do not work at all in the Audacity Nyquist prompt, what was the reason why Roger then had added the ABS-ENV macro to Nyquist.

This means that it’s not me who has deserved the guitar lessons, but maybe you can translate some trumpet lessons from German to English and mail them to Roger Dannenberg?

  • edgar

Nice try Edgar
You succeeded to 99 %, or to be exact, to 99.94614512 %…

try the following line instead of the last one you provided in your code example.

(format t "Length: ~a" (second (snd-extent  (abs-env (chromatic-scale)) ny:all)))

You see, we don’t have 4 seconds and that’s where the fun begins.
Ps. I use normally the statement

(set-sound-srate *sound-srate*)

to be independent from the selection-length in Nyquist (a side-effect) and it additionally fixes the default-sound-srate-bug, when using a different SR than 44100 Hz (which is ok for this challenge). No lesson yet…

Has no one solved the puzzle?
Ok, maybe not interesting enough. I’m posting the explanation anyway. Here is a possibble solution, that produces exact 4 seconds of a chromatic line:

(defun tone-seq ()
  (seqrep (i 16)
    (stretch 0.25 (osc-note c4))))

(defun pitch-rise ()
  (stretch 4.0 (scale 16 (ramp))))

(defun chromatic-scale ()
  (transpose (pitch-rise) (tone-seq)))

(set-sound-srate *sound-srate*)
(set-control-srate 2204)
(s-save-autonorm  (chromatic-scale) ny:all "" :play t)
(format nil "Length of sound: ~a" (second  (snd-extent (chromatic-scale) ny:all)))

The essential lines are those which set the ssample-rates. The first one is somewhat senseless, because we set the default sound-srate (of 44100 Hzee) to the Samplerate of the Input (which is in most cases also 44100 Hz). The macro, that executes this command, initializes afterwards the environment and sets sound-srate to default-sound-srate which is of course senseless, because it has this value already. We use the command rather to reset the sound duration (i.e. sustain-factor) to the default of 1 s.
In the next step we reduce the control-srate from 2205 to 2204 Hz. That seems to be a very strange thing to do since there is nowhere the control-srate involved in the code (except for ramp, which returns a correct sound-length of 4 s). Osc-note returns pursuant to the documentation a sound with the usual sound-srate of 44100 Hz, but this is only partially true. The sound is actually composed of a sine-oscilator which is multiplied by an envelope, that only works with 2205 Hz. The envelope is upsampled so that the samplerates match in order to apply the mult-function. That’s alright in normal cases since the envelope can be upsampled by a factor of 20 to receive a new SR of 44.1 Khz.
the example code stretches (shrinks) the note by 0.25. But the stretching is not applied after the construction of the osc-note but already at the construction of the components themselves. What does this mean? Let’s take the simple command

(format nil  "~a" (snd-length (stretch 0.25 (osc-note c4)) ny:all))

The waveform which is 44100 samples = 1 s long is divided by 4 and yields 11025 samples at 44.1 Khz. the envelope which is 2205 s long is also divided by 4 yields 551 samples with a rest of 1 sample. This value is upsampled by a factor of 20 and that gives 11020 samples. The signal is now multiplied by the envelope. The resulting sound takes its stop-time from the Minimum of the two samples thus the incorrect length of only 11020 samples.
In the code for the solution for the challenge I changed the control-srate so that it could be divided by 4 without any rest. For the 4 s of the melody-line we have the new pair of lengths: Signal → 176400 samples (16 x 11025) and envelope → 176400 (16 x 551 x 20.00907441
(44100/2204)).
Sorry for the long explanation. Keep only in mind, that it is dangerous to shrink sounds, when there are control-signals involved. you can of course change the control-srate to 44100 Hz, this will always work but it slows down the computations and needs a lot of resources.
Could anyone tell me if the s-save-autonorm function works on all machines or only on my Windows?
Thank you all.
A last Tip: take a look at the second example on the Nyquist page, I wonder if you get it to work…