Last Details
In the previous section we implemented a basic granular synthesis voice transformer that lowers the pitch of the input voice. In this section we will address some remaining issues, namely:
implement an effect that raises the pitch of the voice (aka the "Chipmunks" effect)
properly initialize the buffer as a function of the pitch change
optimize the code a little more
The Chipmunks
To raise the pitch of the voice we need to set to values larger than one. As we have seen, this makes the effect noncausal, which we need to address by introducing some processing delay.
The way to achieve this is to place the audio buffer's input index forward with respect to the output index; let's do this properly by creating an initialization function for the buffer that takes the resampling factor as the input.
static void InitBuffer(float Alpha) {
memset(buffer, 0, BUF_LEN * sizeof(int16_t));
alpha = (int32_t)(0x7FFF * Alpha);
// input index for inserting DMA data
if (Alpha <= 1)
buf_ix = 0;
else
buf_ix = ...;
prev_ix = BUF_LEN - GRAIN_STRIDE;
curr_ix = 0;
grain_m = 0;
}
By now you know where to place this code but don't forget to
add the following line to the file
main.h
between the/* USER CODE BEGIN Includes */
tags.#include <memory.h>
declare the function prototype in the
USER CODE BEGIN PFP
blockcall the function before launching the DMA transfers:
UNMUTE SET_MIC_LEFT InitBuffer(3.0 / 2.0); // begin DMAs HAL_I2S_Transmit_DMA(&hi2s1, (uint16_t *) dma_tx, FULL_BUFFER_SIZE); HAL_I2S_Receive_DMA(&hi2s2, (uint16_t *) dma_rx, FULL_BUFFER_SIZE);
Switching between effects
We can use the blue button on the Nucleo board to switch between Darth Vader and the Chipmunks; to do so, define the following constants at the beginning of the code
#define DARTH (2.0 / 3.0)
#define CHIPMUNK (3.0 / 2.0)
and modify the user button callback like so:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
if (GPIO_Pin == B1_Pin) {
// blue button pressed
if (user_button) {
user_button = 0;
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
InitBuffer(DARTH);
} else {
user_button = 1;
HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
InitBuffer(CHIPMUNK);
}
}
}
Final optimizations
In the main processing loop, we are performing two checks on the value of grain_m
per output sample. However, in the current implementation, both the stride and the taper lengths are multiples of the size of the DMA half-buffer. This allows us to move these checks outside of the processing loop and perform them once per call rather than once per sample
Solutions
Are you ready to see the answers ? :)
Last updated
Was this helpful?