1$nyquist plug-in
2$version 4
3$type process
4$name (_ "Noise Gate")
5$manpage "Noise_Gate"
6$debugbutton false
7$preview enabled
8$author (_ "Steve Daulton")
9$release 3.0.4
10$copyright (_ "GNU General Public License v2.0 or later")
11
12;; Released under terms of the GNU General Public License v2.0 or later:
13;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html .
14
15$control mode (_ "Select Function") choice (("Gate" (_ "Gate"))
16                                            ("Analyze" (_ "Analyze Noise Level")))
17                                           0
18$control stereo-link (_ "Stereo Linking") choice (("LinkStereo" (_ "Link Stereo Tracks"))
19                                                  ("DoNotLink" (_ "Don't Link Stereo")))
20                                                 0
21;; Work around bug 2336 - Text after control is not read by screen reader.
22$control threshold (_ "Gate threshold (dB)") float "" -40 -96 -6
23$control gate-freq (_ "Gate frequencies above (kHz)") float "" 0 0 10
24$control level-reduction (_ "Level reduction (dB)") float "" -24 -100 0
25$control attack (_ "Attack (ms)") float "" 10 1 1000
26$control hold (_ "Hold (ms)") float "" 50 0 2000
27$control decay (_ "Decay (ms)") float "" 100 10 4000
28
29
30;; The gain envelope for the noisegate function may be a mono sound (stereo-link = 1, or *track* is mono)
31;; or an array of sounds (stereo-link = 0 and *track* is stereo).
32;; 'Level Reduction' is similar to "Range" or "Floor", but is a (negative) amount of gain
33;; rather than a fixed level.
34;;
35;; To create the gain envelope:
36;; 1. If stereo track and stereo-link = 1, get the max of left and right.
37;; 2. Add 'hold' when level > threshold.
38;;    This adds a high level signal for 'hold' seconds when the level
39;;    falls below the threshold.
40;; 3. Nyquist GATE function to generate exponential rise and decay.
41;;    Unlike analog noise gates, lookahead is used so that the gate
42;;    begins to open before the signal rises above the threshold.
43;;    When the threshold is reached, the gate is fully open.
44;;    This prevents the gate from clipping the beginning of words / sounds.
45;; 4. Scale level of envelope and offset so that we have unity gain above
46;;    threshold, and 'level-reduction' below the threshold.
47;;    If silence-flag is set (= 1), gain below the threshold is zero.
48
49
50; Global variables
51(setf silence-flag (if (> level-reduction -96) 0 1))
52(setf gate-freq (* 1000.0 gate-freq))
53(setf floor (db-to-linear level-reduction))
54(setf threshold (db-to-linear threshold))
55(setf attack (/ attack 1000.0))
56(setf lookahead attack)
57(setf decay (/ decay 1000.0))
58(setf hold (/ hold 1000.0))
59
60
61(defun error-check ()
62  (let ((max-hz (* *sound-srate* 0.45))  ;10% below Nyquist should be safe maximum.
63        (max-khz (roundn (* 0.00045 *sound-srate*) 1))
64        (gate-freq-khz (roundn (/ gate-freq 1000.0) 1)))
65    (when (>= gate-freq max-hz)
66      ;; Work around bug 2012.
67      (throw 'err (format nil (_ "Error.
68\"Gate frequencies above: ~s kHz\"
69is too high for selected track.
70Set the control below ~a kHz.")
71                        gate-freq-khz
72                        max-khz))))
73  (let ((start (get '*selection* 'start))
74        (end (get '*selection* 'end)))
75    (when (> (* *sound-srate* (- end start)) (1- (power 2 31)))
76      ;; Work around bug 2012 and 439.
77      (throw 'err (format nil (_ "Error.
78Selection too long.
79Maximum length is ~a.") (format-time (/ (1- (power 2 31)) *sound-srate*))))))
80  (when (< len 100) ;100 samples required
81    ;; Work around bug 2012.
82    (throw 'err (format nil (_ "Error.
83Insufficient audio selected.
84Make the selection longer than ~a ms.")
85                        (round-up (/ 100000 *sound-srate*))))))
86
87
88;;; Analysis functions:
89;; Measure the peak level (dB) and suggest setting threshold a little higher.
90
91(defun analyze (sig)
92  ; Return analysis text.
93  (let* ((test-length (truncate (min len (/ *sound-srate* 2.0))))
94         (peakdb (peak-db sig test-length))
95         (target (+ 1.0 peakdb))) ;suggest 1 dB above noise level
96    ;; Work around bug 2012.
97    (format nil (_ "Peak based on first ~a seconds ~a dB~%
98Suggested Threshold Setting ~a dB.")
99            (roundn (/ test-length *sound-srate*) 2)
100            (roundn peakdb 2)
101            (roundn target 0))))
102
103(defun peak-db (sig test-len)
104  ;; Return absolute peak (dB).
105  ;; For stereo tracks, return the maximum of the channels.
106  (if (arrayp sig)
107      (let ((peakL (peak (aref sig 0) test-len))
108            (peakR (peak (aref sig 1) test-len)))
109        (linear-to-db (max peakL peakR)))
110      (linear-to-db test-len)))
111
112
113;;; Utility functions
114
115(defun round-up (num)
116  (round (+ num 0.5)))
117
118(defun roundn (num places)
119  ;; Return number rounded to specified decimal places.
120  (if (= places 0)
121      (round num)
122      (let* ((x (format NIL "~a" places))
123             (ff (strcat "%#1." x "f")))
124        (setq *float-format* ff)
125        (format NIL "~a" num))))
126
127(defun format-time (s)
128  ;;; format time in seconds as h m.
129  (let* ((hh (truncate (/ s 3600)))
130         (mm (truncate (/ s 60))))
131  ;i18n-hint: hours and minutes. Do not translate "~a".
132  (format nil (_ "~ah ~am") hh (- mm (* hh 60)))))
133
134
135;;; Gate Functions
136
137
138(defun noisegate (sig follow)
139  ;; Takes a sound and a 'follow' sound as arguments.
140  ;; Returns the gated audio.
141  (let ((gain (/ (- 1 (* silence-flag floor)))) ; silence-flag is 0 or 1.
142        (env (get-env follow)))
143    (if (> gate-freq 20)
144        (let* ((high (highpass8 sig gate-freq))
145               (low  (lowpass8 sig (* 0.91 gate-freq)))) ;magic number 0.91 improves crossover.
146          (sim (mult high gain env) low))
147        (mult sig gain env))))
148
149(defun get-env (follow)
150  ;; Return gate's envelope
151  (let* ((gate-env (gate follow lookahead attack decay floor threshold))
152         (gate-env (clip gate-env 1.0)))  ;gain must not exceed unity.
153    (diff gate-env (* silence-flag floor))))
154
155(defun peak-follower (sig)
156  ;; Return signal that gate will follow.
157  (setf sig (multichan-expand #'snd-abs sig))
158  (when (and (arrayp sig)(= stereo-link 0))
159    (setf sig (s-max (aref sig 0) (aref sig 1))))
160  (if (> hold 0)
161      (multichan-expand #'snd-oneshot sig threshold hold)
162      sig))
163
164(defun process ()
165  (error-check)
166  ;; For stereo tracks, 'peak-follower' may return a sound
167  ;; or array of sounds, so pass it to 'noisegate' rather than
168  ;; calculating in 'noisegate'.
169  (multichan-expand #' noisegate *track* (peak-follower *track*)))
170
171
172;; Run program
173(case mode
174  (0 (catch 'err (process)))
175  (T (analyze *track*)))
176