Skip to content

Instantly share code, notes, and snippets.

@SteveDaulton
Last active March 9, 2023 21:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SteveDaulton/06037f68adad6afef47d9e11a82a4fc6 to your computer and use it in GitHub Desktop.
Save SteveDaulton/06037f68adad6afef47d9e11a82a4fc6 to your computer and use it in GitHub Desktop.
Sequential recording with Audacity with Python.
#!/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