mirror of
https://github.com/cookiengineer/audacity
synced 2025-04-29 15:19:44 +02:00
Cherry-picked from upstream Audacity project as seen here:
df091bbde3
Signed-off-by: Panagiotis Vasilopoulos <hello@alwayslivid.com>
138 lines
4.6 KiB
Common Lisp
138 lines
4.6 KiB
Common Lisp
$nyquist plug-in
|
|
$version 4
|
|
$type process spectral
|
|
$name (_ "Spectral Delete")
|
|
$manpage "Spectral_Delete"
|
|
$author (_ "Steve Daulton")
|
|
$release 3.0.4
|
|
$copyright (_ "GNU General Public License v2.0 or later")
|
|
|
|
;; Released under terms of the GNU General Public License v2.0 or later:
|
|
;; http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
|
|
;;
|
|
;; For information about writing and modifying Nyquist plug-ins:
|
|
;; https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference
|
|
|
|
|
|
(defun sinc (x fc)
|
|
;; http://www.dspguide.com/ch16/1.htm
|
|
;; Note that fc is a fraction of the sample rate.
|
|
(if (= x 0)
|
|
(* 2 pi fc)
|
|
(/ (sin (* 2 pi fc x)) x)))
|
|
|
|
(defun blackman (i M)
|
|
;; Where: 0 <= i <= M
|
|
(+ 0.42
|
|
(* -0.5 (cos (/ (* 2.0 pi i) M)))
|
|
(* 0.08 (cos (/ (* 4 pi i) M)))))
|
|
|
|
(defun calc-kernel (size fc)
|
|
;; Generate windowed sinc kernel impulse
|
|
(when (oddp size)
|
|
(error "Size of sinc filter must be even"))
|
|
(let ((ar (make-array (1+ size)))
|
|
(norm 0) ;Normalization factor
|
|
val)
|
|
(do ((i 0 (1+ i))
|
|
(j size (1- j))
|
|
(x (- halfk) (1+ x)))
|
|
((> i j))
|
|
(setf val (* (sinc x fc)(blackman i size)))
|
|
(setf norm (+ norm val))
|
|
(setf (aref ar i) val)
|
|
(setf (aref ar j) val))
|
|
;; norm is sum of all samples, but don't count middle value twice.
|
|
(setf norm (- (* norm 2)(aref ar halfk)))
|
|
(dotimes (i size ar)
|
|
(setf (aref ar i)(/ (aref ar i) norm)))))
|
|
|
|
(defun get-kernel (size fc type)
|
|
;; type: 0 (low pass) or 1 (highpass)
|
|
;; Returns filter kernel as a sound.
|
|
(let ((kernel (calc-kernel size fc)))
|
|
(when (= type 1)
|
|
;; Convert kernel to high pass
|
|
;; https://tomroelandts.com/articles/how-to-create-a-simple-high-pass-filter
|
|
(dotimes (i size kernel)
|
|
(setf (aref kernel i)(* -1 (aref kernel i))))
|
|
(incf (aref kernel halfk)))
|
|
(snd-from-array 0 *sound-srate* kernel)))
|
|
|
|
(defun sinc-filter (sig start end impulse)
|
|
(extract-abs start end (convolve sig impulse)))
|
|
|
|
(defmacro validate-low-hz (hz fmin fmax)
|
|
;; Discard if out of valid range.
|
|
;; Do NOT coerce into range if too high - if multiple tracks with
|
|
;; different sample rates, that could cause very unexpected results.
|
|
`(if (or (not ,hz) (< ,hz fmin) (> ,hz fmax))
|
|
(setf ,hz nil)))
|
|
|
|
(defmacro validate-high-hz (hz fmin fmax)
|
|
;; Discard if too high. Coerce into range if too low.
|
|
`(if (or (not ,hz) (>= ,hz fmax))
|
|
(setf ,hz nil)
|
|
(setf ,hz (max ,hz fmin))))
|
|
|
|
(defun dofilter (cf bw type)
|
|
;; type: 0 (low pass) or 1 (highpass)
|
|
;; Calculate kernel length (must be even)
|
|
;; http://www.dspguide.com/ch16/2.htm
|
|
(setf klength (/ 4.0 bw))
|
|
(setf halfk (round (/ klength 2)))
|
|
(setf klength (* 2 halfk))
|
|
(let ((imp (get-kernel klength cf type))
|
|
(start (/ halfk *sound-srate*))
|
|
(dur (get-duration 1)))
|
|
(multichan-expand #'sinc-filter *track* start (+ start dur) imp)))
|
|
|
|
(defun bandwidth (hz)
|
|
;; Set bandwidth ratio of each filter as 1% of filter frequency.
|
|
(* hz 0.01))
|
|
|
|
(defun bw-ratio (hz)
|
|
;; Bandwidth ratio is required as a fraction of the sampling rate
|
|
(/ (bandwidth hz) *sound-srate*))
|
|
|
|
(defun filter ()
|
|
(when (< *sound-srate* 100)
|
|
(throw 'err (_ "Error.~%Track sample rate below 100 Hz is not supported.")))
|
|
(let* ((f0 (get '*selection* 'low-hz))
|
|
(f1 (get '*selection* 'high-hz))
|
|
(fc (get '*selection* 'center-hz))
|
|
; If frequency too low, filter length is too large.
|
|
(fmin (* 0.002 *sound-srate*))
|
|
(fmax (* 0.498 *sound-srate*))
|
|
(tn (truncate len))
|
|
(transition (truncate (* 0.01 *sound-srate*))) ; 10 ms
|
|
(t1 (min transition (/ tn 2))) ; fade in length (samples)
|
|
(t2 (max (- tn transition) (/ tn 2))) ; length before fade out (samples)
|
|
(breakpoints (list t1 1.0 t2 1.0 tn))
|
|
(env (snd-pwl 0.0 *sound-srate* breakpoints)))
|
|
(validate-low-hz f0 fmin fmax)
|
|
(validate-high-hz f1 fmin fmax)
|
|
;; Handle very narrow selections.
|
|
;; This may cause f0 or f1 to 'slightly' exceed fmin fmax.
|
|
(when (and f0 f1 (< (- f1 f0) (* fc 0.02)))
|
|
(setf f0 (* fc 0.99))
|
|
(setf f1 (* fc 1.01)))
|
|
(when f0
|
|
(setf lp-width (bw-ratio f0))
|
|
(setf f0 (/ f0 *sound-srate*)))
|
|
(when f1
|
|
(setf hp-width (bw-ratio f1))
|
|
(setf f1 (/ f1 *sound-srate*)))
|
|
;(format t "Low: ~a High: ~a" (if f0 (* f0 *sound-srate*) nil) (if f1 (* f1 *sound-srate*) nil))
|
|
(if (not (or f0 f1))
|
|
"" ;may occur if multiple tracks with different sample rates
|
|
(sim
|
|
(mult env
|
|
(if f0 (dofilter f0 lp-width 0) 0))
|
|
(mult env
|
|
(if f1 (dofilter f1 hp-width 1) 0))
|
|
(mult (diff 1.0 env) *track*)))))
|
|
|
|
|
|
(catch 'err (filter))
|