Help with pwlv

I’m learning Nyquist. I did a plugin that would make a marked section silent with a steep fadeout in the beginning and a fadein at the end. The following image shows the effect:

The left edge is ok, but at the right edge you see something strange. This is a magnified part of the right edge of another case:

Some samples before the right edge, the fadein turns into a fadeout all of a sudden. It really is not audible, it’s only 19 samples long, but I guess in some cases it will be audible.
I’m using the pwlv function to shape an envelope for the volume (or amplitude or whatyoucallit). Here’s the whole code:

;nyquist plug-in
;version 1
;type process
;name "Soft zeroing"
;action "Do the thing..."
;control fade "Fade" real "ms" 10 1 500
(setq percnt (/ (/ fade 1000.0) (/ len *sound-srate*)))
(setq a (pwlv 1.0 percnt 0.0 (- 1.0 percnt) 0.0 1.0 1.0))
(mult a s)

So I have a slider to set the length of the fadeout and fadein, between 1 and 500 milliseconds. Then some math and stuff to turn it into a percentage (um… from 0 to 1, not from 0 to 100, but you get the idea). The whole pwlv line should create the envelope for me going through (0, 1) (l, 0) (r, 0) and (1, 1) where l and r would be the end time of the fadeout and start time of the fadein.
My code fails at the far right edge. pwlv doesn’t get it right. It adds a fading to zero as a last point. What’s going on here?

To avoid nasty click-sounds at the beginning or end, a Nyquist envelope is defined as always to start and end with a zero-sample. Because your envelope ends with 1.0, Nyquist adds a fade to zero at the end.

The solution is described in the Nyquist manual under Piece-wise Linear Functions:

If you really want a linear ramp to reach its final value at the specified time, you need to make a signal that is one sample longer.

But this reads easier than it really is, because “pwl” envelopes are computed with control-srate (in Audacity always 44100/20=2205Hz), not with sound-srate (the sample rate of the Audacity track). That’s the reason why the zero-fade at the end is more than one sample long. One sample in control-srate are several samples in sound-srate.

Below is a “fade” function where you can give “fade-out” (at the beginning) and “fade-in” (at the end) with times in seconds. If no “fade-in” time is given, then the “fade-in” at the end will be the same time as the “fade-out” at the beginning.

(defun fade (sound fade-out &optional fade-in)
  (let* ((seconds  (/ len *sound-srate*))      ; length of sound in seconds
         (end-out  (/ fade-out seconds))       ; end of fade-out in Nyquist time
         (start-in (- 1.0 (if fade-in          ; start of fade-in in Nyquist time
                             (/ fade-in seconds)
                             end-out)))
         (len+1    (* (/ 1.0 len) (1+ len))))  ; Nyquist time at len + 1 sample
    (progv '(*control-srate*) (list *sound-srate*)
      (mult sound (pwl 0.0 1.0 end-out 0.0 start-in 0.0 1.0 1.0 len+1)))))

“progv” sets control-srate temporarily to sound-srate, so the “pwl” envelope gets computed with sound-srate precision.

Examples:

(fade s 0.2 0.5)  ; 0.2 seconds fade-out, 0.5 seconds fade-in
(fade s 0.3)      ; 0.3 seconds fade-out, 0.3 seconds fade-in

Note that currently there is no check if “fade-in” + “fade-out” together are longer than the sound itself, so “pwl” will compute nonsense in this case.

Am I correct in thinking that

(progv '(*control-srate*) (list *sound-srate*) beh)

does the same as

(control-srate-abs *sound-srate* beh)

Yes, “nyquist.lsp” says:

(defmacro control-srate-abs (r s)
  `(progv '(*CONTROL-SRATE*) (list ,r)
      ,s))

And (1.0/len)*(1+len) is the longwinded version of (1+len)/len, so here now simplified and with “control-srate-abs”:

(defun fade (sound fade-out &optional fade-in)
  (let* ((seconds  (/ len *sound-srate*))  ; length of sound in seconds
         (end-out  (/ fade-out seconds))   ; end of fade-out in Nyquist time
         (start-in (- 1.0 (if fade-in      ; start of fade-in in Nyquist time
                              (/ fade-in seconds)
                              end-out)))
         (len+1    (/ (1+ len) len)))      ; Nyquist time at len + 1 sample
    (control-srate-abs *sound-srate*
      (mult sound (pwl 0.0 1.0 end-out 0.0 start-in 0.0 1.0 1.0 len+1)))))

Thanks Edgar.

Hi
I’m completely new to Nyquist and just starting to learn the plugins
I tried this code and the result is something like this. There is no fade out, silence, fadein
Any help would be highly appreciated.
Thanks
nyq_1.png

Wow, this is an old thread.

Nyquist has evolved quite a lot over the last 9 years.
Here’s an updated version using “version 4” syntax. I’ve commented everything to explain what each code line does:

;nyquist plug-in
;version 4
;type process
;name "Soft zeroing"
;action "Do the thing..."
;control fade "Fade length" float "ms" 10 1 500

;; Fade length as proportion of selection
(setf fade (/ (/ fade 1000) (get-duration 1)))

;; Ensure that fade length is no more than half the duration
(setf fade (min (get-duration 0.5) fade))

;; Do fade with pwlv at same sample rate as the track
(control-srate-abs *sound-srate*
  (mult *track* (pwlv 1 fade 0 (- 1 fade) 0 1 1)))

Thank you so much steve,
this thread is so helpful to me, as Im learning the basics.
thank you,

Just as a matter of interest, if you prefer a logarithmic fade, you could use PWEV rather than PWLV. The one thing to be careful of though, is that log zero is “undefined”. To avoid log zero, create the gain envelope offset a little way above zero, then subtract the offset to bring the envelope back down to zero:

;nyquist plug-in
;version 4
;type process
;name "Soft zeroing"
;action "Do the thing..."
;control fade "Fade length" float "ms" 10 1 500

;; Fade length as proportion of selection
(setf fade (/ (/ fade 1000) (get-duration 1)))

;; Ensure that fade length is no more than half the duration
(setf fade (min (get-duration 0.5) fade))

;; Log zero is "undefined", so we use a small "epsilon" value
(setf eps 0.01)

(control-srate-abs *sound-srate*
  (mult *track*
    (diff (pwev (+ 1 eps) fade eps (- 1 fade) eps 1 (+ 1 eps)) eps)))