1$nyquist plug-in
2$version 3
3$type process
4$preview enabled
5$name (_ "Vocoder")
6$manpage "Vocoder"
7$action (_ "Processing Vocoder...")
8$author (_ "Edgar-RFT")
9$release 2.3.0
10$copyright (_ "Released under terms of the GNU General Public License version 2")
11
12;; vocoder.ny by Edgar-RFT
13;; a bit of code added by David R. Sky
14;; GUI update by Steve Daulton July 2012.
15;; Performance improvement, error message for mono, code cleanup
16;; by Paul Licameli and Steve Daulton October 2014
17
18;; Released under terms of the GNU General Public License version 2:
19;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20;;
21;; For information about writing and modifying Nyquist plug-ins:
22;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference
23
24$control dst (_ "Distance: (1 to 120, default = 20)") real "" 20 1 120
25$control mst (_ "Output choice") choice (
26   ("BothChannels" (_ "Both Channels"))
27   ("RightOnly" (_ "Right Only"))
28) 0
29$control bands (_ "Number of vocoder bands") int "" 40 10 240
30$control track-vl (_ "Amplitude of original audio (percent)") real "" 100 0 100
31$control noise-vl (_ "Amplitude of white noise (percent)") real "" 0 0 100
32$control radar-vl (_ "Amplitude of Radar Needles (percent)") real "" 0 0 100
33$control radar-f (_ "Frequency of Radar Needles (Hz)") real "" 30 1 100
34
35; maybe the code once again has to be changed into _one_ local let-binding
36; if you have lots of nyquist "[gc:" messages try this:
37; (expand 100) ; gives Xlisp more memory but I have noticed no speed difference
38
39;; number of octaves between 20hz and 20khz
40(setf octaves (/ (log 1000.0) (log 2.0)))
41
42;; convert octaves to number of steps (semitones)
43(setf steps (* octaves 12.0))
44
45;; interval - number of steps per vocoder band
46(setf interval (/ steps bands))
47
48;;; Some useful calculations but not used in this plugin
49
50;; half tone distance in linear
51; (print (exp (/ (log 2.0) 12)))
52
53;; octave distance in linear
54; (print (exp (/ (log 1000.0) 40)))
55
56;;; The Radar Wavetable
57
58;; make *radar-table* a global variable.
59(setf contol-dummy *control-srate*)   ; save old *control-srate*
60(set-control-srate *sound-srate*)
61(setf *radar-table* (pwl (/ 1.0 *control-srate*) 1.0  ; 1.0 after 1 sample
62                         (/ 2.0 *control-srate*) 0.0  ; 0.0 after 2 samples
63                         (/ 1.0 radar-f))) ; stay 0.0 until end of the period
64(set-control-srate contol-dummy)      ; restore *control-srate*
65;; make *radar-table* become a nyquist wavetable of frequency radar-f
66(setf *radar-table* (list *radar-table* (hz-to-step radar-f) T))
67
68;; increase the volume of the audacity track in the middle of the slider
69;; the sqrt trick is something like an artificial db scaling
70(setf track-vol (sqrt (/ track-vl 100.0)))
71;; decrease the volume of the white noise in the middle of the slider
72;; the expt trick is an inverse db scaling
73(setf noise-vol (expt (/ noise-vl 100.0) 2.0))
74;; also increase the volume of the needles in the middle of the slider
75(setf radar-vol (sqrt (/ radar-vl 100.0)))
76
77;;; here you can switch the tracks on and off for bug tracking
78
79;  (setf radar-vol 0)
80;  (setf noise-vol 0)
81;  (setf track-vol 0)
82
83;;; The Mixer
84
85;; calculate duration of audacity selection
86(setf duration (/ len *sound-srate*))
87
88(defun mix ()
89  ;; if track volume slider is less than 100 percent decrease track volume
90  (if (< track-vl 100)
91      (setf s
92            (vector (aref s 0) (scale track-vol (aref s 1)))))
93
94  ;; if radar volume slider is more than 0 percent add some radar needles
95  (if (> radar-vl 0)
96      (setf s
97            (vector (aref s 0)
98                    (sim (aref s 1)
99                         (scale radar-vol (osc (hz-to-step radar-f)
100                                               duration *radar-table*))))))
101
102  ;; if noise volume slider is more than 0 percent add some white noise
103  (if (> noise-vl 0)
104      (setf s
105            (vector (aref s 0)
106                    (sim (aref s 1) (scale noise-vol (noise duration)))))))
107
108;;; The Vocoder
109
110(defun vocoder ()
111  (do* ((i 0 (1+ i))
112        mod-envelope  ; local variable for filtered envelope of left channel.
113        band          ; local variable for band-passed audio.
114        (result 0)    ; result must be initialized because you cannot sum to NIL.
115        (q (/ (sqrt 2.0) (/ octaves bands)))    ; quick approximation of q
116                                                ; for given bandwidth.
117        (p (+ (hz-to-step 20) (/ interval 2.0)) ; midi step of 20 Hz + offset.
118           (+ p interval))
119        (f (step-to-hz p) (step-to-hz p)))
120      ((= i bands) result)        ; DO for each band then return 'result'.
121    (setf band (bandpass2 s f q)) ; intermediate results (2 channels)
122    (setf mod-envelope (lowpass8 (s-abs (aref band 0)) (/ f dst)))
123    (setf result
124          (sum result
125               (bandpass2
126                 (mult mod-envelope (aref band 1))
127                 f q)))))
128
129;;; The Program
130
131(cond
132 ((arrayp s)
133  (mix)                     ; changes s
134  (let ((original (or (= mst 0) (aref s 0))))
135    (setf s (vocoder))
136    ;; Now normalize s to 0 db peak
137    (setf s (scale (/ (peak s ny:all)) s))
138    (case mst
139          (0 s)             ; let Audacity coerce back to stereo
140          (1 (vector original s)))))
141 (t                         ; this effect isn't meant for mono
142  (format nil (_ "Error.~%Stereo track required."))))
143