Following Steve’s invaluable help in https://forum.audacityteam.org/t/macro-how-to-make-a-selection-relative-to-labels/51042/1 I created a plugin which I remember working just fine over the course of last year. After a long pause (most probably including an update of Audacity to 2.3.3), I tried using it again and it keeps running into the error condition of “Cursor must be before or between two labels” (last line). No need to say that of course I do have my cursor position surrounded by 2 (or more, for a test) labels.
- Have there been any changes in Nyquist command/syntax that may have rendered my script unusable in the meantime?
- If not, could someone please help me with hints to debugging the code. I would like to print the list of labels and the cursor position (of which it says it is not between the labels), but so far I failed at finding/applying the correct syntax for adding debug-print statements or any other way to show me some debug output.
(BTW the comments marked with “Qu” I just took over from Steve, but don’t really understand. Any insights in understanding these lines would also be appreciated)
Here is the code:
;nyquist plug-in
;version 4
;type tool
;name "fade 'n silence"
;author "Steve Daulton & Samse26"
;release 2.3.1
;copyright "Released under terms of the GNU General Public License version 2"
; This script helps you cutting a continuous recording of multiple tracks (eg. vinyl or tape) into individual tracks.
; Prerequisite:
; ============
; - label1 set at the detected end of the signal of the previous track, i.e. at the point where the music completely
; gets submerged by the turntable rumble/noise or tape noise (end of musical fade-out)
; - label2 set at the beginning of the signal of the next track, i.e. just before the first note or at first audible
; signs of a musical fade-in.
; - The cursor needs to sit
; anywhere between labels 1 and 2
; or
; anywhere left to label1, as long as there is no (third) label left to the cursor
; caveat: in this case the effect will happen between labels 1 and 2.
; Else the effect will happen between the labels to the left and right of the cursor.
; Upon execution the script does the following:
; =============================================
; - It prompts you to specify the duration of the to-be
; fade-out of previous track (min 0.01s, max 2s, default: 0.2s)
; fade-in of next track (min 0.01s, max 2s, default: 0.2s)
; silence between the fades (1, 2, 3, or 4 s, default: 2s)
; - fades-out the previous track so that fade-out is done (at 0%) at label1
; this indeed may affect some audible sigal, so label1 should only sit where the signal is already weak anyway.
; - fades-in the next track, so that fade-in is done (at 100%) at label2
; - replaces background noise between end and beginining of fade areas with silence of the desired duration
; (i.o.w. it cuts out the audio of any lenth and inserts silence of specified length)
; - deletes labels 1 and 2
; - adds a new label at former positon of label1 (= begin of silence) with cursor in text field,
; so you can type the name of the next track right in.
; Once you're done processing all tracks of the recording you can export the individual tracks with File > Export > Export Multiple
; according to the label list.
;
; The script will abort with errors if :
; ======================================
; - initially the cursor is placed to the right of label 2 and there is no further label to the right
; - the specified fade-in time is longer than the distance between the labels (which means that fade-in would overlap with the fade-out range)
; Define control to prompt user for fade and silence times
;;name "Set fade-out/-in times and duration of silence" ; a second ";name" line would override the script name (?!)
;control fto "Fade-out time" float "seconds" 0.2 0.01 2
;control fti "Fade-in time" float "seconds" 0.2 0.01 2
;control sit "Silence" int "seconds" 2 1 4
;; define function "select", with 2 args, based on Audacity "Select:" macro
;; this one works with the default value for "RelativeTo=" -- whatever this is ...
(defun select (start end)
(aud-do (format nil "Select:Start=~a End=~a Mode=Set" start end)))
;; define another select function "select-start", working relative to SelectionStart
;; I need this because of the unknown default for "RelativeTo=" in "select" above.
;; Here is definitely need the selection relative to the selection start, and nothing else.
(defun select-start (start end)
(aud-do (format nil "Select:Start=~a End=~a Mode=Set RelativeTo=SelectionStart" start end)))
;; define function "get-region" with one arg
;; Qu: suspecting this creates a list with cursor positions
;; Qu: where does the value for "labels" come? Or is this a return value?
;; Qu: is "now" a predefined variable?
(defun get-region (labels)
(do* ((j 1 (1+ j))
(next (first (nth j labels))
(first (nth j labels))))
((or (>= j (length labels))
(>= next now))
(list (first (nth (1- j) labels)) next))))
;; get list of (manually set) labels:
;; 1st label sits at end of to-be fade-out (i.e. signal just died)
;; 2nd label sits at end of to-be fade-in (i.e. just before signal starts)
(setf labels (second (aud-get-info "Labels")))
(setf now (get '*selection* 'start)) ; Qu: get current cursor position (?)
(defconstant ld-min fti) ; define minimal distance between lables in sec
(cond
((>= (length labels) 2) ; if there is 2 or more labels
(setf region (get-region labels))
(cond
((not (second region)) ; ERROR: if there is no region (= label) to the right of the cursor
"Cursor is not between labels")
((> (- (second region)(first region)) ld-min) ; if length of region is more than ld-min sec
(select (- (first region) fto) (first region)) ; Select range of (1st label-ft) up to 1st label
(aud-do "FadeOut:") ; Fade-out with duration of ft
(select (- (second region) fti) (second region)) ; Select range of (2nd label-ft) up to 2nd label
(aud-do "FadeIn:") ; Fade-in with duration of ft
; Select region from end of fade-out up to beginning of fade-in (= pos X)
(select (first region) (- (second region) fti))
(aud-do "Cut:") ; cut this range
(select-start 0 sit) ; select sit seconds (desired duration of silence) from current position
(aud-do "Copy:") ; copy audio in range (could be anything)
(select-start 0 0) ; set cursor back to left border of selection (= pos X)
(aud-do "Paste:") ; insert copied audio
(aud-do "Silence:") ; mute inserted audio
(aud-do "MoveToNextLabel:") ; jump ahead to next label to the right
(aud-do "Delete:") ; delete this label (we don't need this any more once the cursor is there)
(select-start (- 0 sit fti) 0) ; select a range backwards from here (= negative) with a duration of sit+fti
; this makes sure that the silence ends just at the beginning of the fade-in,
; no matter what values for silence and fade-in durations are selected
(aud-do "CursSelStart:") ; set cursor to the left border of selection
(aud-do "AddLabel:") ; add a label here,
; do this as the last thing so we can type the title of the next track right in
;; now we have:
;; - deleted (left) label 1 while cutting the range from end of fade-out to beginning of fade-in
;; - deleted (right) label 2 explicitly
;; - inserted a new label at the original position of label 1, i.e. after fade-out = beginnnig of silence
;; This is the beginning of the next track (starting with sit seconds of silence) when later
;; exporting audio, split at labels.
)
; ERROR: if distance between labels is too small to accommodate the fade-in range (the fade-out range is outside!)
(t "Region between labels is less than the fade-in time required before 2nd label")
; Qu: how could I use the value of a variable (ld-min) in a string output?
)
)
(t "Cursor must be before or between two labels")
)