✓ Converting point labels to regions

Hi!

There doesn’t seem to be a feature to join labels together in Audacity. For instance if I have two point labels, L1 and L2, I want only L1, but I want it to be a region starting at L1 and ending at L2.
Then I found that if I export my labels, they will end up as a text file that really is a CSV with TAB separators, so I wrote this very simple Bash script to convert point labels to regions:

#!/bin/bash

# Constants ————————————————————————————————————————————————————————————————————
readonly UserError=3
readonly ScriptName=${0##*/}

# Usage ————————————————————————————————————————————————————————————————————————
if (($#!=1)); then
	printf "%s\n%s\n\n" "Usage:" "${ScriptName} <path>"
	exit $((UserError))
fi

# Initial values ———————————————————————————————————————————————————————————————
OutFile=$(realpath "${1}")
InFile="${OutFile}.old"
mv "${OutFile}" "${InFile}"

# Main —————————————————————————————————————————————————————————————————————————
while IFS=$'\t' read -r Start1 End1 Title; do
	IFS=$'\t' read -r Start2 End2 Dummy
	printf "%s\t%s\t%s\n" "${Start1}" "${Start2}" "${Title}" >> "${OutFile}"
done < "${InFile}"

How to use it:

  • Export your labels in Audacity.
  • Run your script in a terminal: NameOfTheScript.sh /Path/to/the/exported/file
  • You’ll now have two files: The modified file (Labeltrack.txt) and the original file (Labeltrack.txt.old), or something like that.
  • Remove your current label track in Audacity (I’m not sure if this is necessary, though).
  • Import the ”Labeltrack.txt” file in Audacity.
  • Done.

This doesn’t work if regions are supposed to overlap each other.
The end label’s title is ignored. Title of the region is the start label’s title.

Disclaimer
As always, backup before testing. This script is not thoroughly tested, so there might be bugs, even though the script is very simple. You might want to add some error handling, I guess. The script currently needs the text file to be properly made to begin with. If you, for instance, add a label in there somewhere before exporting, things will not work properly since the script will not detect that at all. It will just stupidly assume that every odd label is a start label and every even label is an end label. It’s basically just a loop with four lines reading two lines from the text file every time.

So this is not really a question, I just thought that maybe someone would find it useful. If not, feel free to remove it from this forum.

Doesn’t seem to work for me.

This was my input Labeltext.txt file:

1.003868	1.003868	1
2.733589	2.733589	2
4.146706	4.146706	3
5.388387	5.388387	4

and this was the output:

1.003868	1.003868	1	2.733589	2.733589	2	
4.146706	4.146706	3	5.388387	5.388387	4

Am I right in thinking that the intended output should be:

1.003868	2.733589	1
2.733589	4.146706	2
4.146706	5.388387	3

How about running this script in the Nyquist Prompt (https://manual.audacityteam.org/man/nyquist_prompt.html). This acts directly on point labels that are in the first label track:

;type tool
(setf labels (cadar (aud-get-info "Labels")))

(let (start end txt
      (indx 0))
  (dolist (label labels "")
    (setf start end)
    (setf txt (third label))
    (setf end (first label))
    (when (and start end)
      (aud-do (format nil "SetLabel: Label=~s Text=~s Start=~s End=~s~%"
                      indx txt start end))
      (incf indx))))

This may be why it didn’t appear to work.
To run the script on Xubuntu 20.04, or I assume other Debian based distros, either the script has to be made executable and then run with:

bash NameOfTheScript.sh /Path/to/the/exported/file

or

<path.to/script>/NameOfTheScript.sh /Path/to/the/exported/file

and not

sh NameOfTheScript.sh /Path/to/the/exported/file

When running the script with bash, I’m getting:

1.003868	2.733589	1
4.146706	5.388387	3

I assume that is as intended - Each odd numbered label treated as start, and each even numbered label treated as an end time, and the label text taken from the odd numbered label.

That can be done with a Nyquist script like this:

;type tool

(defun get-new-labels (labels)
  ;;; Returns Nyquist formatted labels in reverse order.
  (let ((isodd t)
        (newlabels ())
        start txt)
    (dolist (label labels newlabels)
      (if isodd
          (progn
            (setf start (first label))
            (setf txt (third label)))
          (push (list start (first label) txt) newlabels))
      (setf isodd (not isodd)))))

(defun delete-labeltrack (trknum)
  (aud-do (format nil "SelectTracks: Track=~s TrackCount=1 Mode=\"Set\"" trknum))
  (aud-do "SetTrackStatus: Focused=1")
  (aud-do "TrackClose:"))

(defun make-new-labels (labels)
  (dolist (label labels "")
    (aud-do (format nil "SelectTime: Start=~s End=~s~%"
                    (first label) (second label)))
    (aud-do "AddLabel:")
    ; Labels are in reverse order, so each new label is Label=0
    (aud-do (format nil "SetLabel: Label=0 Text=~s" (third label)))))

(let* ((oldlabels (aud-get-info "Labels"))
       (trknum (caar oldlabels))      ; id of first label track
       (oldlabels (cadar oldlabels))  ;first label track labels
       (newlabels (get-new-labels oldlabels)))
  (delete-labeltrack trknum)
  (make-new-labels newlabels))

Probably a very good idea. I had that idea in mind too, but I suck at Nyquist, so I tried something that I think I know slightly better.
I’ll try this next time, thanks for the suggestion!

Sure, no problem.

A nice aspect of Nyquist is that bits of code like this can easily be converted into plug-ins (like the effects below the dividing line in the Effect menu).
Some information about that here: https://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference

If you do, and if you need any help, feel free to ask. I’m sure that as you can write bash, you’d have little difficulty using Nyquist :wink:

Yes, I forgot to mention that the scripts needs to be executable, sorry for that.
Also, it has to be run in Bash, since a few lines are ”Bashisms”. It might work in Ksh and Zsh to, but I didn’t try that. It shouldn’t be too hard to rewrite the script to be POSIX compliant, though. I just like Bash better, therefore Bash for me… :smiley:
And you assume it right. Odd labels are start and even labels are end, and titles are expected at start labels, while end label names are ignored.

I guess I could do it, but as I haven’t yet (except a couple of years ago when I tried it a little), I think the syntax looks confusing with the parentheses around a function instead of around its parameters, or as in Bash, no parentheses needed at all in most cases:

(FunctionName Parameter1 Parameter2) ; Nyquist
FunctionName(Parameter1, Parameter2) // C, Basic etc.
FunctionName $Parameter1 $Parameter2 # Bash etc.

Of course it’s possible to get used to that too. :slight_smile:

:smiley: Yes it is.
I guess that I had an advantage when I started to use Nyquist in that I had no previous programming experience (other than a little bit of BASIC when I was a kid).

On the other hand, you don’t need to bother with semicolons at the end of lines :smiley:

There is an alternative syntax for Nyquist called SAL. I’ve rarely used it myself, but apparently it is often preferred by people that are familiar with C-like syntax. There’s some information about SAL here:
http://www.cs.cmu.edu/~rbd/doc/nyquist/part7.html

I tried this thing now, and there are two things that comes to mind:

  • Swedish characters (åäöÅÄÖ) doesn’t seem to be supported, they are all replaced with underscores.
  • Unnecessary regions are created: It seems like last End tag and next Start tag is converted to a region as well, which is not wanted. What I want is this:
    Let’s say I have 6 labels, L1–L6. All odd labels are start labels and all even labels are end labels. The regions I then want are the following:
    [L1]————[L2] [L3]————[L4] [L5]————[L6]
    But I also get:
    [L2]—[L3] [L4]—[L5] in between the wanted regions.

I am very unfamiliar with Nyquist to say the least, so I can’t say that I fully understand the code.
To begin with, is it possible to overcome the lack of UNICODE support?
I will study the code further and look some things up to see if I can understand the code better and maybe modify it myself to remove (or never create) those unnecessary regions, but all help are appreciated.

This works the way I want, except that UNICODE doesn’t seem to be supported. Many of my labels are Swedish song titles, and we have the letters åäöÅÄÖ in our alphabet (those are real letters, so they are not just a and o with some funny dots and stuff). They are all replaced with underscores. Is this possible to work around, somehow? Also I might have some Japanese titles in the future (such as 夜空の星 and 京都慕情), and I suspect those will be all underscores, which would be a little confusing, so full UNICODE support would be nice.

But otherwise, this code works great. Thanks!

Unfortunately that’s going to be a deal breaker for you. Nyquist does not have Unicode support.

Is there a way in Nyquist to export the labels, run a shell script on the exported label file and then import it back? That would solve this issue, I think. It would allow me to use my script without leaving Audacity.

The Audacity version of Nyquist cannot run a shell script. (It was considered too much of a security risk to allow Nyquist to run arbitrary system commands).

You can export and import labels, and you don’t need to use Nyquist for that.
See: Label Tracks - Audacity Manual
and: Importing and Exporting Labels - Audacity Manual

You could perhaps automate the process (export labels > shell script > import labels) with “Autokey” (available for most Linux distributions).

I actually use AutoKey and have done so for years, but I didn’t think of that in this case.

Yes, that seems to work. It’s not fast, but it’s OK. I had to insert some delays to make sure things weren’t executed before last command was finished. Here’s my AutoKey script, in case anyone is interested:

import os
import glob
import subprocess

def SendKey(Key):
    keyboard.send_keys(Key)
    time.sleep(0.3)

# Define needed paths.
LabelFile="/tmp/Etikettspår"
if os.path.exists(LabelFile):
    os.remove(LabelFile)

Script=os.path.join(os.environ['HOME'], 
                    "bin",
                    "ConvertLabelsPointsToRegions.sh")

# Export labels.
SendKey("<alt>+a")
SendKey("e")
SendKey("e")
SendKey("<ctrl>+a")
SendKey(LabelFile)
SendKey("<enter>")
time.sleep(2)

# Run the script.
subprocess.check_call([Script, LabelFile])

# Import the new labels.
SendKey("<alt>+a")
SendKey("i")
SendKey("e")
SendKey("<ctrl>+l")
SendKey(LabelFile)
SendKey("<enter>")

# Tidy up.
time.sleep(2)
FileList = glob.glob(LabelFile+"*")
for File in FileList:
    if os.path.exists(File):
        os.remove(File)

ConvertLabelsPointsToRegions.sh is the bash script that’s described in the original post, and it’s located in $HOME/bin/ in this case. Maybe I should have added a test to see if that script is present before running it, but there are probably quite a few more things to improve as well.

You may be able to make it quicker (and shorter) by creating a keyboard shortcut for “Export Labels” (Shortcuts Preferences - Audacity Manual)

Yes, I know about the shortcut thing. I always try to use shortcuts as much as possible, so that is usually one of the first things I look for in most applications. IN this case I already made a few shortcuts, like exporting audio, but I actually don’t have one for labels, yet…
Thanks for the suggestion. :slight_smile:

If anyone wonders why I take so long to look for any replies to my own posts, it’s because I thought I’d get an email every time someone replies, but apparently that doesn’t happen, and is maybe not supposed to happen, but I’m used to that in other forums, so I just assumed it’s the same here.

Way down at the bottom of the page, click on the wrench icon and click on “subscribe topic” so that it does NOT have a tick mark. You should then receive email notifications of replies.

It’s not ticked, but this time I actually got an email, so I don’t know what was going on there. Maybe I just didn’t recognize the email and deleted it without realizing what I was deleting. Ok, so at least that’s not an issue any more, as it seems. :mrgreen: