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