mirror of
				https://github.com/cookiengineer/audacity
				synced 2025-11-04 16:14:00 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			159 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			Common Lisp
		
	
	
	
	
	
$nyquist plug-in
 | 
						|
$version 4
 | 
						|
$type process
 | 
						|
$name (_ "Noise Gate")
 | 
						|
$manpage "Noise_Gate"
 | 
						|
$action (_ "Gating audio...")
 | 
						|
$debugbutton false
 | 
						|
$preview enabled
 | 
						|
$author (_ "Steve Daulton")
 | 
						|
$release 2.4.0
 | 
						|
$copyright (_ "Released under terms of the GNU General Public License version 2")
 | 
						|
 | 
						|
;; Released under terms of the GNU General Public License version 2:
 | 
						|
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html .
 | 
						|
 | 
						|
 | 
						|
$control mode (_ "Select Function") choice ((_ "Gate")
 | 
						|
                                            ("Analyze" (_ "Analyse Noise Level")))
 | 
						|
                                            0
 | 
						|
$control stereo-link (_ "Stereo Linking") choice (("LinkStereo" (_ "Link Stereo Tracks"))
 | 
						|
                                                  ("DoNotLink" (_ "Don't Link Stereo")))
 | 
						|
                                                  0
 | 
						|
$control low-cut (_ "Apply Low-Cut filter") choice ((_ "No")
 | 
						|
                                                    ("10Hz" (_ "10Hz 6dB/octave"))
 | 
						|
                                                    ("20Hz" (_ "20Hz 6dB/octave")))
 | 
						|
                                                    0
 | 
						|
;; Work around bug 2336.
 | 
						|
$control gate-freq (_ "Gate frequencies above (kHz)") float "" 0 0 10
 | 
						|
$control level-reduction (_ "Level reduction (dB)") float "" -12 -100 0
 | 
						|
$control threshold (_ "Gate threshold (dB)") float "" -48 -96 -6
 | 
						|
$control attack (_ "Attack/Decay (ms)") float "" 250 10 1000
 | 
						|
 | 
						|
 | 
						|
; Global variables
 | 
						|
(setq silence (if (> level-reduction -96) 0 1)) ; flag for silence
 | 
						|
(setq freq (* 1000.0 gate-freq))
 | 
						|
(setq level-reduction (db-to-linear level-reduction))
 | 
						|
(setq threshold (db-to-linear threshold))
 | 
						|
(setq attack (/ attack 1000.0))
 | 
						|
(setq look attack)  ; lookahead
 | 
						|
(setq decay attack) ; this could be replaced with a slider if required
 | 
						|
 | 
						|
 | 
						|
(defun error-check ()
 | 
						|
  (when (>= freq (* *sound-srate* 0.45))  ;10% below Nyquist should be safe maximum.
 | 
						|
    ;; Work around bug 2012.
 | 
						|
    (throw 'err (format nil (_ "Error.
 | 
						|
\"Gate frequencies above: ~s kHz\"
 | 
						|
is too high for selected track.
 | 
						|
Set the control below ~a kHz.")
 | 
						|
                        gate-freq
 | 
						|
                        (roundn (* 0.00045 *sound-srate*) 1))))
 | 
						|
  (when (< len 100) ;100 samples required 
 | 
						|
    ;; Work around bug 2012.
 | 
						|
    (throw 'err (format nil (_ "~%Insufficient audio selected.
 | 
						|
Make the selection longer than ~a ms.")
 | 
						|
                        (round-up (/ 100000 *sound-srate*))))))
 | 
						|
 | 
						|
 | 
						|
;;; Analysis functions:
 | 
						|
 | 
						|
(defun analyze (sig)
 | 
						|
  ; Return analysis text.
 | 
						|
  (let* ((test-length (truncate (min len (/ *sound-srate* 2.0))))
 | 
						|
         (levm (peak-avg-db sig test-length))
 | 
						|
         (target (* 0.925 levm))) ;suggest 7.5% above noise level
 | 
						|
    ;; Work around bug 2012.
 | 
						|
    (format nil (_ "Peak based on first ~a seconds ~a dB~%
 | 
						|
Suggested Threshold Setting ~a dB.")
 | 
						|
            (roundn (/ test-length *sound-srate*) 2)
 | 
						|
            (roundn levm 2)
 | 
						|
            (roundn target 0))))
 | 
						|
 | 
						|
(defun peak-avg-db (sig test-len)
 | 
						|
  ;; Return average of positive/ negative peaks (dB).
 | 
						|
  ;; For stereo tracks, return the maximum of the channels.
 | 
						|
  (if (arrayp sig)
 | 
						|
      (let ((avgL (peak-avg (aref sig 0) test-len))
 | 
						|
            (avgR (peak-avg (aref sig 1) test-len)))
 | 
						|
        (linear-to-db (max avgL avgR)))
 | 
						|
      (let ((avg (peak-avg sig test-len)))
 | 
						|
        (linear-to-db avg))))
 | 
						|
 | 
						|
(defun peak-avg (sig test-len)
 | 
						|
  ;; Return average of positive/ negative peaks.
 | 
						|
  ;; We use the average for the analysis so as to ignore
 | 
						|
  ;; DC offet (assuming noise is approximately symetrical).
 | 
						|
  (let* ((pos (s-max sig 0))
 | 
						|
         (neg (s-min sig 0))
 | 
						|
         (lev (peak pos test-len))
 | 
						|
         (inv-lev (peak neg test-len)))
 | 
						|
    (/ (+ lev inv-lev) 2.0)))
 | 
						|
 | 
						|
 | 
						|
;;; Utility functions
 | 
						|
 | 
						|
(defun round-up (num)
 | 
						|
  (round (+ num 0.5)))
 | 
						|
 | 
						|
(defun roundn (num places)
 | 
						|
  ;; Round number to specified decimal places.
 | 
						|
  (if (= places 0)
 | 
						|
      (round num)
 | 
						|
      (let* ((x (format NIL "~a" places))
 | 
						|
             (ff (strcat "%#1." x "f")))
 | 
						|
        (setq *float-format* ff)
 | 
						|
        (format NIL "~a" num))))
 | 
						|
 | 
						|
 | 
						|
;;; Gate Functions
 | 
						|
 | 
						|
(defun noisegate (sig gatefollow lookahead risetime falltime floor threshold Hz)
 | 
						|
  (let ((gain (/ (- 1 (* silence floor))))
 | 
						|
        (env (get-env gatefollow lookahead risetime falltime floor threshold)))
 | 
						|
    (if (> Hz 20)
 | 
						|
        (sim (mult (highpass8 sig Hz) gain env)
 | 
						|
             (lowpass8 sig (* 0.91 Hz))) ;magic number 0.91 improves crossover.
 | 
						|
        (mult sig gain env))))
 | 
						|
 | 
						|
(defun get-env (follow look rise fall floor thresh)
 | 
						|
  ;; Return gate's envelope
 | 
						|
  (let* ((gate-env (gate follow look rise fall floor thresh))
 | 
						|
         (gate-env (clip gate-env 1.0)))  ;gain must not exceed unity.
 | 
						|
    (diff gate-env (* silence floor))))
 | 
						|
 | 
						|
(defun gate-follow (sig mode)
 | 
						|
  ;; Return signal that gate will follow.
 | 
						|
  (setf sig (hp sig 20)) ; hp filter to remove DC off-set
 | 
						|
  (if (and (= mode 0)(arrayp sig)) ; stereo track set to mono/link
 | 
						|
      (s-max (s-abs (aref sig 0)) (s-abs (aref sig 1)))
 | 
						|
      (s-abs sig)))
 | 
						|
 | 
						|
(defun hpfilter (sig)
 | 
						|
  ;; First order filters to minimise ringing.
 | 
						|
  (case low-cut
 | 
						|
    (1 (hp sig 10))
 | 
						|
    (2 (hp sig 20))
 | 
						|
    (T sig)))
 | 
						|
 | 
						|
(defun process ()
 | 
						|
  ;; Main function
 | 
						|
  (error-check)
 | 
						|
  (let ((sig (hpfilter *track*)))
 | 
						|
        (setf *track* nil)
 | 
						|
        (setf gatefollow (gate-follow sig stereo-link))
 | 
						|
        (multichan-expand #' noisegate sig
 | 
						|
                                       gatefollow
 | 
						|
                                       look
 | 
						|
                                       attack
 | 
						|
                                       decay
 | 
						|
                                       level-reduction
 | 
						|
                                       threshold
 | 
						|
                                       freq)))
 | 
						|
 | 
						|
;; Run program
 | 
						|
(case mode
 | 
						|
  (0 (catch 'err (process)))
 | 
						|
  (T (analyze *track*)))
 |