Switch with the Tempo (with Wha) Effect

Hello to somebody that would read this, and scuse me in advance for my english mistake.
I tried to make this plugin wich Switch on/off cadenced with the tempo, quite simple, but it can give some idea or help with the comment (i tried to comment as much as possible). There are some rise/fall time on switching.
I also add a wha effect (resonant filter sweeping on freq wich is controled by oscillator), but i guess that other guys have already made this better than me. But finally it works not so bad, for both mono and stereo.

PS: it’s important to put Wha effect stage before Switching and not after, because if not, you hear “pouic-pouic”, i guess it is because off the thing called the “side-effect”… maybe im not sure.







;nyquist plug-in
;version 3
;type process
;name "Switch on Tempo with Wha"
;action "Processing Switch on Tempo with Wha"
;info "          Gate-like switched on tempo with Wha (Wha can be removed) \n"


;control bpm "Switch bpm (tempo)" real "bpm" 160 10 350
;control sterex "Stereo alternance (disabled for mono track)" real "" 0.5 0 1
;control pwmswtch "Sitch PWM (time on/off per periode)" real "" 0.7 0.5 0.9
;control widthwha "Width of sweep" real "" 3.5 2 4
;control bandfact "Resonant bandwidth factor" real "" 0.8 0.5 1
;control centerfreq "Center frequency of sweep" real "Hz" 632 200 3000
;control sweepspeed "Speed of sweep" real "Hz" 0.4 0.05 2
;control compgain "Attenuation/compensation gain" real "dB" -10 -30 7
;control whafact "Remove Wha effect (dry/wet)" real "" 1 0 1


; this function return duration of selection (stereo signal)
(defun timeselec (sign)

(setq lefts (aref sign 0))                     ; aref function to get only one channel (here left=0)
(setq stoplist (snd-extent lefts 10000000 ) )  ; snd-extent to return duration in a list 
(setq stopt (nth 1 stoplist ) )                ; extract duration from previous list

)

; this function return duration of selection (mono signal)
(defun timeselecmono (sign)

(setq stoplist (snd-extent sign 10000000 ) )    ; snd-extent to return duration in a list
(setq stopt (nth 1 stoplist ) )                 ; extract duration from previous list

)


; this function generate a square wave in mono signal
; wich have duration of selection, and have freq and pwm set by user

(defun mksqare-mono (stoptime bpmset pwmset)

(setq demi-per (/ (/ 1 (/ bpmset 60)) 4))        ; half period
(setq nbpoint (/ stopt demi-per))                ; finally i didn't use it (number of pair in list)
(setq logidur (/ demi-per (/ stopt 20)))         ; i had some problem with logic and real time
(setq logidur (/ logidur 20))                    ; i don't have more explanation but it work

(setq time-per (/ stopt (* demi-per 2)))         ; number of period
(setq rifa (/ logidur 10) )                      ; rise and fall times are 1/10 period (rifa=risefall)
(setq pwmset (* (- pwmset 0.5) 2) )              ; convert pwm user value in an addition term

(setq count 0)                                   ; set counter of while wich is below
(setq point1 (* logidur (float count)) )
(setq point2 (* logidur (+ (float count) (+ (float 1) pwmset)) ) )  ; setting x values (time axis)
(setq point3 (* logidur (+ count 2)) )                              ; point2 is modulated according
                                                                    ; to pwm value
(setq point4 (+ point2 rifa ))                                      ; rifa follow point2 modulation
(setq point5 (- point3 rifa ))

(setq pair1 (list point1 1))                                        ; set pair in some lists
(setq pair2 (list point2 1))                                        ; with x value, y is 0 or 1
(setq pair3 (list point4 0))                                        ; 4 pairs= 4 points a period
(setq pair4 (list point5 0))

(setq list3 (nconc pair1 pair2 pair3 pair4))    ; using nconc funtion to concatenate in single list



(while (< count (* (- time-per 1) 2))           ; while repeat the same as previously
                                                ; for the number of period (one loop is already done)
   (setq count (+ count 2))
   (setq point1 (* logidur (float count)) )
   (setq point2 (* logidur (+ (float count) (+ (float 1) pwmset)) ) )  ; setting x values (time axis)
   (setq point3 (* logidur (+ count 2)) )                              ; point2 is modulated according
   (setq point6 (+ point2 rifa) )                                      ; to pwm value
   (setq point7 (- point3 rifa) )                                      ; rifa follow point2 modulation

   (setq pair1 (list point1 1))                                      ; set pair in some lists
   (setq pair2 (list point2 1))                                      ; with x value, y is 0 or 1
   (setq pair3 (list point6 0))                                      ; 4 pairs= 4 points a period
   (setq pair4 (list point7 0))

   (setq list3 (nconc list3 pair1 pair2 pair3 pair4))   ; using nconc funtion to concatenate
                                                        ; in a single list
  )


(setq pwm1 (pwl-list list3))                            ; pwl-list function generate
                                                        ; the square wave with all points
)                                                       ; that are in the previous list



; this function generate a "half square" wave for left stereo signal
; wich have duration of selection, and have freq and pwm set by user
; the period is 2 time more long than mono version
; because one empty period is added for stereo a alternance

(defun mksqareleft (stoptime bpmset pwmset)

(setq demi-per (/ (/ 1 (/ bpmset 60)) 4))            ; half period
(setq nbpoint (/ stopt demi-per))                    ; finally i didn't use it (number of pair in list)
(setq logidur (/ demi-per (/ stopt 20)))             ; i had some problem with logic and real time
(setq logidur (/ logidur 20))                        ; i don't have more explanation but it work

(setq time-per (/ stopt (* demi-per 2)))             ; number of period
(setq rifa (/ logidur 10) )                       ; rise and fall times are 1/10 period (rifa=risefall)
(setq pwmset (* (- pwmset 0.5) 2) )                 ; convert pwm user value in an addition term

(setq count 0)                                      ; set counter of while wich is below
(setq point1 (* logidur (float count)) )
(setq point2 (* logidur (+ (float count) (+ (float 1) pwmset)) ) )  ; setting x values (time axis)
(setq point3 (* logidur (+ count 2)) )                              ; point2 is modulated according
(setq point8 (* logidur (+ count 3)) )                              ; to pwm value
(setq point9 (* logidur (+ count 4)) )                              ; rifa follow point2 modulation

(setq point4 (+ point2 rifa )) 
(setq point5 (- point9 rifa ))

(setq pair1 (list point1 1))                                        ; set pair in some lists
(setq pair2 (list point2 1))                                        ; with x value, y is 0 or 1
(setq pair3 (list point4 0))                                        ; 6 pairs= 6 points a period
(setq pair4 (list point3 0))                                        ; here one period is 2 time more
(setq pair5 (list point8 0))                                        ; long than mono version
(setq pair6 (list point5 0))


(setq list3 (nconc pair1 pair2 pair3 pair4 pair5 pair6))      ; using nconc funtion to concatenate
                                                              ; in a single list

(while (< count (* (- time-per 1) 2))                         ; while repeat the same as previously
                                                              ; for the number of period
   (setq count (+ count 4))                                   ; (one loop is already done)
   (setq point1 (* logidur (float count)) )
   (setq point2 (* logidur (+ (float count) (+ (float 1) pwmset)) ) )  ; setting x values (time axis)
   (setq point3 (* logidur (+ count 2)) )                              ; point2 is modulated according
   (setq point8 (* logidur (+ count 3)) )                              ; to pwm value
   (setq point9 (* logidur (+ count 4)) )                              ; rifa follow point2 modulation

   (setq point4 (+ point2 rifa ))
   (setq point5 (- point9 rifa ))

   (setq pair1 (list point1 1))                                        ; set pair in some lists
   (setq pair2 (list point2 1))                                        ; with x value, y is 0 or 1
   (setq pair3 (list point4 0))                                        ; 6 pairs= 6 points a period
   (setq pair4 (list point3 0))                                        ; here a period is 2 time more
   (setq pair5 (list point8 0))                                        ; long than mono version
   (setq pair6 (list point5 0))

   (setq list3 (nconc list3 pair1 pair2 pair3 pair4 pair5 pair6))  ; using nconc funtion to concatenate
                                                                   ; in a single list
  )


(setq pwm1 (pwl-list list3))                                       ; pwl-list function generate
                                                                   ; the square wave with all points
                                                                   ; that are in the previous list
)



; this function generate a "half square" wave for left stereo signal
; wich have duration of selection, and have freq and pwm set by user
; the period is 2 time more long than mono version
; because one empty period is added for stereo a alternance

(defun mksqareright (stoptime bpmset pwmset)

(setq demi-per (/ (/ 1 (/ bpmset 60)) 4))            ; half period
(setq nbpoint (/ stopt demi-per))                    ; finally i didn't use it (number of pair in list)
(setq logidur (/ demi-per (/ stopt 20)))             ; i had some problem with logic and real time
(setq logidur (/ logidur 20))                        ; i don't have more explanation but it work

(setq time-per (/ stopt (* demi-per 2)))             ; number of period
(setq rifa (/ logidur 10) )                       ; rise and fall times are 1/10 period (rifa=risefall)
(setq pwmset (* (- pwmset 0.5) 2) )                  ; convert pwm user value in an addition term

(setq count 0)                                       ; set counter of while wich is below
(setq point1 (* logidur (float count)) )
(setq point2 (* logidur (+ (float count) (float 1)) ) )  ; setting x values (time axis)
(setq point3 (* logidur (+ count 2)) )
(setq point8 (* logidur (+ count (+ (float 3) pwmset))) )   ; point8 (=5th) is modulated according
                                                            ; to pwm value
(setq point4 (- point3 rifa ))                              ; rifa follow point8 modulation
(setq point5 (+ point8 rifa ))


(setq pair1 (list point1 0))                                ; set pair in some lists
(setq pair2 (list point2 0))                                ; with x value, y is 0 or 1
(setq pair3 (list point4 0))                                ; 6 pairs= 6 points a period
(setq pair4 (list point3 1))                                ; the period is 2 time more
(setq pair5 (list point8 1))                                ; long than mono version
(setq pair6 (list point5 0))                                ; and the pulse is on 2nd period
                                                            ; for right channel, for stereo alternance

(setq list3 (nconc pair1 pair2 pair3 pair4 pair5 pair6))    ; using nconc funtion to concatenate
                                                            ; in a single list

(while (< count (* (- time-per 1) 2))                         ; while repeat the same as previously
                                                              ; for the number of period
   (setq count (+ count 4))                                   ; (one loop is already done)
   (setq point1 (* logidur (float count)) )                   ; setting x values (time axis)
   (setq point2 (* logidur (+ (float count) (float 1)) ) )
   (setq point3 (* logidur (+ count 2)) )
   (setq point8 (* logidur (+ count (+ (float 3) pwmset))) )  ; point8 (=5th) is modulated according
                                                              ; to pwm value
   (setq point4 (- point3 rifa ))                             ; rifa follow point8 modulation
   (setq point5 (+ point8 rifa ))


   (setq pair1 (list point1 0))                              ; set pair in some lists
   (setq pair2 (list point2 0))                              ; with x value, y is 0 or 1
   (setq pair3 (list point4 0))                              ; 6 pairs= 6 points a period
   (setq pair4 (list point3 1))                              ; the period is 2 time more
   (setq pair5 (list point8 1))                              ; long than mono version
   (setq pair6 (list point5 0))                              ; and the pulse is on 2nd period
                                                             ; for right channel, for stereo alternance

   (setq list3 (nconc list3 pair1 pair2 pair3 pair4 pair5 pair6))  ; using nconc funtion to concatenate
                                                                   ; in a single list
  )


(setq pwm1 (pwl-list list3))                                       ; pwl-list function generate
                                                                   ; the square wave with all points
)                                                                  ; that are in the previous list



; this function use the 3 function previously defined
; It make 2 stereo square signals, using square wave generated by previous function
; (one alterning, one without alterning). Then it multiplies these 2 signals with input, 
; to make them switched by square wave. Then it mix alterning & non-alterning according to user set

(defun gatestereo (sign timestp bpmtm stereo pwmsett)

(setq squarelft (mksqareleft timestp bpmtm pwmsett))      ; use previous function
(setq squarerght (mksqareright timestp bpmtm pwmsett))    ; generate 3 square signals
(setq squaremono (mksqare-mono timestp bpmtm pwmsett))

(setq sqrgain (vector                             ; make stereo signal square
    squarelft                                     ; with one alterned on each channel
    squarerght ) )

(setq dualmono (vector                            ; make "pseudo mono" square wave in stereo signal
   squaremono                                     ; (2 channels have the same signal)
   squaremono ) )

(setq gatemono (mult dualmono sign))              ; multiplies it with input signal to switch it
(setq gatestereo (mult sqrgain sign))             

(setq gatestereo (mult gatestereo stereo))        ; apply gain on alterned and non-alterned signal
(setq gatemono (mult gatemono (- 1 stereo)))      ; according to the user parameter

(setq globalgate (sum gatemono gatestereo)) )     ; mix alterned and non-alterned



; this function generate a mono square wave using the previously defined function mksqare-mono
; then it multiplies square signal with input to switch it
(defun gatemono (sign timestp bpmtm pwmsett)

(setq squaremono (mksqare-mono timestp bpmtm pwmsett))
(setq gatemonout (mult squaremono sign))

)



; *************** Main program ******************



(if (arrayp s)                      ; is it mono or stereo? (is it an array)

(setq durt (timeselec s))           ; get duration selection using previous function  

(setq durt (timeselecmono s)) )


; generate a sinus wave with snd-sine function, duration si the same as selection, frequency is 
; the frequency of wha sweep, between 1=max and 0=mini
(setq modulsine (snd-offset (mult (snd-sine 0 sweepspeed 44100 durt) 0.5) 0.5) )
; constant function --> multiplies amplitude by 0
(setq offsin (snd-offset (mult (snd-sine 0 0.1 44100 durt) 0) 1) )         


; here i try to convert everything in geometric scale (not arithmetic)
; so it began to be a little more complicated

(setq minimodulsine (/ 1 (* widthwha widthwha)) )  ; the minimum frequency center of sweep will be 
(setq geocentmodulsine (/ 1 widthwha) )            ; given by dividing the maximum 2 times by 
(setq modulsinamp (- 1 minimodulsine) )            ; width factor; whereas the ge0metrical center
                                                   ; is given by dividing only one time
                                                   ; The amplitude of modulation sinus is between 
                                                   ; 1 and minimodulsine, amplitude= modulsinamp

; the amplitude of sinus is below decreased by minimodulsine value, so offset need to be increased
; with the same value to keep maximum=1, using snd-offset function
(setq modulsine2 (snd-offset (mult modulsine modulsinamp) minimodulsine) )

; sometime the resonance become very loud, so attenuating factor is added
; Convert the logarithmic user value in linear : 10^(x/10) 
; but the compensation gain also vary with frequency because i noticed that low frequency where
; louder than high frequency, but then i notice the inverse and began to hack the formula

(setq compgain (power 10 (/ compgain 10)))


; at geometrical center of the amplitude of the control sinus, the frequency is the one set by user,
; so the amplitude variation is geometrical, but not the speed of this variation. The bandwith also
; vary with frequency to make it larger on high frequency and smaller on low frequency, according
; to geometrical scale. Compensation gain is added and is hacked inside out (multiplied by itself but
; i think it was to get exponentially increase that foot with geometrical scale) but it works and 
; modulate at the same time with compensationgain and with frequency

(setq resonate (mult (reson s (mult modulsine2 (/ centerfreq geocentmodulsine)) (mult (mult modulsine2 (/ centerfreq geocentmodulsine)) bandfact)) (snd-offset (mult (mult modulsine modulsine) compgain) (/ compgain (* (/ 20 compgain) (* 3 compgain)) )) ) )



(setq resonate (mult resonate whafact)) ; apply gain to each dry/wet wha
(setq sin (mult s (- 1 whafact)))
(setq resonate2 (sum resonate sin))     ; mix dry and wet by adding these signals



(if (arrayp s)                                       ; is it stereo or mono? (is it an array)

(gatestereo resonate2 durt bpm sterex pwmswtch)      ; apply switch function previously defined

(gatemono resonate2 durt bpm pwmswtch) )

Thanks for posting Frenchgaby.
That works well.

Some tips that you may find useful:

As a general rule, new plug-ins should be written as “version 4” plug-ins as the old version 3 syntax is deprecated. The main thing for version 4 are:

  1. Use the header:
;version 4
  1. Rather than the special variable “S” for the track sound, use
*track*

Also, code indentation is a great help for readable Lisp code. There’s a good guide here: http://dept-info.labri.u-bordeaux.fr/~idurand/enseignement/PFS/Common/Strandh-Tutorial/indentation.html
If you find that you have really long lines of code like this:

(setq resonate (mult (reson s (mult modulsine2 (/ centerfreq geocentmodulsine)) (mult (mult modulsine2 (/ centerfreq geocentmodulsine)) bandfact)) (snd-offset (mult (mult modulsine modulsine) compgain) (/ compgain (* (/ 20 compgain) (* 3 compgain)) )) ) )

consider rewriting it. For example, something like:

(setf resonate
  (let ((square-sine (mult modulsine modulsine))
        (raised-sine (/ centerfreq geocentmodulsine))
        (compensation (/ compgain 60)))
    (setf center (mult modulsine2 raised-sine))
    (setf bandwidth (mult modulsine2 raised-sine bandfact))
    (setf resonate (reson s center bandwidth))
    (setf offset (sum (mult square-sine compgain) compensation))
    (mult resonate offset)))