Digital Audio Projects
- Extending the Training with Projects
- Digital Audio Effects
- Amplitude Modulation Tremolo Effect
- Vibrato Effect
- Fuzz Effect
- Wah-wah Effect
- Chorus Effect
- Audio Jammer
Extending the Training with Projects
The first section is based solely in the time domain. The second section includes the frequency domain as well, borrowing much of the code introduced during ARM's Online DSP Course.Digital Audio Effects
With thanks to Prof. Dave Marshall at Cardiff School of Computer Science, I've implemented a couple of his suggested Digital Audio Effects on the Cypress FM4 Start Kit card. This gives a real-time DSP audio effects usually associated with a guitar. Only here they are applied to my child's ukulele instead.
Amplitude Modulation Tremolo Effect
A fixed modulation typically in the range 6-10Hz (as 20Hz sounded robotic), altering the amplitude by a factor 1.0±0.3.
Vibrato Effect
Varying the time delay periodically using a sine function to compress and expand the waveform at 8-10Hz.
Fuzz Effect
Use of a non-linear function commonly used to simulate distortion or fuzz, using a non-linear exponential function with a controllable level of distortion. Its common to mix part of the distorted signal with original signal for output.
Wah-wah Effect
Using a time varying State Variable Filter for the band pass filter and a sawtooth waveform to sweep the pass band up and down between 500Hz and 3,000Hz, at 3,000Hz/s.
Ukulele → Pre-Amp → Cypress FM4 Start Kit → Speaker
The lad looks a bit bored of the testing! (Direct YouTube Link)

Chorus Effect
Adding multiple but slightly delayed copies of the original with some modulation borrowed from the previous Tremolo or Vibrato examples, and then rendered spatially in stereo using coefficients:
\[ \big[L, R\big] = \Bigg[ cos \left(\frac{\theta\pi}{180}\right) + sin \left(\frac{\theta\pi}{180}\right), \; cos \left(\frac{\theta\pi}{180}\right) - sin \left(\frac{\theta\pi}{180}\right) \Bigg] \]The effects here are achieved by creating 6 modified copies of the original at different angles up to 45° from centre, 3 to the left, 3 to the right, and added to the original in the centre. The copies used delays varying between 0.0 and 0.1s, and each was given a vibrato modulation (phase) of 7, 8 or 9 Hz. Amplitude modulation was not used in this recording.
This example showed up how expensive trigonometric function lookups are. I decided to pre-compute 6, 7 and 8 Hz sine functions used for modulation and store the results in an array for quicker lookups at the expense of storage. A Time-space trade-off.
Audio Jammer
For your amusement, there's an interactive demonstration of an audio jammer online. "Audio jammers are popular tools used during confidential meetings. They produce a unique sound for masking and protecting conversations from external listening devices." For my project, I'll make the audio produced responsive to the audio received, e.g. using some of the digital audio effects in response to different frequencies heard.

The first stage was to FFT the input and then inverse FFT it to the output to prove I could get out what I put in. Its worth remembering that the inverse FFT is the same FFT function, only the second application gives the negative timeseries. Hence the minor difference in arguments in the call below. 'arm_cfft_f32()' is the fastest FFT function tried on the ARM DSP course.
// DMA_BUFFER_SIZE = 128
for (i = 0; i < DMA_BUFFER_SIZE; i++) {
// Real
samples[2 * i] = (float32_t)rx_proc_buffer[i][LEFT];
// Imaginary
samples[2 * i + 1] = 0.0;
}
arm_cfft_f32(&arm_cfft_sR_f32_len128, samples, 0, 1);
for (i = 0; i < DMA_BUFFER_SIZE; i++) {
// Straight Copy
// Real
samples_new[2 * i] = samples[2 * i] * SCALING_FACTOR;
// Imaginary
samples_new[2 * i + 1] = samples[2 * i + 1] * SCALING_FACTOR;
}
arm_cfft_f32(&arm_cfft_sR_f32_len128, samples_new, 1, 1);
for (i = 0; i < DMA_BUFFER_SIZE; i++) {
// Real values only, imagainary values are ~0
tx_proc_buffer[i][LEFT] = (int16_t) samples_new[2 * i] * SCALING_FACTOR;
//tx_proc_buffer[i][LEFT] = (int16_t) tone[2 * i];
tx_proc_buffer[i][RIGHT] = rx_proc_buffer[i][LEFT];
}
For the 'detect' stage, two audio ranges were chosen, voice (125-250 kHz) and whistle 1312.5-1625 kHz). The bins are 62.5kHz in width from the sampling frequency of 8kHz and 128 bins in the FFT. If either of these ranges are detected using different amplitude levels, then a multiple tone response is injected.
// Inspect (detect)
// 16 is 1kHz, 32 is 2kHz. Total 128 bins with sampling frequency 8kHz.
// Speech is bins 2-4 (125-250 kHz)
// Whistle is bins 21-26 (1312.5-1625 kHz)
magnitude[i] = sqrt((samples[2 * i] * samples[2 * i]) +
(samples[2 * i + 1] * samples[2 * i + 1]));
if ( ((i >= 2) && (i <= 4) && (magnitude[i] > 100000.0f)) ||
((i >= 21) && (i <= 26) && (magnitude[i] > 300000.0f)) ) {
trigger = 1;
}
The frequency response is composed by the supposition of multiple tone in the time domain, and then transformed by an FFT call. This prepared spectrum is now ready to be injected and if necessary mixed before being transformed back. In this case no mixing was performed, just a substitution.
// Deploy counter measure
for(i = 0; i < DMA_BUFFER_SIZE; i++) {
if (trigger > 0) {
// Manufacture an output, e.g. the FFT of s sine created at startup.
// Real
samples_new[2 * i] = sin_fft[2 * i] * SCALING_FACTOR;
// Imaginary
samples_new[2 * i + 1] = sin_fft[2 * i + 1] * SCALING_FACTOR;
} else {
// Real
samples_new[2 * i] = 0.0f;
// Imaginary
samples_new[2 * i + 1] = 0.0f;
}
}
For the time domain response I simply swapped out the frequency domain response for the original time series and mixed in the time domain when triggered. A little unexciting, but proves the point. Here are the frequency domain plots with green being the stimulus signal from a tone generator, and the red being the response from the Cypress FM4 Start Kit.
Job done. Just needs fine tuning to certain members of the family!