Adding randomized silence (Windows 10, Audacity 2.1.3)

Hello :slight_smile:

I’ve been currently working on my thesis where I’m supposed to create track which should consist of tones at frequency of 1000 Hz and different levels of gain (their duration is 250 ms). Between those tones there is supposed to be randomized silence according to the formula : silence:= 1800 ms + random(1000), so the silence is supposed to range from 1800 ms to 2800 ms. I’m deeply asking for help in writing formula that can realise this task. Thanks in advance for helping the newbie :slight_smile:

I hope I’m not doing your homework for you :wink:
Try running this in the Nyquist Prompt effect::

;version 4
;type generate
(setf tone
  (mult (osc (hz-to-step 1000) 0.25)
        (pwlv 0 0.01 0.8 0.24 0.8 0.25 0)))

(defun rest ()
  (s-rest (+ 1.8 (rrandom))))

(seqrep (i 20)
  (seq (cue (rest))(cue tone)))

Thank you for the reply and help :slight_smile: To be honest, you almost did my homework and your post is a lot of help :astonished: In this track I’m supposed to conclude tones at frequency of 1000 Hz and 10 different levels of gain that ranges from -5 dB to -45 dB in reference to basic sound which whole duration time is 250 ms (accretion time is 20 ms, duration is 210 ms and descent time is 20 ms) and amplitude is 1. Between those tones there is supposed to be a silence as I described in the previous post. I’m a complete newbie to programming in Nyquist so I would be very grateful if it was possible to get even very short explanation what this code came from :slight_smile:

Hi :slight_smile:
Could I count on help with my issue ? Big thanks in advance and best greetings :slight_smile:

Anything after a semicolon is treated by Nyquist as a “comment” and is ignored by Nyquist.

The first couple of lines are special “comments” in that they are ignored by Nyquist, but mean something to Audacity. These comments are referred to as “headers” (they usually occur at the head (top) of the script. More about Nyquist plug-in headers here: Missing features - Audacity Support

;version 4
;type generate
;; The two lines above tell Audacity to treat the code as "version 4" plug-in code
;; and treat it as a "Generator".

A significant difference between “generate” type effects and “process” type effects is in how they deal with time.
For a “process” type effect, “1 unit” of time is equivalent to the duration of the selection. In other words, time is relative to the duration length.
For “generate” type effects, “1 unit” of time is 1 second. In other words, “local time” (in the plug-in) is equivalent to “global time” (real time outside of the plug-in).


;; Set the value of the variable 'tone' to a sound that is created
;; by multiplying a 1000 Hz, 0.25 seconds oscillator, by a control signal.
;; The control signal is created by PWLV 
(setf tone
  (mult (osc (hz-to-step 1000) 0.25)
        (pwlv 0 0.01 0.8 0.24 0.8 0.25 0)))

http://www.cs.cmu.edu/~rbd/doc/nyquist/part8.html#index381
http://www.cs.cmu.edu/~rbd/doc/nyquist/part19.html#index1630
http://www.cs.cmu.edu/~rbd/doc/nyquist/part8.html#index410
Here, PWLV creates an “envelope” that rises from 0 at time=zero to 0.8 and time=0.1, then falls from 0.8 at time=0.24 to zero at time=0.25
OSC generates the 1000 Hz tone of duration 0.25 seconds (“seconds” because it is “generate” type plug-in code) with an amplitude of 1.
MULTiplying the tone by the control signal, scales the amplitude of each sample in the tone by the respective level in the control signal.


;; Define a function 'REST' that generates silence
;; for a random duration of 1.8 + (random value 0 to 1)
(defun rest ()
  (s-rest (+ 1.8 (rrandom))))

http://www.audacity-forum.de/download/edgar/nyquist/nyquist-doc/xlisp/xlisp-ref/xlisp-ref-087.htm
http://www.cs.cmu.edu/~rbd/doc/nyquist/part8.html#index573
http://www.cs.cmu.edu/~rbd/doc/nyquist/part19.html#index1630
The reason that we use a function here rather than a variable, is because we need to regenerate each instance of the silence so that we get different lengths of silence each time.



;; Repeat a sequence of behaviours for i = 0 to 20
;; The 'behaviour' is to sequence the silence produced by the REST function
;; with the CUEd sound "tone"
(seqrep (i 20)
  (seq (cue (rest))(cue tone)))

http://www.cs.cmu.edu/~rbd/doc/nyquist/part8.html#index611
http://www.cs.cmu.edu/~rbd/doc/nyquist/part8.html#index610
http://www.cs.cmu.edu/~rbd/doc/nyquist/part8.html#index344
The important thing that CUE does for us, is that it allows us to use a “sound” as a “behaviour”. This is really the tricky part to explain, but you can read about it here: Behavioral Abstraction
If we didn’t CUE the sounds, then it would still work, but Nyquist would complain and create debug warnings.

So instead of creating “tone” as a variable with a fixed sound value, you will need to define a function that returns the required sound each time it is called.

Use DEFUN to define the function.
PWLV to create the envelope.

Once you have defined a function “tone”, assuming that you are not sending parameters to the function, you would call it by writing the name of the function in parentheses
(tone)
as we did previously with
(rest)

Thanks a lot for this very comprehensive explanation, it’s a huge help :slight_smile: I’m wondering if and how it is possible to randomize also gain cause it is supposed to be random in the range -5 dB to -45 dB, but these sound levels should change exactly of amount 5 dB. For example first appeared sound’s gain is -5 dB in reference to basic sound, then -15 dB to basic sound, then -45 dB to basic sound and so on. I mean randomized gain change of multiple of 5 dB in reference to basic sound. I was thinking about making a rrandom function in a pwlv envelope multiplied by 5 and also a truncate function to truncate it to an integer and recieve a condition of change of exactly multiple of 5 dB), but I think I’m still missing something or overcomplicate it. I would be very grateful for further tips.

P.S. Out of pure curiosity : Is it possible to change (mult (osc (hz-to-step 1000) 0.25) (pwlv 0 0.01 0.8 0.24 0.8 0.25 0))) to this function (osc-note pitch [duration env loud table]). I think the second function does exactly the same, but I can be wrong.

Yes you could, but you would still need to define “env” as a “sound” (control signal) because the alternative syntax “(t1 t2 t4 l1 l2 l3)” does not give you enough control points.

Got to dash - back in a bit to answer your other question.

There’s a random integer function that you could use:
http://www.cs.cmu.edu/~rbd/doc/nyquist/part19.html#index1629
For multiples of 5, simply generate an integer in an appropriate range, and multiply by 5 (-5 for negative numbers)
For example:

(* -5 (+ 1 (random 10)))  ;generate a random number between -5 and -50

Big, big thanks :slight_smile: this is clear, but how to manipulate amplification of created sound ? I was thinking about randomizing it inside envelope but I don’t know if an amplitude changes linearly proportional to a change of amplification, cause inside an envelope we can change an amplitude. No more ideas come to my head when it comes to that issue. I need to ask for help once again :slight_smile:

“Amplification” is just “multiplication”.
“Multiplying a sound” applies the multiplication factor to each sample value, thereby scaling the amplitude. Multiplying by x2 is very close to +6 dB boost. Multiplying by 0.5 is very close to -6 dB (reduction).

Note that for sounds you can’t use (* A B), you have to use “mult” (mult A B).

There are a couple of handy conversion functions:
DB-TO-LINEAR converts a number from dB scale to linear scale
LINEAR-TO-DB converts a number from linear scale to dB scale

Example: Amplify the selected audio in a track by -3 dB

(mult *track* (db-to-linear -3.0))

Thanks a lot. I created a code like this

;version 4
;type generate
(defun tone ()
  (mult (osc (hz-to-step 1000) 0.25))
        (pwlv 0 0.02 1 0.23 1 0.25 0)
(mult tone (db-to-linear (* -5 (+ 1 (random 10)))))

(defun rest ()
  (s-rest (+ 1.8 (rrandom))))

(seqrep (i 200)
  (seq (cue (rest))(cue tone)))

I still get errors after debugging. Could you take a look ? You are a huge help :slight_smile:

I think you could do with a better text editor - one that has “parentheses matching”. Counting the brackets will send you crazy :wink:

Try NotePad++ (https://notepad-plus-plus.org/) and set the language option to “Lisp” (There are enough similarities between the “Nyquist” and “Lisp” to make the syntax highlighting useful, even though not complete).

Apart from mismatched parentheses, there are just a couple of mistakes.

In the last line of your “tone” function, you have a variable called “tone”, but that variable has not been defined. In fact, you don’t need that variable at all. All you need to do is to multiply three things: the sound, the envelope and the random scale factor.

The other error is in the final line. You are using “tone” as is if it is a “variable” when actually it is now a “function”.

Suggestion: Now that both “rest” and “tone” are functions, we don’t need to CUE them. They are simply generated (as “behaviors”) when and where SEQREP requires them, so that last couple of lines may be simplified as:

(seqrep (i 200)
  (seq (rest) (tone)))
;version 4
;type generate
(defun tone ()
  (mult (osc (hz-to-step 1000) 0.25))
        (pwlv 0 0.02 1 0.23 1 0.25 0)
(mult (db-to-linear (* -5 (+ 1 (random 10))))))

(defun rest ()
  (s-rest (+ 1.8 (rrandom))))
  
(seqrep (i 200)
  (seq (rest) (tone)))

I think every parentheses match in this code above, cause I checked in Notepad++ and I still get errors after debugging. What can be wrong ? Thanks once again for a tremendous help :slight_smile:

Your errors are …?

I will paste everything

error: bad argument type - 0.316228
Function: #<Subr-SND-SRATE: #68781f8>
Arguments:
  0.316228
Function: #<FSubr-LET: #687b358>
Arguments:
  ((SND-SR (SND-SRATE SND)))
  (COND ((> SR SND-SR) (SND-UP SR SND)) ((< SR SND-SR) (SND-DOWN SR SND)) (T SND))
Function: #<FSubr-COND: #687b458>
Arguments:
  ((ARRAYP SND) (LET* ((LEN (LENGTH SND)) (RESULT (MAKE-ARRAY LEN))) (DOTIMES (I LEN) (SETF (AREF RESULT I) (FORCE-SRATE SR (AREF SND I)))) RESULT))
  (T (LET ((SND-SR (SND-SRATE SND))) (COND ((> SR SND-SR) (SND-UP SR SND)) ((< SR SND-SR) (SND-DOWN SR SND)) (T SND))))
Function: #<Closure-FORCE-SRATE: #68c6a38>
Arguments:
  44100
  0.316228
Function: #<FSubr-PROGV: #6879578>
Arguments:
  (QUOTE (*WARP*))
  (IF (WARP-FUNCTION *WARP*) (LIST (LIST (SREF-INVERSE (WARP-FUNCTION *WARP*) T0) (WARP-STRETCH *WARP*) (WARP-FUNCTION *WARP*))) (LIST (LIST T0 (WARP-STRETCH *WARP*) NIL)))
  (CHECK-T0 (FORCE-SRATE S%RATE (TONE)) (QUOTE (FORCE-SRATE S%RATE (TONE))))
Function: #<FSubr-PROGV: #6879578>
Arguments:
  (QUOTE (*WARP* *SUSTAIN* *START* *LOUD* *TRANSPOSE* *STOP* *CONTROL-SRATE* *SOUND-SRATE*))
  (QUOTE ((0 1 NIL) 1 -1e+021 0 0 1e+021 2205 44100))
  (AT-ABS T0 (FORCE-SRATE S%RATE (TONE)))
Function: #<Closure: #6924990>
Arguments:
  2.70984
1>

I’ll try to show you how I approach debugging…

The first line:

error: bad argument type - 0.00316228

>

Looking through the code, there's only one place in the code that can have come from, and that's dB amplitude value.

Let's check...
In the code we have:

```text
(db-to-linear (* -5 (+ 1 (random 10))))

so if that is where the first error came from, then “0.00316228” should be equivalent to a dB value that is a multiple of 5, so we can check if it is:

(print (linear-to-db 0.00316228))

and sure enough, that’s -50 dB (prints as -49.999994)

So now we look at the context. Find the first pair of matched parentheses outside of the db-to-linear function:

(mult (db-to-linear (* -5 (+ 1 (random 10)))))

What are we multiplying “(db-to-linear (* -5 (+ 1 (random 10))))” with? Nothing!

What do we want to multiply “(db-to-linear (* -5 (+ 1 (random 10))))” with?
We want to multiply it with
(osc (hz-to-step 1000) 0.25))
and
(pwlv 0 0.02 1 0.23 1 0.25 0)

so what we should have here is:

(mult (osc (hz-to-step 1000) 0.25))
      (pwlv 0 0.02 1 0.23 1 0.25 0)
      (db-to-linear (* -5 (+ 1 (random 10))))

Try changing that, check that the parentheses all match up in pairs, and then see if there are further errors.

Huge thanks to you :slight_smile: I checked every parenthesis and now it works perfectly :slight_smile: this is the final code that works :slight_smile:

;version 4
;type generate
(defun tone ()
  (mult (osc (hz-to-step 1000) 0.25)
        (pwlv 0 0.02 1 0.23 1 0.25 0)
(db-to-linear (* -5 (+ 1 (random 10))))))

(defun rest ()
  (s-rest (+ 1.8 (rrandom))))
 
(seqrep (i 200)
  (seq (rest) (tone)))

How can I make it up to you :slight_smile: ?

Congratulations :smiley:

If you wanted to tidy up the indentation to aid readability (highly recommended when dealing with longer code), the final line of the “tone” function could be indented to line up with the other “mult” parameters like this:

(defun tone ()
  (mult (osc (hz-to-step 1000) 0.25)
        (pwlv 0 0.02 1 0.23 1 0.25 0)
        (db-to-linear (* -5 (+ 1 (random 10))))))

Probably not important in this case, but if you wanted to make the code more efficient, we don’t actually need to generate the tone and the envelope for each instance - we could just generate them once and call a new dB gain factor each time like this:

;version 4
;type generate
(defun tone ()
  (mult (osc (hz-to-step 1000) 0.25)
        (pwlv 0 0.02 1 0.23 1 0.25 0)))

(defun rest ()
  (s-rest (+ 1.8 (rrandom))))

(defun gain ()
  (db-to-linear (* -5 (+ 1 (random 10)))))

(let ((tone (tone)))
  (seqrep (i 200)
    (seq (rest) (cue (mult (gain) tone)))))

(this version runs about 2 times faster than the previous version).

Thank you for all the tips and letting me know there is more efficient way to run this code :slight_smile: Out of pure curiosity : is it possible to incorporate 0 dB amplification into this randomized function of amplification to be able to incorporate basic sound into this randomized track ?