File: crossfadeclips.ny

package info (click to toggle)
audacity 3.7.3%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 125,252 kB
  • sloc: cpp: 358,238; ansic: 75,458; lisp: 7,761; sh: 3,410; python: 1,503; xml: 1,385; perl: 854; makefile: 122
file content (132 lines) | stat: -rw-r--r-- 4,825 bytes parent folder | download | duplicates (2)
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.")))