audacia/plug-ins/spectral-delete.ny

138 lines
4.7 KiB
Common Lisp

$nyquist plug-in
$version 4
$type process spectral
$name (_ "Spectral Delete")
$manpage "Spectral_Delete"
$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
;;
;; 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))