1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
|
$nyquist plugin
$version 4
$type process
$mergeclips 1
$restoresplits 0
$name (_ "Crossfade Clips")
$author (_ "Steve Daulton")
$release 3.0.4-1
$copyright (_ "GNU General Public License v2.0 or later")
;; License: GPL v2+
;; 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
;; Instructions:
;; Place two audio clips into the same track.
;; Select (approximately) the same amount of audio from the
;; end of one clip and the start of the other.
;; Apply the effect.
;; The selected regions will be crossfaded.
;;
;; Note, the audio clips do not need to be touching. Any
;; white-space between the clips is ignored.
;;
;; If the selected region is continuous audio (no splits),
;; the the first and last halves of the selected audio
;; will be crossfaded.
;;
;; Advanced Tip:
;; A discontinuity in a waveform may be smoothed by applying
;; a short crossfade across the glitch.
;; Limitations (should not occur in normal usage).
;; 1) There may be no more than two clips selected in each channel.
;; 2) The selection may not start or end in white-space.
(setf err1 (format nil (_ "Error.~%Invalid selection.~%More than 2 audio clips selected.")))
(setf err2 (format nil (_ "Error.~%Invalid selection.~%Empty space at start/ end of the selection.")))
(defun find-ends (T0 T1 clips)
"Look for a split or gap within the selection, or return the mid-point"
(let ((trk-ends ()) ;starts of clips
(trk-starts ())) ;ends of clips
(dolist (clip clips)
;; look for clip enclosing the selection.
(when (and (>= (second clip) T1) (<= (first clip) T0))
(psetq trk-ends (list (/ (+ T0 T1) 2))
trk-starts (list (/ (+ T0 T1) 2)))
(return))
;; look for track starts.
(when (and (> (first clip) T0) (< (first clip) T1))
(push (first clip) trk-starts))
;; look for track ends.
(when (and (> (second clip) T0) (< (second clip) T1))
(push (second clip) trk-ends))
; stop looking when we have passed end of selection.
(when (> (first clip) T1) (return)))
;; if exactly one split position for crossfading,
;; return clip positions, else error.
(cond
((and (= (length trk-ends) 1)
(= (length trk-starts) 1)
(<= (car trk-ends) (car trk-starts)))
(list (car trk-ends)(car trk-starts)))
((or (> (length trk-ends) 1)
(> (length trk-starts) 1))
(throw 'error err1))
(T (throw 'error err2)))))
(defun crossfade (sig out-end in-start end)
"Do the crossfade"
(abs-env
(control-srate-abs *sound-srate*
(let* ((fade-out (mult sig (env out-end 0)))
(cflen (max out-end (- end in-start))) ;crossfade length
(finstart (max (- out-end (- end in-start)) 0))
(fade-in (mult (extract (- end cflen) end sig)
(env (- cflen finstart) 1 finstart))))
(sim fade-out fade-in)))))
(defun env (dur direction &optional (offset 0))
"Generate envelope for crossfade"
(abs-env
(if (< dur 0.01) ;make it linear
(control-srate-abs *sound-srate*
(if (= direction 0)
(pwlv 1 dur 0) ;fade out
(pwlv 0 offset 0 (+ offset dur) 1))) ;fade in
(if (= direction 0) ;cosine curve
(cos-curve dur 0)
(seq (s-rest offset)
(cos-curve dur 1))))))
(defun cos-curve (dur direction)
"Generate cosine curve"
(if (= direction 0) ;fade out
(osc (hz-to-step (/ 0.25 dur)) dur *sine-table* 90)
(osc (hz-to-step (/ 0.25 dur)) dur *sine-table* 0)))
(defun process (sig t0 t1 clips)
"Find the split positions and crossfade"
(setf fadeclips
(multichan-expand #'find-ends t0 t1 clips))
(if (arrayp fadeclips)
(prog ((fade-out-end (min (first (aref fadeclips 0))
(first (aref fadeclips 1))))
(fade-in-start (max (second (aref fadeclips 0))
(second (aref fadeclips 1)))))
(return
(multichan-expand #'crossfade sig
(- fade-out-end t0)
(- fade-in-start t0)
(- t1 t0))))
(crossfade sig
(- (first fadeclips) t0)
(- (second fadeclips) t0)
(- t1 t0))))
;;; Run the program.
(if (= (length (get '*selection* 'tracks)) 1)
(catch 'error
(process *track*
(get '*selection* 'start)
(get '*selection* 'end)
(get '*track* 'clips)))
(format nil (_ "Error.~%Crossfade Clips may only be applied to one track.")))
|