Altering a selection's endpoints


I’d like to automate a set of repetitive operations on a selected portion of audio.


  1. manually select a portion of audio

Automation steps: (version 1)

  1. select the section PRECEDING the current selection
  2. copy this portion of audio
  3. paste this portion of audio, replacing the original selection
  4. reverse pasted audio
  5. invert pasted audio

Automation steps: (version 2)

  1. select the portion FOLLOWING the current selection
  2. copy this portion of audio
  3. paste this portion of audio, replacing the original selection
  4. reverse pasted audio
  5. invert pasted audio

    I am trying to write a Nyquist script like this:

(setf WidthInSamples (snd-length (aref TRACK 0) 100000000))

(setf SelectionStart (round (* (get 'SELECTION 'start) SOUND-SRATE)))

(setf PrecedingStart (- SelectionStart WidthInSamples))
(setf FollowingStart (+ SelectionStart WidthInSamples))

(setf PrecedingEnd (+ PrecedingStart WidthInSamples))
(setf FollowingEnd (+ FollowingStart WidthInSamples))

(aud-do “SelSave:”)

(aud-do (format nil “SetLeftSelection: Text=~s” PrecedingStart))
(aud-do (format nil “SetRightSelection: Text=~s” PrecedingEnd))

(aud-do “Copy”)

(aud-do “SelRestore:”)
(aud-do “Paste:”)
(aud-do “Reverse:”)
(aud-do “Invert:”)

However, I’m seeing a few problems:

  1. The SetLeftSelection command produces a popup window. Is it possible to run this as silent with a parameter?
  2. The Paste command appears to work. But when the script completes (as revealed in Debug mode) the operation reverts, leaving the audio track unaltered.

To overcome these, I believe I can:

  1. possibly create a clip of my original selection and use their boundaries? But I think I’ll still be limited in my ability to alter the selection endpoints.
  2. use Steve’s PunchCopyPaste, though it is overkill for my use case

Does this track?



Maybe this will help to get you started:

;type tool
(let ((start (get '*selection* 'start))
      (end (get '*selection* 'end))
      (duration 0))
  (setf duration (- end start))
  (aud-do-command "SelectTime" :start (- start duration)
                               :end start)
  (aud-do "Copy:")
  (aud-do-command "SelectTime" :start start :end end)
  (aud-do "Paste:")
  (aud-do "Reverse:")
  (aud-do "Invert:")

Whew! That worked perfectly. I had hoped it was a simple effort.

In all my searching, I never saw aud-do-command

Is there a reference/API for this online? A quick search yielded but this looks like a plugin perhaps…?

Follow-up question:

I’ve implemented both versions of the script. I notice that both versions will copy and paste a portion of audio different lengths than the original.

For example,
Original = 52,474 samples
Preceding = 52,320 samples
Following = 52,800 samples

Would this be a rounding error associated with working in seconds instead of working in samples? Can the code be forced to operate in samples to increase precision?

This is a good starting point: Missing features - Audacity Support

That’s my blog :slight_smile:
Mostly it’s about using Nyquist in Audacity, but there’s not (yet) much about using Audacity’s macro scripting commands with Nyquist on that site - I may add some stuff as and when I have time.

It could be. Could you provide exact steps for a test case that I can try?

Unfortunately no. “AUD-DO” (and “AUD-DO-COMMAND”) calls Audacity’s “macro scripting” commands (Scripting Reference - Audacity Manual) and they always use seconds for time, with precision limited to 5 decimal places.
Nyquist itself is sample accurate, but cannot access audio outside of the selection at the time that the effect is launched.

Thanks Steve.

I wonder if 5 decimal places is the contributing factor…

My original selection is 52,474 samples @ 48000 samples/sec = 1.093208333 seconds

Ideally the script should produce start/end times such that the duration is 1.093208333 seconds

However, the selection is as follows:

Preceding Selection: 52,320 samples, 1.09 seconds
Following Selection: 52,800 samples, 1.10 seconds

Does this suggest a rounding after 2 decimal places?

Here’s the code:

(let ((Seconds_Start      0)
      (Seconds_End        0)
      (Seconds_Duration   0)
      (Samples_Start      0)
      (Samples_End        0)
      (Samples_Duration   0)
      (Preceding_Start    0)
      (Preceding_End      0)
      (Preceding_Duration 0)
      (Following_Start    0)
      (Following_End      0)
      (Following_Duration 0))
  (setf Seconds_Start (get '*SELECTION* 'start))
  (setf Seconds_End   (get '*SELECTION* 'end  ))
  (setf Samples_Start (* Seconds_Start *SOUND-SRATE*))
  (setf Samples_End   (* Seconds_End   *SOUND-SRATE*))
  (setf Seconds_Duration (- Seconds_End Seconds_Start))
  (setf Samples_Duration (- Samples_End Samples_Start))

  (setf Preceding_Start    (-   Seconds_Start   Seconds_Duration))
  (setf Preceding_End           Seconds_Start                    )
  (setf Preceding_Duration (- Preceding_End   Preceding_Start   ))

  (setf Following_Start         Seconds_End                    )
  (setf Following_End      (+   Seconds_End   Seconds_Duration))
  (setf Following_Duration (- Following_End Following_Start   ))

  (print   Seconds_Start   )
  (print   Seconds_End     )
  (print   Seconds_Duration)
  (print    *SOUND-SRATE*  )
  (print   Samples_Start   )
  (print   Samples_End     )
  (print   Samples_Duration)
  (print Preceding_Start   )
  (print Preceding_End     )
  (print Preceding_Duration)
  (print Following_Start   )
  (print Following_End     )
  (print Following_Duration)

;  (aud-do-command "SelectTime" :start Preceding_Start :end Preceding_End)
  (aud-do-command "SelectTime" :start Following_Start :end Following_End)


Testing the script that I posted with a 48kHz sample rate track, and a selection that is 52,474 samples long, the result is sample accurate.

You mean that before applying the effect there is only 1.09 seconds of audio before the selection? In that case it will be impossible to copy more that 1.09 seconds of audio from before the selection :confused:

Thanks Steve.

Hmm. Unfortunately your code as posted produces a different effect on my end. Ahh… the joys of isolating all the variables. :smiley:

Both codes (yours and mine, which should be equiv to yours) produce a selection that is not sample accurate (smaller on the preceding side and larger on the following side). Could there be something in my configuration at work here? I am using the latest Audactiy v3.2.2, 64 bit.

New data:

The original WAV data was extracted from an MP4 file. This WAV data produces the behavior which is not sample accurate, as noted previously.

I extracted a subset of this data (for testing) and saved as 32-bit float WAV (current format selection) using Audacity. When I load this data into another instance of Audacity, I can produce sample accurate selections using our code. I suspect that my WAV data (when originally extracted by Audacity) was not saved in 32-bit float WAV format, which feeds my hypothesis of rounding error. Does this track? Is there a way I can confirm my suspicion that my WAV data is not 32-bit float on disk?

Thinking about the rounding error, I exported a section of my test data to work with a smaller size. After saving it in a 32-bit float WAV format, I see our code works as nearly sample accurate. (+/- 1, which is good enough for this use case)

So, I went back to my original MP4 and performed an export as 32-bit float WAV format. The scripts work as expected. I’m now able to edit my data in a fraction of the time!

Thanks Steve for the guidance. It’s always difficult to find the root cause, eh?