Audio Spectrum analyser - FFT using mbed-dsp - driving RGB(W) LED or PC python script.

Dependencies:   HSI2RGBW_PWM NVIC_set_all_priorities mbed-dsp mbed FastAnalogIn

Spectrum Analyzer

Code ported from Tony DiCola at AdaFruit FFT: Fun with Fourier Transforms.
Modifications were made to allow the use of the KL25Z on-board RGB LED or an external RGBW power LED.
See items marked with * in the next sections.

Main features

  • Uses mbed-dsp library.
  • Uses FastAnalogIn to allow a sample rate of 40kHz.
  • Display the audio spectrum on a single RGB(W) LED*.
  • Display the audio spectrum on your computer using an audio spectrogram tool (python script).
  • Change parameters using a terminal connection : Sample rate, min/max db, slowdown*.

Information

Detailed information and download of the python scripts is available here.

KL25Z wiring

Audio inputs
The software samples a single audio channel at 40kHz and applies a Fourier transform to return the frequency spectrum.
Analog input : PTC2.
A DC offset, and possibly some amplification or attenuation, is needed before the signal is fed into the analog inputs.
The opamp choice is not critical, just make sure it supports single supply operation.
Schematic
Currently, only one channel is used when the KL25Z is sampling at 40kHz.
/media/uploads/frankvnk/opamp_input_stage-tlc2264.png

External PWM outputs

PinColor
PTD4Red
PTA12Green
PTA4Blue
PTA5White

If you want to use a RGB LED, remove the last pin declaration (PTA5).
The conversion routine automatically switches from HSI/RGBW to HSI/RGB.
To use the external RGBW LED, enable following line in the code:

#define RGBW_ext // Disable this line when you want to use the KL25Z on-board RGB LED.

Commands

Parameters can be altered through the serial port Using a terminal program (eg : TeraTerm).
Communication settings : 38400 baud + local echo.
Each command needs to be terminated with a semicolon.
Use GET <command>; to read a parameter.
Use SET <command> <value>; to set a parameter.

CommandDescription
GET MAGNITUDES;Reads back the FFT magnitudes.
Number of magnitudes = PIXEL_COUNT.
GET SAMPLES;Reads back the current samples.
Number of samples = PIXEL_COUNT.
GET FFT_SIZE;The size of the FFT.
GET SAMPLE_RATE_HZ;Audio sample rate (Hz).
SET SAMPLE_RATE_HZ <value>;Change the audio sample rate (see also 'Limitations' below).
GET LEDS_ENABLED;LEDs enabled status.
SET LEDS_ENABLED <value>;Control if the LED's should display the spectrum or not.
1 is true, 0 is false.
GET SPECTRUM_MIN_DB;Audio intensity (in decibels) that maps to low LED brightness.
SET SPECTRUM_MIN_DB <value>;Change low sensitivity (0...100dB).
GET SPECTRUM_MAX_DB;Audio intensity (in decibels) that maps to high LED brightness.
SET SPECTRUM_MAX_DB <value>;Change high sensitivity (0...100dB).
GET SLOWDOWN;LED visualisation delay.
SET SLOWDOWN <value>;* Useful to visualize the spectrum using a single RGB(W) LED.
Without this command, the color values are shown too fast for the human eye.
This allows you to slow down the visualization without interfering in the FFT conversion.
Each frequency window is shown a little longer.
The number of frequency windows depends on the value of the PIXEL_COUNT variable.
[0...999] The larger the value, the longer each frequency window is shown - a good value is 4 when PIXEL_COUNT = 32 (choose a higher SLOWDOWN value when PIXEL_COUNT is lowered).
[1000...1000 + PIXEL_COUNT] Selecting a value within this range allows us to lock to a specific frequency window.

NOTE : PIXEL_COUNT is declared at compile time and determines the number of frequency windows (aka LED colors).

Limitations

The original code was written for a cortex-M4 processor.
For a cortex-M0 processor, following limitations apply:

SAMPLE_RATE_HZFFT_SIZE
1...40000max 64

Demo Videos

Parameter settings

SLOWDOWN16 (initial value - changed to 1000+ during the video to demonstrate the lock option).
SAMPLE_RATE_HZ40000
SPECTRUM_MIN_DB40.0
SPECTRUM_MAX_DB80.0
FFT_SIZE64
PIXEL_COUNT32

Using the on-board RGB LED
SLOWDOWN is set to different values (normal mode and locked mode).

Using an external 10W RGBW LED
SLOWDOWN is set to different values (normal mode and locked mode).

Spectrogram on Computer screen (serial input from KL25Z board

Revision:
2:035d551759a5
Parent:
1:736b34e0f484
--- a/main.cpp	Sat Mar 08 18:56:14 2014 +0000
+++ b/main.cpp	Sat Mar 08 19:30:20 2014 +0000
@@ -6,6 +6,7 @@
 #include "NVIC_set_all_priorities.h"
 #include <ctype.h>
 #include "arm_math.h"
+#include "arm_const_structs.h"
 #include "hsi2rgbw_pwm.h"
 #include "FastAnalogIn.h"
 
@@ -35,21 +36,18 @@
 // These values can be changed to alter the behavior of the spectrum display.
 // KL25Z limitations
 // -----------------
-// - When used without the Spectrogram python script :
-//   When SAMPLE_RATE_HZ = 10000...40000, max allowed FFT_SIZE is 16.
-//   When SAMPLE_RATE_HZ < 9000, FFT can go up to 256 
 // - When used with the Spectrogram python script :
 //   There is a substantial time lag between the music and the screen output.
-//   Max allowed SAMPLE_RATE_HZ is 8000
-//   Max allowed FFT_SIZE is 16
+//   Max allowed SAMPLE_RATE_HZ is 40000
+//   Max allowed FFT_SIZE is 64
 ////////////////////////////////////////////////////////////////////////////////
 
-int SLOWDOWN = 3;                       // Create an optical delay in spectrumLoop - useful when only one RGB led is used.
+int SLOWDOWN = 4;                       // Create an optical delay in spectrumLoop - useful when only one RGB led is used.
                                         // Only active when nonzero.
                                         // A value >= 1000 and <= 1000 + PIXEL_COUNT fixes the output to a single frequency
                                         // window = a single color.
 int SAMPLE_RATE_HZ = 40000;             // Sample rate of the audio in hertz.
-float SPECTRUM_MIN_DB = 40.0;           // Audio intensity (in decibels) that maps to low LED brightness.
+float SPECTRUM_MIN_DB = 30.0;           // Audio intensity (in decibels) that maps to low LED brightness.
 float SPECTRUM_MAX_DB = 80.0;           // Audio intensity (in decibels) that maps to high LED brightness.
 int LEDS_ENABLED = 1;                   // Control if the LED's should display the spectrum or not.  1 is true, 0 is false.
                                         // Useful for turning the LED display on and off with commands from the serial port.
@@ -62,6 +60,7 @@
 // INTERNAL STATE
 // These shouldn't be modified unless you know what you're doing.
 ////////////////////////////////////////////////////////////////////////////////
+const static arm_cfft_instance_f32 *S;
 Ticker samplingTimer;
 float samples[FFT_SIZE*2];
 float magnitudes[FFT_SIZE];
@@ -316,13 +315,44 @@
     // Begin sampling audio
     samplingBegin();
 
+    // Init arm_ccft_32
+    switch (FFT_SIZE)
+    {
+    case 16:
+        S = & arm_cfft_sR_f32_len16;
+        break;
+    case 32:
+        S = & arm_cfft_sR_f32_len32;
+        break;
+    case 64:
+        S = & arm_cfft_sR_f32_len64;
+        break;
+    case 128:
+        S = & arm_cfft_sR_f32_len128;
+        break;
+    case 256:
+        S = & arm_cfft_sR_f32_len256;
+        break;
+    case 512:
+        S = & arm_cfft_sR_f32_len512;
+        break;
+    case 1024:
+        S = & arm_cfft_sR_f32_len1024;
+        break;
+    case 2048:
+        S = & arm_cfft_sR_f32_len2048;
+        break;
+    case 4096:
+        S = & arm_cfft_sR_f32_len4096;
+        break;
+    }
+
     while(1) {
         // Calculate FFT if a full sample is available.
         if (samplingIsDone()) {
             // Run FFT on sample data.
-            arm_cfft_radix4_instance_f32 fft_inst;
-            arm_cfft_radix4_init_f32(&fft_inst, FFT_SIZE, 0, 1);
-            arm_cfft_radix4_f32(&fft_inst, samples);
+            // Run FFT on sample data.
+            arm_cfft_f32(S, samples, 0, 1);
             // Calculate magnitude of complex numbers output by the FFT.
             arm_cmplx_mag_f32(samples, magnitudes, FFT_SIZE);