Last active
March 9, 2023 21:30
-
-
Save SteveDaulton/06037f68adad6afef47d9e11a82a4fc6 to your computer and use it in GitHub Desktop.
Sequential recording with Audacity with Python.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
# -*- coding: utf-8 -*- | |
""" | |
Record Parts: | |
============= | |
A demonstration Python application controlling Audacity to make a | |
sequence of recordings. There will inevitably be short breaks between | |
each recording due to the time required to Export, clean up and start | |
the next recording. | |
Before running this script, ensure that Audacity is running and | |
mod-script-pipe is enabled. | |
""" | |
import sys | |
from time import sleep | |
from pathlib import Path | |
from os import remove | |
import pipeclient as pc | |
def config_ext() -> str: | |
"""Return project file extension.""" | |
extensions: tuple = ('wav', 'mp3', 'ogg', 'flac') | |
while True: | |
ext: str = input('Export file type (wav, mp3, ogg, flac): ') | |
if ext in extensions: | |
return ext | |
print('Enter wav, mp3, ogg, or flac') | |
def config_fname(ext) -> str: | |
"""Return base file name.""" | |
while True: | |
fname: str = input('Root name for projects: ') | |
print(f'Projects will be named {fname}1.{ext}, {fname}2.{ext}, ...') | |
reponse = input('Use this project name? Y/N: ') | |
if reponse.lower() == 'y': | |
return fname | |
def config_fpath(fname: str, ext: str) -> Path: | |
"""Return output directory""" | |
while True: | |
path = Path(input('Output directory: ')).expanduser() | |
try: | |
path.mkdir(parents=True, exist_ok=False) | |
except FileExistsError: | |
reponse = input(f'{path} already exists. Use this path? Y/N: ') | |
if reponse.lower() != 'y': | |
continue | |
except PermissionError: | |
print(f'{path} is not writeable. Try again.') | |
continue | |
try: | |
path.joinpath(f'{fname}.{ext}').touch() | |
remove(path.joinpath(f'{fname}.{ext}')) | |
except PermissionError: | |
print(f'{path} is not writeable. Try again.') | |
continue | |
print(f'Output folder is: {path}') | |
return path | |
def config_recordings() -> tuple: | |
"""Return tuple(dur, number) | |
dur is the duration of each recording in seconds. | |
number is the number of recordings to make. | |
""" | |
while True: | |
response: str = input('Length of each recording in minutes: ') | |
try: | |
dur: float = float(response) * 60 | |
except ValueError: | |
print(f'"{response}" is not a number.') | |
continue | |
response = input('Number of recordings: ') | |
try: | |
number: int = int(response) | |
except ValueError: | |
print(f'"{response}" is not a whole number.') | |
continue | |
return (dur, number) | |
def main(params: dict): | |
"""Loop through commands to record and export recordings.""" | |
client = pc.PipeClient() | |
client.write('GetPreference: Name="AudioIO/RecordChannels"') | |
# Wait for the response: | |
while not(chans := client.read()): | |
sleep(0.01) | |
# print('Chans:', chans) | |
for idx in range(1, params['rcount'] + 1): | |
filename = params['fpath'].joinpath( | |
f'{params["fname"]}{idx}.{params["ext"]}' | |
) | |
client.write(f'SelectTime: Start=0 End={params["duration"]}') | |
client.write('Record2ndChoice:') | |
# Audacity 2.4.2 requires waiting for recording to complete. | |
if LEGACY: | |
sleep(params['duration'] + 1) | |
client.write('SelectAll:') | |
client.write(f'Export2: Filename={filename} NumChannels={chans}') | |
client.write('RemoveTracks:') | |
# Configuration options. | |
# Overrides interactive options when 'USE_CONFIG = True'. | |
USE_CONFIG = False | |
CONFIG: dict = {'ext': 'mp3', # Export format | |
'fname': 'myaudio', # Root file name | |
'fpath': Path('~/Desktop/mytest'), # Export directory | |
'duration': 10.0, # Duration of each recording (seconds) | |
'rcount': 3 # Number of recordings | |
} | |
# Older versions of Audacity (including 2.4.2) requires waiting for | |
# recording to complete before exporting. We can't know the exact time | |
# it will take Audacity to initiate recording, so by default this script | |
# allows 'duration + 1' seconds for recording to complete. This | |
# workaround is not required for later versions of Audacity and may | |
# be disabled by setting 'LEGACY = False'. | |
LEGACY = True | |
if __name__ == '__main__': | |
if USE_CONFIG: | |
config: dict = CONFIG | |
else: | |
config = {} | |
print('\nConfigure output location:') | |
print('==========================') | |
config['ext'] = config_ext() | |
config['fname'] = config_fname(config['ext']) | |
config['fpath'] = config_fpath(config['fname'], config['ext']) | |
print('\nConfigure length and number of recordings:') | |
print('==========================================') | |
config['duration'], config['rcount'] = config_recordings() | |
print(config) | |
sys.exit(main(config)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment