Contents

Batch Processing in Audacity using Python

Contents

I am not an audio person, but I have occasionally had to edit audio, and I’ve always used Audacity - it’s a great tool. Some time ago, I had to normalize sound levels on hundreds of voice recordings. A podcaster asked how I did it, I wrote it up in an email, and, thinking back, I decided it might be useful to others, so I’m posting it here. As I review the problem today, I think I would probably explore Audacity’s macro functionality to solve this type of problem - I don’t know why I jumped to scripting solution at the time. I did try the PyAudacity library, and found it was easier to interface directly though pipes.

  1. Save the file below as audacityautomation.py
  2. Set the infolder and outfolder folder variables in the script. Ensure the output folder is empty!
  3. Ensure Audacity is Open
  4. Ensure Audacity has scripting enabled (Edit -> Preferences -> Modules -> mod-script-pipe: set to Enabled) - you may have to restart Audacity
  5. Then, from command line/terminal, in the folder you’ve copied the script, run
1
python audacityautomation.py

audacityautomation.py output screenshot

The script, as is, just runs the normalize command on every file – that’s set on this line in the script:

1
do_command("Normalize: PeakLevel=-6")

But pretty much any operation in Audacity, you can script – the scripting reference manual can be found here: https://manual.audacityteam.org/man/scripting.html.

Hopefully this is useful for you,

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import os
import sys

# Adapted from https://github.com/audacity/audacity/blob/master/scripts/piped-work/pipe_test.py

# In Windows, remember to add escapes, like C:\\Users\\RichardAudette\\temp\\infolder\\ 
infolder="/home/raudette/temp/infolder"
outfolder="/home/raudette/temp/outfolder"

if sys.platform == 'win32':
    print("pipe-test.py, running on windows")
    TONAME = '\\\\.\\pipe\\ToSrvPipe'
    FROMNAME = '\\\\.\\pipe\\FromSrvPipe'
    EOL = '\r\n\0'
else:
    print("pipe-test.py, running on linux or mac")
    TONAME = '/tmp/audacity_script_pipe.to.' + str(os.getuid())
    FROMNAME = '/tmp/audacity_script_pipe.from.' + str(os.getuid())
    EOL = '\n'

print("Write to  \"" + TONAME +"\"")
if not os.path.exists(TONAME):
    print(" ..does not exist.  Ensure Audacity is running with mod-script-pipe.")
    sys.exit()

print("Read from \"" + FROMNAME +"\"")
if not os.path.exists(FROMNAME):
    print(" ..does not exist.  Ensure Audacity is running with mod-script-pipe.")
    sys.exit()

print("-- Both pipes exist.  Good.")

TOFILE = open(TONAME, 'w')
print("-- File to write to has been opened")
FROMFILE = open(FROMNAME, 'rt')
print("-- File to read from has now been opened too\r\n")


def send_command(command):
    """Send a single command."""
    print("Send: >>> \n"+command)
    TOFILE.write(command + EOL)
    TOFILE.flush()

def get_response():
    """Return the command response."""
    result = ''
    line = ''
    while True:
        result += line
        line = FROMFILE.readline()
        if line == '\n' and len(result) > 0:
            break
    return result

def do_command(command):
    """Send one command, and return the response."""
    send_command(command)
    response = get_response()
    print("Rcvd: <<< \n" + response)
    return response


# Actual logic is here
for filename in os.listdir(infolder):
    infile = os.path.join(infolder, filename)
    outfile = os.path.join(outfolder, filename)
    # checking if it is an MP3 file
    if os.path.isfile(infile) and "mp3" in infile.lower():
        infile_cleanname=infile.replace("\\","/")
        # All audacity operations here
        do_command(f"Import2: Filename=\"{infile_cleanname}\"")
        do_command("Select: Track=0")
        do_command("SelTrackStartToEnd")
        outfile_cleanname=outfile.replace("\\","/")
        #Start of sound manipulation operations
        # Any operation you want to perform on the samples, add them here
        #This line adjusts loudness - sets LUFS
        #do_command("LoudnessNormalization: LUFSLevel=-23")
        #This line sets peak sound level to -6 dB
        do_command("Normalize: PeakLevel=-6")
        #End of sound operations - export file
        do_command(f"Export2: Filename=\"{outfile_cleanname}\"")
        do_command(f"SaveProject2: Filename=\"{outfile_cleanname.replace('.mp3','.aup3')}\"")
        do_command("Close")