3.3 Real-time with Python
Last updated
Was this helpful?
Last updated
Was this helpful?
In the process of implementing an algorithm on an embedded system, it is sometimes worth testing it in a workspace with less constraints than on the final environment. Here we propose a Python framework that will help in this prototype/debugging state for the alien voice effect (and for future applications). In the we will implement it on the STM32 board and set up a timer to benchmark our implementation.
The main idea of this framework is to code in the same way as it will be done in C. This probably means that the Python implementation will be very cumbersome. However, this will make the porting to C much easier. One big obstacle is to think in a block-based manner, as if buffers were filled and processed one after the other in real-time. The other obstacle of porting the code from Python to C is the definition of variables and to manage their sizes.
Python requirements: Python 3, numpy, scipy.io
We propose the following template for simulating real-time processing in C with Python. Please note the use of block processing and the definition of the variables with the dtype
argument. You can find this code in the in the script.
We recommend cloning/downloading the repository so that you have all the necessary files in place, i.e. the speech.wav
file for the code below and utility functions for the various voice effects we will be implementing.
In the above code, we can observe several key sections:
Imports: The normal Python imports, use only the necessary ones as they will also need to be ported to C.
Parameters definition: Equivalent to #define
definitions we did in C.
Allocation: We do this to fix the length of our buffer, as needs to be done in C. Note that we use the data type of the speech file; we try to use files that are 16-bit or 32-bit PCM.
State variables: The init
function initializes all variables and should build all necessary lookup tables. We need the global
definition for those variables used in other functions.
process
function: This is the heart of the block-based processing. Please note that every time a global variable is touched in a function, you will need to define it as global
otherwise it will use a local clone. Note that we process one sample at a time to replicate how we would have to code our STM32 board in C!
Simulate block processing: This last part (after Nothing to touch after this!
) slices the test signal into buffers, then calls the process
function one buffer at a time. Finally, the modified output is written to a new WAV file.
As previously mentioned, we try to use integer variables in order to save processing time. So for the sinusoid lookup table, we will code it from the minimum to the maximum value possible in the corresponding (typically integer) data type. This scaling factor will need to be incorporated whenever using the lookup table.
In the above code, we can observe this use of the full range when creating the sinusoid table:
Now we come to the main processing for the alien voice effect. Notice that x_prev
and sine_pointer
are declared as global
as their values will be modified within the process
function.
You can test your implementation by running your script with the following line from the command line (replacing [script_name].py
with your script's name):
Notice how we define a callback
function that calls our process
function and that before streaming audio we call the init
function.
As before, run your script from the command line to try out your alien voice effect in real-time. Use headphones so that you avoid feedback!
Test signal: Here we load a WAV test signal with the function. We will parse the test signal one block at a time (according to buffer_len
) in order to simulate a real-time operation.
Below we provide you with the function that computes the sinusoid lookup table. This function is implemented in a file.
In the repository, there is an incomplete script for you to complete. Notice that in the init()
function we use the above build_sine_table
function to create the sinusoid lookup table, and that the variables needed in the process
function are declared as global
.
If your script runs without error, your alien voice effect will be applied to the file, and the processed speech will be saved into a file called "alien_voice_effect.wav"
.
When the output file sounds as expected - see/listen to in the repository - you can move on to implementing the effect in real-time!
Similar to the rt_simulated.py
script we saw earlier, we also provide a template script for performing block-based processing with your laptop's sound card. The script, entitled , can also be found in the repository.
The main difference is the setup code at the bottom of the script, which uses the library:
Hint: complete the process
function in the script , which can also be found in the repository.
In the , we guide you through implementing the alien voice effect on the microcontroller, as we also setup a timer to benchmark the implementation.