Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python effect prototyping framework #1290

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions scripts/python-effect-prototyping-framework/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Byte-compiled
__pycache__/

# Sphinx documentation
docs/_build/

# Environments
.env
.venv
env/
venv/
ENV/

# Audio files
*.wav
139 changes: 139 additions & 0 deletions scripts/python-effect-prototyping-framework/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
Intro
-----

Purpose of this framework and usage.


Contents:
- audio-testbench-fileIO.py
- audio-testbench-realtime.py
- effect_prototypes.py
- utilities.py
- audio-data-lib

effect_prototypes.py
-----------------

Implements effects that are to be tested with this framework.
Initialize the effect with desired default parameters.
Implement set_params for setting effect parametrs and process for processing blocks of audio data
in numpy arrays of size [number of samples per buffer, number of channels].
Implement a help function for explaining purpose of each effect and its parameters.

audio-testbench-fileIO.py
-------------------------
This script main purpose is to serve as a framework for prototyping new dsp
algorithm implementation in python.

The input audio file is processed by the specified effect from effects
module, in a frame by frame way, as it would be processed in real time.

Effect parameters are passed as arguments, and the processd file is
saved to the described output file.

arguments:

-effect: (mandatory)
Select effect to be used from effects module.
-fxparameters: (mandatory)
comma seperated values of the effect parameters.
-input_file: (mandatory)
Input file directory.
-buffer_length: (optional/defaults to 1024)
Length of buffer that each block will be processed at.
-output_file: (mandatory)
Output file directory.
-plot: (optional/default 0)
if this flag is set then a debug plot is performed a the end of audio processing the whole file.

example call:
python3 audio-testbench-fileIO.py -e FxGain -i 'audio-data-lib/test_input_mono.wav' -o 'audio-data-lib/test_output_mono.wav' -f "0.25, 0"

usage: audio-testbench-fileIO.py [-h] [-e EFFECT_ID] [-f FXPARAMETERS]
[-i INPUTFILE] [-o OUTPUTFILE]
[-b BUFFERLENGTH] [-p PLOTFLAG]


optional arguments:
-h, --help show this help message and exit
-e EFFECT_ID, --effect_id EFFECT_ID
effect to be applied to inupt
-f FXPARAMETERS, --fxparameters FXPARAMETERS
effect parameters comma seperated
-i INPUTFILE, --inputfile INPUTFILE
Input file directory
-o OUTPUTFILE, --outputfile OUTPUTFILE
Output file directory
-b BUFFERLENGTH, --bufferlength BUFFERLENGTH
Define length of buffer to be used for block
processing
-p PLOTFLAG, --plotflag PLOTFLAG
Set to get a debug plot at the end of proceesing



audio-testbench-realtime.py
----------------------------

This script main purpose is to serve as a framework for prototyping new dsp
algorithm implementation in python.

Accepts audio input from the selected device and process audio in real time in specified
buffer length.

A minimal cli interface can issue python statements and alter the parameters of the effect real time.

arguments:

-effect: (mandatory)
Select effect to be used from effects module.
-fxparameters: (mandatory)
comma seperated values of the effect parameters.
-input_device: (optional)
Device to input audio from, if not specified default audio device will be selected.
-buffer_length: (optional/defaults to 1024)
Length of buffer that each block will be processed at.
-output_device: (optional)
Device to consume audio to, if not specified default audio device will be selected.
-samplerate: (optional)
Select sampling rate if not specified, default samplerate of selected device will be set.

example call:
python3 audio-testbench-realtime.py -e FxGain -f "0.25, 0"


usage: audio-testbench-realtime.py [-h] [-e EFFECT_ID] [-f FXPARAMETERS]
[-i INPUT_DEVICE] [-o OUTPUT_DEVICE]
[-c CHANNELS] [-t DTYPE] [-s SAMPLERATE]
[-b BLOCKSIZE] [-l LATENCY]

optional arguments:
-h, --help show this help message and exit
-e EFFECT_ID, --effect_id EFFECT_ID
effect to be applied to inupt
-f FXPARAMETERS, --fxparameters FXPARAMETERS
effect parameters comma seperated
-i INPUT_DEVICE, --input-device INPUT_DEVICE
input device ID or substring
-o OUTPUT_DEVICE, --output-device OUTPUT_DEVICE
output device ID or substring
-c CHANNELS, --channels CHANNELS
number of channels
-t DTYPE, --dtype DTYPE
audio data type
-s SAMPLERATE, --samplerate SAMPLERATE
sampling rate
-b BLOCKSIZE, --blocksize BLOCKSIZE
block size
-l LATENCY, --latency LATENCY
latency in seconds



utilities.py
------------
Audio framework helper functions.

audio-data-lib
--------------
Directory used in the examples/test as audio files root directory.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
101 changes: 101 additions & 0 deletions scripts/python-effect-prototyping-framework/audio-testbench-fileIO.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#!/usr/bin/env python3
"""
This script main purpose is to serve as a framework for prototyping new dsp
algorithm implementation in python.

The input audio file is processed by the specified effect from effects
module, in a frame by frame way, as it would be processed in real time.

Effect parameters are passed as arguments, and the processd file is
saved to the described output file.

arguments:

-effect: (mandatory)
Select effect to be used from effects module.
-fxparameters: (mandatory)
comma seperated values of the effect parameters.
-input_file: (mandatory)
Input file directory.
-buffer_length: (optional/defaults to 1024)
Length of buffer that each block will be processed at.
-output_file: (mandatory)
Output file directory.
-plot: (optional/default 0)
if this flag is set then a debug plot is performed a the end of audio processing the whole file.

example call:
python3 audio-testbench-fileIO.py -e FxGain -i 'audio-data-lib/test_input_mono.wav' -o 'audio-data-lib/test_output_mono.wav' -f "0.25, 0"

author:
sqrwvzblw - [email protected]
"""

import sys
import utilities
import matplotlib.pyplot as plt
import numpy as np
import soundfile as sf
import effect_prototypes
import audio_framework_params as afp
import ipdb

if __name__ == "__main__":

# Parse User Input Arguments
[effect_id, fx_parameters_list, input_file_dir, buffer_length, output_file_dir, plot_id] = utilities.parse_input_arguments_file()

ipdb.set_trace()
# Match effect id to imported fx modules and initialize effect object.
try:
exec("effect=effect_prototypes."+effect_id+"()")
except:
print("something went wrong - effect id either mispelled or not properly imported")
sys.exit(-1)

# Collect info for audio input file.
audio_file_info = sf.info(input_file_dir, verbose=False)
num_of_channels = audio_file_info.channels
input_length = audio_file_info.frames

# Save to global framework variables collected data.
afp.input_file_samplerate = audio_file_info.samplerate
afp.input_file_channels = num_of_channels
afp.input_file_duration = audio_file_info.duration
afp.input_file_dir = input_file_dir
afp.processing_buffer_length = buffer_length

# Read input file into a numpy array object and reshape so as to be able to work with multichannel arrays.
input_array_data, samplerate = sf.read(input_file_dir)
input_array = np.reshape(input_array_data,(input_length, num_of_channels))
# Initialize output array
output_array = np.zeros_like(input_array)

# Setup requested effect parameters
effect.set_params(fx_parameters_list)

# Process each block of buffer_length samples with effect.process
sample_index = 0
while sample_index<input_length:
block = input_array[sample_index:sample_index+buffer_length,:]
output_block = effect.process(block)
output_array[sample_index:sample_index+buffer_length,:] = output_block
sample_index+=buffer_length

#TODO: mayybe add here a scope update with block and output block.

# write result to output file
sf.write(output_file_dir, output_array, audio_file_info.samplerate)
print("Processed data written to ",output_file_dir)

# plot final waveforms
if plot_id:
plt.figure(1)
plt.title('input vs output waveform')
plt.plot(input_array)
plt.plot(output_array)
plt.show()

# Terminate session
print("Processing complete\n")
sys.exit(0)
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""
This script main purpose is to serve as a framework for prototyping new dsp
algorithm implementation in python.

Accepts audio input from the selected device and process audio in real time in specified
buffer length.

A minimal cli interface can issue python statements and alter the parameters of the effect real time.

arguments:

-effect: (mandatory)
Select effect to be used from effects module.
-fxparameters: (mandatory)
comma seperated values of the effect parameters.
-input_device: (optional)
Device to input audio from, if not specified default audio device will be selected.
-buffer_length: (optional/defaults to 1024)
Length of buffer that each block will be processed at.
-output_device: (optional)
Device to consume audio to, if not specified default audio device will be selected.
-samplerate: (optional)
Select sampling rate if not specified, default samplerate of selected device will be set.

example call:
python3 audio-testbench-realtime.py -e FxGain -f "0.25, 0"

author:
sqrwvzblw - [email protected]

TODO: add realtime plot of input vs processed frames using queue and maplotlib in a seperate thread.
TODO: tidy up keyboard input functionality.

hint: try stty sane in case enter/return does not return a new line but ^M instead.

author: sqrwvzblw - [email protected]
"""

import sys
import sounddevice as sd
import utilities
import effect_prototypes
import threading
import time
import ipdb

kbdInput = ''
playingID = ''
finished_kbd_flag = True


def kbdListener():
global kbdInput, finished_kbd_flag
kbdInput = input("> ")
finished_kbd_flag = True

[effect_id, fx_parameters_list,input_device,output_device,samplerate,blocksize,dtype,latency,channels] = utilities.parse_input_arguments_rt()

# Match effect id to imported fx modules and initialize effect object.
try:
ipdb.set_trace()
exec("effect=effect_prototypes."+effect_id+"()")
except:
print("something went wrong - effect id either mispelled or not properly imported")
sys.exit(-1)

# # Setup requested effect parameters
effect.set_params(fx_parameters_list)

try:

def audio_callback(indata, outdata, frames, time, status):
if status:
print(status)
outdata[:] = effect.process(indata)

with sd.Stream(device=(input_device, output_device),
samplerate=samplerate, blocksize=blocksize,
dtype=dtype, latency=latency,
channels=channels, callback=audio_callback):

# Handle input commands from keyboard input thread
print("Type python expression to evaluate - type quit to exit.")
while True:
if playingID != kbdInput:
if kbdInput == "quit" or kbdInput == "q" :
print("Thank you very much")
sys.exit(0)
try:
if kbdInput == "help" or kbdInput == "h" :
print("type a python statement to evaluate")
print("i.e if gain is an effect parameter, it can be set by typing:")
print("effect.gain = 0.7")
else:
exec(kbdInput)
except:
print("Unexpected error - not a valid statement")

playingID = kbdInput
if finished_kbd_flag:
finished_kbd_flag = False
listener = threading.Thread(target=kbdListener)
listener.start()
time.sleep(2)

except KeyboardInterrupt:
print('\nProcess terminated by user')
sys.exit(0)
except Exception as e:
print(type(e).__name__ + ': ' + str(e))
sys.exit(-1)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python3
"""
Global parameters of audio processing framework to be shared between submodules.
"""
input_file_samplerate = 44100
input_file_channels = 1
input_file_duration = 0
input_file_dir = ''

processing_buffer_length = 0
Loading