Advanced Punch Copy/Paste: Is this possible?

Apologies in advance if I’m posting in the wrong spot, but I think I’ve ruled out Nyquist alone for this plugin. What I would love to have is a version of Steve Daulton’s Punch Copy / Punch Paste script that is able to store different copied audio for each track of a project. I have audio recorded in different locations, and I use PCP to preserve room sound when editing, but, as things are, I can only do that to one track at a time–and these are 2-hour recordings.

My understanding is that Nyquist cannot tell which track audio passed to it is from, but is something like this possible by other means?

Background: It is possible to apply macro / scripting commands from Nyquist using “AUD-DO” / “AUD-DO-COMMAND” functions. I am NOT referring to this in the rest of this post, but referring specifically to acting on audio directly with Nyquist.

Nyquist can only access audio from one track at a time. If you apply a Nyquist effect to a selection that contains multiple tracks, Audacity cycles through the tracks (top to bottom), passing one track at a time to Nyquist.

Nyquist in Audacity has a number of “property lists” that allow Nyquist to access information about the Audacity project’s state at the time that the Nyquist effect is launched (Documentation here: Nyquist Plug-ins Reference - Audacity Wiki). Some relevant properties are accessed like this:

(get `*project* `tracks)  ; Returns the number of tracks in the project.
(get `*project* `wavetracks)  ; Returns the number of audio tracks in the project.
(get `*selection* `tracks)  ; Returns a list of track numbers of selected audio tracks.
(get `*track* `channels)  ; Returns the number of channels in the track (mono = 1, stereo = 2).
(get `*track* `index)  ; A counter that increments for each track processed. On processing the first track, the value is "1".

So you can get the track number of the current track that is being processed like this:

(setf selected (get `*selection* `tracks))  ; get the list of track numbers
(setf current-index (get `*track* `index))  ; index count starts from 1.
(print (nth (1- current-index) selected))

Oh, interesting! That sounds promising!

Note that copying audio to *SCRATCH* is quite tricky, because Audacity will normally try to delete the audio from *TRACK* when the Nyquist script returns, and *SCRATCH* is pointing to *TRACK*, so it’s easy to end up with *SCRATCH* pointing to a “null sound”, rather than to the audio data that we have tried to save.

To retain the audio beyond the termination of the “copy” script, we need to ensure that there is still a reference to the audio when the script terminates. I don’t recall exactly how I did that with the “copy” script (I don’t have the plug-in handy right now), but I guess I did something like returning the length of the audio. By returning a reference to the selected audio, Audacity does not immediately clear the audio from RAM, so it remains available on *SCRATCH* for pasting later.

Oh, snap, I didn’t realize you were that Steve. Thank you for your work. Since it’s GNU licenced, is it alright if I start from the original when trying to make this version?

Sure, no problem. I’d just ask that continue the tradition of sharing the finished plug-in with the Audacity community.

1 Like

I’ve never used Lisp before, but if I manage to cobble something together, I’ll share it, for sure. From looking at the code, it seems you did output the length of the copied audio to keep it in *scratch*. If I’m understanding things correctly, the copy function would just need to either create unique property names referencing the *track* when saving to *scratch*, e.g. (putprop '*scratch* s trackN-left) rather than (putprop '*scratch* s left), or, if dynamic naming isn’t possible, maybe make it an array?

I’m not sure exactly what you are asking, but here’s a bit of background that may help.

In Nyquist, variable names are “symbols”. A symbol has a unique name (such as the symbol *scratch*).
Each symbol is a kind of “namespace” that supports a list of “properties” (a “property list”).
Each property on a symbol has a unique name.

Because the *scratch* symbol is the only user defined symbol that survives beyond the run time of the script, a certain amount of care is required to avoid possible property name clashes with other plug-ins.

A symbol’s properties can each have a value. Any type of value is OK, but remember that assigning “sounds” can be problematic due to Audacity cleaning up after the script runs.

So yes, you could have a unique property name for each saved clip, or you could use just one property name with a list of clips. Whichever approach you take, you will need to think about how you are going to retreive the correct clip from *scratch*when it comes to pasting.

Here’s what I’m thinking:
(let ((lname (concatenate "PUNCH-L-" (getf *TRACK* name)))
(rname (concatenate "PUNCH-R-" (getf *TRACK* name)))))
This will create unique symbols that include the Track names the sounds were pulled from. (e.g. PUNCH-L-TRACK1) (I will need to include code to handle illegal characters)

Then PunchPaste can find them by recreating those symbols using the same code above. Obviously, this will fail if the user renames their tracks, but I don’t know that there’s a way around that, given the track info Audacity provides.