6.2 Implementation
Below are the LPC utility functions provided in the IPython notebook.
The function bac
is sufficient from a real-time microcontroller "point of view" as it performs operations sample-by-sample in order to compute the entries of the (biased) autocorrelation matrix . For our microcontroller implementation in C
, we may, however, wish to pre-allocate a global array for r
, as its values will change for each grain.
The function ld
(for performing the Levinson-Durbin recursion) is not suitable for a microcontroller C implementation, as we have array operations (np.dot
) and memory is allocated on the fly (np.r_
concatenates values into a new row vector).
We will therefore re-implement the ld
function so that porting it to C will be much more straightforward. Below we provide you an incomplete function ld_eff
that is meant to implement Levinson-Durbin recursion in a "C-friendly" manner.
TASK 1: Complete the above function ld_eff
in the utils_lpc.py
file so that it correctly implements Levinson-Durbin recursion.
Hint: we refer you to this document (p. 5) in order to determine the correct expression for k
and a[j]
.
You can test your implementation by running the script test_lpc_utils.py
. The script should print CORRECT!
if you have successfully implemented the function; otherwise it will print Something's wrong...
or error out if you have a bug in your implementation.
As for bac
, for our microcontroller implementation of ld_eff
in C
, we may wish to pre-allocate global arrays for a
and a_prev
.
Modifying the process
function
process
functionIn fact, it is possible to use the same function for "vanilla" and LPC granular synthesis pitch shifting. We can do this by introducing a boolean variable use_LPC
.
Below, we provide the incomplete process
function, which you can find in this script.
TASK 2: As a sanity check, you can first copy your code from your granular synthesis implementation into the above process
function and the init
function in the script granular_synthesis_LPC_incomplete.py. (Copy the appropriate lines under the comments # copy from granular synthesis
.)
Run the file and make sure the output is the same as before!
We can now begin adding the code for LPC! Let's remind ourselves of the steps we mentioned in the previous section:
1. Compute the LPC coefficients for the input speech
TASK 3: Complete the code after the comment # compute LPC coefficients
, namely compute the LPC coefficients for the input raw samples.
Hint: use the function lpc_eff
and cast the input raw samples to np.float32
.
2. Inverse-filter the raw samples in order to estimate the excitation
TASK 4: Complete the code after the comment # estimate excitation
, namely filter the raw input samples with the "recently" obtained LPC coefficients.
Hints:
We are applying an FIR filter in this case; recall your implementation from the Digital Filter Design chapter, notably the code from this script. In this case
input_buffer
should be the concatenated raw samples vector,x
should belpc_prev_in
, and there is no equivalent toy
since this is an FIR filter.You can rewrite into the concatenated raw samples vector, NOT
input_buffer
!Don't forget to apply
GAIN
!
3. Apply pitch-shifting on the excitation signal
This is already done with your code from the granular synthesis effect!
4. Forward-filter the modified grain
TASK 5: Complete the code after the comment # forward filter the resampled grain
, namely filter the resampled grain with the LPC coefficients.
Hints:
We are applying an IIR filter in this case.
You can rewrite into the resampled grain vector.
Use
lpc_prev_out
for the previous output samples.
And that's all the extra code needed for this LPC feature! Try out your completed granular_synthesis_LPC_incomplete.py script with the fixed WAV file (make sure use_LPC=True
) and listen to the output to see if it sounds correct.
If you notice some strange output, make sure you are casting (when appropriate) to int
; this is a common point for mistakes.
Real-time implementation
TASK 6: When your implementaton works with the fixed WAV file, you can complete the sounddevice
template in order to run the effect in real-time with your laptop's soundcard.
Congrats on incorporating this LPC component to your granular synthesis pitch shifter! Given the Python implementation, the porting to C should be more straightforward. As noted earlier, it may be useful to pre-allocate memory for the LPC coefficients and their computation.
In the next chapter, we implement a DFT-based pitch shifter. With this effect, we will shift our speech up in order to create a chipmunk-like voice; no need to inhale helium!
Last updated