Macro: How to make a selection relative to labels

I want to automate cutting transitions between tracks in a continuous recording.
I start by manually setting two labels
(1) at end of previous track and (2) at start of next track

I’d like to automate the subsequent steps
3) fade out 0,2sec prior to label (1)
4) fade in 0,2sec prio to label (2)

option 1:
5a) replace range between (1) and [(2) - 0,2sec] with silence
6a) trim silence to 2sec (usually the range is more than 2sec)
option 2:
5b) delete range between (1) and [(2) - 0,2sec]
6b) insert silence of 2sec at cursor (= original position of (1))

Provided I start by setting the cursor between labes (1) and (2) the following macro nicely completes steps 3 and 4:

MoveToPrevLabel:
Select:End=“0” Mode=“Set” RelativeTo=“Selection” Start=“-0,2”
FadeOut:Use_Preset=“”
MoveToNextLabel:
MoveToNextLabel:
Select:End=“0” Mode=“Set” RelativeTo=“Selection” Start=“-0,2”
SelSave:
FadeIn:Use_Preset=“”

But then I am stuck:
I would also manage to set a third label at the beginning of the fade-in [(2) - 0,2sec],
I can even jump to a label and then save the cursor position
but then
How do I define a selection based on label positions (or any “remembered” position)?
What I find is that I can define a selection based only on absolute positions (even if relative to a current selection).

So far I spent hours testing all sorts of macros, but to no avail.
Any insights or experiences with this, anyone?

That’s a bit too complex for a normal Macro. You need a “Nyquist Macro”. See: https://alphamanual.audacityteam.org/man/Nyquist_Macros
Even then it’s a bit tricky, and we don’t have much documentation yet for Nyquist Macros, so I’ve written one for you.
If I understand correctly what you want, I think this will do it:

;nyquist plug-in
;version 4
;type tool
;name "Fade between labels"
;author "Steve Daulton"
;release 2.3.1
;copyright "Released under terms of the GNU General Public License version 2"

(defun select (start end)
  (aud-do (format nil "Select:Start=~a End=~a Mode=Set" start end)))


(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))))


(setf labels (second (first (aud-get-info "Labels"))))
(setf now (get '*selection* 'start))

(cond
 ((>= (length labels) 2)
    (setf region (get-region labels))
    (cond
     ((not (second region))
        "Cursor is not between labels")
     ((> (- (second region)(first region)) 4)
        (select (first region) (+ 2 (first region)))
        (aud-do "FadeOut:")
        (select (+ 2 (first region)) (- (second region) 2))
        (aud-do "Silence:")
        (select (- (second region) 2) (second region))
        (aud-do "FadeIn:"))
     (t "Region between labels is less than 4 seconds")))
 (t "Cursor must be before or between two labels"))

wow - this is what I call a prompt resonse (on ym very first post)!
I guess I need some time to digest this.
At least I know now that I didn’t miss anything obvious (for the initiates).
I still wonder a bit how good automation with macros can be, if it only allows setting selection boundaries by absolute values, and not by computed ones.
Applying a macro multiple times would usually mean a different context, wouldn’t it?

Anyway, thanks so much for your help, Steve!

Macros are deliberately kept as simple as possible. The number of effects and options is quite daunting for many users without inventing a new scripting language for writing macros. A simple sequence of commands is perfectly sufficient for many “batch” and “automation” requirements.

Audacity already contained a powerful scripting language (“Nyquist”), so it makes sense to use that for more complex operations.
Unfortunately the jump from “simple macros” and “Nyquist macros” is quite a big one, but we are working on ways to make that less of a jump.

To use the “Nyquist Macro” from my previous post, you just need to save the text to a file and name i t with “.ny” as the file extension, then “install” it as a Nyquist plug-in (instructions here: https://manual.audacityteam.org/man/installing_effect_generator_and_analyzer_plug_ins_on_windows.html#nyquist_install)
When installed, the plug-in will be available in the “Tools” menu, and available in the Macro editor.

Points taken.
I’ll check out the Nyquist macro tomorrow. Thanks for the references.

Hi Steve,
unfortunately I don’t get the Nyquist macro to work in my environment (BTW Win 8.1 64-bit, German, with Audacity 2.3.0).
After I spotted the first error messages in the Nyquist box, followed by some read on the NY syntax I applied some small modifications (quoted some header strings and changed initial cap in “Labels” on line 22) - but to no avail.

Here is the error message:

nxy_Fehler zurückgegeben von Fade between labels.
error: unbound variable - LABELS
if continued: try evaluating symbol again
Function: #<FSubr-SETF: #1be4f4f8>
Arguments:
  LABELS
  (SECOND (FIRST (AUD-GET-INFO LABELS)))
1> NIL
1> error: unbound variable - LABELS
if continued: try evaluating symbol again
Function: #<FSubr-COND: #1be52628>
Arguments:
  ((= (LENGTH LABELS) 2) (SETF REGION (GET-REGION LABELS)) (COND ((NOT (SECOND REGION)) CURSOR IS NOT BETWEEN LABELS) (((- (SECOND REGION) (FIRST REGION)) 4) (SELECT (FIRST REGION) (+ 2 (FIRST REGION))) (AUD-DO FADEOUT) (SELECT (+ 2 (FIRST REGION)) (- (SECOND REGION) 2)) (AUD-DO SILENCE) (SELECT (- (SECOND REGION) 2) (SECOND REGION)) (AUD-DO FADEIN)) (T REGION BETWEEN LABELS IS LESS THAN 4 SECONDS)))
  (T CURSOR MUST BE BEFORE OR BETWEEN TWO LABELS)
Function: #<FSubr-SETF: #1be4f4f8>
Arguments:
  LABELS
  (SECOND (FIRST (AUD-GET-INFO LABELS)))
2> 1>

and this is the modified code:

;nyquist plug-in
;version 4
;type tool
;name "Fade between labels"
;author "Steve Daulton"
;release "2.3.1"
;copyright "Released under terms of the GNU General Public License version 2"

(defun select (start end)
  (aud-do (format nil SelectStart=~a End=~a Mode=Set start end)))


(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))))


(setf labels (second (first (aud-get-info labels))))
(setf now (get 'selection 'start))

(cond
 ((= (length labels) 2)
    (setf region (get-region labels))
    (cond
     ((not (second region))
        Cursor is not between labels)
     (( (- (second region)(first region)) 4)
        (select (first region) (+ 2 (first region)))
        (aud-do FadeOut)
        (select (+ 2 (first region)) (- (second region) 2))
        (aud-do Silence)
        (select (- (second region) 2) (second region))
        (aud-do FadeIn))
     (t Region between labels is less than 4 seconds)))
 (t Cursor must be before or between two labels))

Attached is my very simple test project, but I suspect it doesn’t matter which one to run the plug-in on.

I’m willing to dive some more into Nyquist programming, but if there is anything obvious for you that may help, I’d appreciate your advice.
Thanks in advance.
Makrotest-comp.zip (739 KB)

Which version of Audacity are you using?
I’m assuming 2.3.0, but I’m using 2.3.1 alpha, so I may have included a feature that is not in 2.3.0 (“scripting” is under heavy development).

I see from one of your other forum topics that you have Audacity 2.3.0.

I’d forgotten, but in Audacity 2.3.0 there was a problem with the formatting of the data from the command (aud-get-info “Labels”). This problem has been resolved in Audacity 2.3.1.

The code in my previous post is correct for Audacity 2.3.1, but requires a slight modification to work in Audacity 2.3.0.
For 2.3.0, change the line:

(setf labels (second (first (aud-get-info "Labels"))))

to:

(setf labels (second (aud-get-info "Labels")))

ouch - just on to the next error message:

error: unbound variable - J
if continued: try evaluating symbol again
Function: #<FSubr-DO: #53e21a0>
Arguments:
  ((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))
Function: #<Closure-GET-REGION: #5356f30>
Arguments:
  ((0.6 0.6 "1") (5.1 5.1 "2"))
Function: #<FSubr-SETF: #53dd650>
Arguments:
  REGION
  (GET-REGION LABELS)
Function: #<FSubr-COND: #53e0780>
Arguments:
  ((= (LENGTH LABELS) 2) (SETF REGION (GET-REGION LABELS)) (COND ((NOT (SECOND REGION)) CURSOR IS NOT BETWEEN LABELS) (((- (SECOND REGION) (FIRST REGION)) 4) (SELECT (FIRST REGION) (+ 2 (FIRST REGION))) (AUD-DO FADEOUT) (SELECT (+ 2 (FIRST REGION)) (- (SECOND REGION) 2)) (AUD-DO SILENCE) (SELECT (- (SECOND REGION) 2) (SECOND REGION)) (AUD-DO FADEIN)) (T REGION BETWEEN LABELS IS LESS THAN 4 SECONDS)))
  (T CURSOR MUST BE BEFORE OR BETWEEN TWO LABELS)
1>

here is the code:

;nyquist plug-in
;version 4
;type tool
;name "Fade between labels"
;author "Steve Daulton"
;release "2.3.1"
;copyright "Released under terms of the GNU General Public License version 2"

(defun select (start end)
  (aud-do (format nil SelectStart=~a End=~a Mode=Set start end)))


(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))))


(setf labels (second (aud-get-info "Labels")))
(setf now (get 'selection 'start))

(cond
 ((= (length labels) 2)
    (setf region (get-region labels))
    (cond
     ((not (second region))
        Cursor is not between labels)
     (( (- (second region)(first region)) 4)
        (select (first region) (+ 2 (first region)))
        (aud-do FadeOut)
        (select (+ 2 (first region)) (- (second region) 2))
        (aud-do Silence)
        (select (- (second region) 2) (second region))
        (aud-do FadeIn))
     (t Region between labels is less than 4 seconds)))
 (t Cursor must be before or between two labels))

Let me know if you’d rather have me to switch to Audacity 2.3.1 - I usually prefer to stay away from betas if there is no need.

You have several incorrect changes in there that are not in my original code.

This is the full code for Audacity 2.3.0:

;nyquist plug-in
;version 4
;type tool
;name "Fade between labels"
;author "Steve Daulton"
;release 2.3.1
;copyright "Released under terms of the GNU General Public License version 2"

(defun select (start end)
  (aud-do (format nil "Select:Start=~a End=~a Mode=Set" start end)))


(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))))


(setf labels (second (aud-get-info "Labels")))
(setf now (get '*selection* 'start))

(cond
 ((>= (length labels) 2)
    (setf region (get-region labels))
    (cond
     ((not (second region))
        "Cursor is not between labels")
     ((> (- (second region)(first region)) 4)
        (select (first region) (+ 2 (first region)))
        (aud-do "FadeOut:")
        (select (+ 2 (first region)) (- (second region) 2))
        (aud-do "Silence:")
        (select (- (second region) 2) (second region))
        (aud-do "FadeIn:"))
     (t "Region between labels is less than 4 seconds")))
 (t "Cursor must be before or between two labels"))

oops - this is odd. I am pretty sure I just copied the original code from you, but you’re right, it is severly mutilated.
Ok then - I tried your new code and it runs without errors!
Now I’m ready to play with it to understand the syntax and to tweak the parameters to observe the effects.

Thanks a lot Steve for this great starting point!

oops - I am sure that I just coped/pasted your original code, but somewhere along the way it must have been severely mutilated.
Good news: Your latets code does run without errors!
I’m now ready to play with the syntax to learn and tweak parameters to get more familiar with this Nyquist thingy.

Thanks a lot Steve for a great starting point for me!

There’s a recently fixed bug that you may run into. This bug affect Audacity 2.3.0 (it is fixed for 2.3.1 which is scheduled for release in January).

If you generate audio at a point in an audio track with a Nyquist effect (for example, generating a “Pluck”), then 30 seconds of audio are deleted from the audio track.

Steps to reproduce:

  1. Generate a 30 second Chirp
  2. Click at around 15 seconds to place the cursor
  3. Generate a “Pluck” (default settings) → Audio after the Pluck disappears.

This is relevant to your other post, in that a solution to insert 2 seconds of silence using a macro “should be” (but this does not work in 2.3.0 because of this bug), to create a small Nyquist plug-in to generate 2 seconds of silence.
The plug-in could be something like:

;nyquist plug-in
;version 4
;type generate
;name "Generate Silence..."

;control duration "Duration" time "" 2 0 nil

(snd-const 0 0 *sound-srate* duration)

or for a fixed duration, it could be even simpler:

;nyquist plug-in
;version 4
;type generate
;name "Generate 2s Silence"

(snd-const 0 0 *sound-srate* 2.0)

you keep me hanging on … :slight_smile:
Thanks for the info about the bug.
While we’re at talking about more Nyquist macros, one inevitable question popped up in my head:

  1. Is it possible to reference one ny-macro from another (instead of including the full code)?
    and/or
  2. Is it possible to execute a sequence of ny-macros (like the built-ins), so they can be designed in a modular way?

Nyquist has two “modes” of operation:

1) Traditional Nyquist:
As in all of the shipped Nyquist plug-ins (and all of these plug-ins: https://wiki.audacityteam.org/wiki/Download_Nyquist_Plug-ins).
These effects are one of three “types”: “process” (Effects), “generate” and “analyze”. Usually they will appear in either the Effect, Generate or Analyze menus, though it is also possible to put any type of Nyquist effect in the “Tools” menu.

The Nyquist program runs, produces a result, and returns that result to Audacity.

The Nyquist program runs once for each selected track, and returns a result each time. So, for example, a Nyquist filter will be applied to the audio from each selected track in turn, and return the filtered audio, which Audacity will then use to replace the selected audio.

2) Nyquist Macros:
This is the new mode that allows Nyquist to run “Scripting” commands. Nyquist Macros will usually appear in the “Tools” menu, though we don’t yet ship any Nyquist Macros. This is the page in the “alpha manual”: https://alphamanual.audacityteam.org/man/Nyquist_Macros


Important Limitation:
Nyquist effects must be either “traditional” Nyquist effects, or “Nyquist Macros”. It is not currently possible for a Nyquist program to do both. The simplest way I’ve found of remembering this is that either Nyquist or a “scripting command” may modify the project, but not both in the same plug-in. (Technically, this limitation is because Nyquist is not “reentrant”.)

So it is NOT possible for a “traditional” Nyquist effect to call a Nyquist Macro, or for a Nyquist Macro to call a “traditional” Nyquist effect, but you CAN call any type of Nyquist effect from a “normal” Macro.

If you want to run a sequence of Nyquist effects and/or macros, then the easy way to do that is to create a normal Macro, and include the Nyquist effects in the list of commands.


It is also possible to import Nyquist functions from external files, in fact, much of Nyquist works this way. In the Audacity installation, there is a “nyquist” folder that contains a load of “.lsp” files. These files have the definitions of Nyquist’s high level functions, and are loaded automatically on running a Nyquist effect the first time in the Audacity session.

Audacity 2.3.1 also includes around 50 new high level functions (not yet documented), which are NOT loaded automatically, but may be loaded when required by running the special command (aud-import-commands).

I want to add a process bar for the code above.
how to add a process bar (green color) ?

Capture.PNG

How long does the script take to run?

It depends on the track length, the script takes around 30 seconds.

Give the code to add the green process bar,
honestly, I don’t know how to do it.

I’ve just tested the script on an hour long track and it was virtually instant :confused:

There is no way to force a Nyquist plug-in to create a progress bar. If the Nyquist runs for more than a few seconds, then a progress bar will normally appear automatically. There are some cases when Audacity does not know how long the plug-in will take. In those cases a progress bar may not appear, or may be inaccurate.