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