Just a little hacked together example of AudioAnalyzeFFT1024, using the Adafruit HX8357D 480x320 display.
the grid is 23 x 16 pixels, and as the FFT bins are spaced approx 43Hz apart, each vertical div is 1kHz, and the
vertical scale is set to 5dB/div.
Source is an electret mic soldered to Audio shield rev D, hiding underneath by stacking header breakout under
the Teensy 4.0 (@ 150MHz)
The function at the end, display_fft(), does the work of pulling the FFT values and plotting them.
Code:
#include <Audio.h>
#include <SPI.h>
#include "Adafruit_GFX.h"
#include "Adafruit_HX8357.h"
// Audio setup using audio shield ver D (which uses SPI pins 10-13 and also 6-8, 15, 18-21, 23
AudioAnalyzeFFT1024 fft ;
AudioInputI2S i2s1 ;
AudioOutputI2S i2s2 ;
AudioControlSGTL5000 sgtl5000 ;
AudioConnection patchcord1 (i2s1, 0, fft, 0) ;
// Display pin assignment:
// The audio shield uses pin 10 as SPI CS for uSD, so we use CS = 14 for display SPI
// 16 for display reset
#define TFT_DC 9
#define TFT_CS 14
#define TFT_RST 16
// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_HX8357 tft = Adafruit_HX8357 (TFT_CS, TFT_DC, TFT_RST);
void setup()
{
tft.begin () ;
tft.setRotation(3); // orient as landscape, 1 or 3 depending on which way up
draw_grid (true) ;
start_audio () ;
}
void loop(void)
{
if (fft.available())
display_fft() ;
}
void start_audio ()
{
AudioMemory (12) ;
fft.windowFunction (AudioWindowHanning1024) ;
sgtl5000.enable ();
sgtl5000.inputSelect (AUDIO_INPUT_MIC); // microphone needed on Audio shield
sgtl5000.micGain (55); // microphone gain 0 .. 63
sgtl5000.volume (0.5); // for headphone socket
}
// drawing graph
#define PIXELS_PER_KHZ 23 // approximately, 1000 / (44100 / 1024)
#define PIXELS_PER_DIV 16 // pixels per vertical division
#define gw (20 * PIXELS_PER_KHZ) // graph width
#define gh 19 * PIXELS_PER_DIV // graph height
#define DB_PER_DIV 5
uint16_t text_colour = tft.color565 (128, 255, 128) ;
uint16_t grid_colour = tft.color565 (128, 48, 0) ;
uint16_t grid2_colour = tft.color565 (192, 100, 0) ;
// draw graticule for display of FFT
void draw_grid (bool clear)
{
int w = tft.width();
int h = tft.height();
Serial.print (w) ; Serial.print (", ") ; Serial.println (h) ;
if (clear)
tft.fillScreen(HX8357_BLACK);
tft.setTextColor (text_colour);
tft.setTextSize (1);
int kHz = 0 ;
for (int i = 0 ; i <= gw ; i += PIXELS_PER_KHZ)
{
if ((i/PIXELS_PER_KHZ % 5) == 0)
{
tft.drawLine (i, 0, i, gh+7, grid2_colour) ;
tft.setCursor (i-2, gh+7) ;
tft.printf ("%i", kHz) ;
}
else
tft.drawLine (i, 0, i, gh, grid_colour) ;
kHz += 1 ;
}
int dB = 0 ;
for (int i = 0 ; i <= gh ; i += PIXELS_PER_DIV)
{
tft.drawLine (0, i, gw, i, grid_colour) ;
tft.setCursor(gw-14, i-2);
tft.printf("%4i", dB);
dB -= DB_PER_DIV ;
}
}
// redraw a 16 bit wide column of the graticule
void draw_part_grid (int x)
{
int h = tft.height();
for (int i = x ; i < x+16 ; i ++)
if ((i % PIXELS_PER_KHZ) == 0)
tft.drawLine (i, 0, i, gh, (i/PIXELS_PER_KHZ % 5) == 0 ? grid2_colour : grid_colour) ;
for (int i = 0 ; i <= gh ; i += PIXELS_PER_DIV)
tft.drawLine (x, i, x+15, i, grid_colour) ;
}
#define MIN_DB (-85) // clip dB values to this for 0 samples.
// convert FFT amplitude values to dB scale, 0dB = full scale
int dB_mag (float ampl)
{
ampl *= 1.41421 ; // convert to rms.
int res = ampl == 0.0 ?
round (MIN_DB / DB_PER_DIV * 16) :
round (16 * log10 (ampl) * 20 / DB_PER_DIV) ; // 16 pixels / div vertical scale
return constrain (res, -gh, 0) ;
}
void display_fft()
{
static int prev_trace[gw+1] ; // record previous trace to erase it next time round
int i = 1 ;
tft.drawLine (i-1, prev_trace[i-1], i, prev_trace[i], HX8357_BLACK) ;
int pp = 0 - dB_mag (fft.read (0)) ;
prev_trace[0] = pp ;
while (i <= gw-16)
{
// redraw in columns, 16 pixels wide, for flicker-free refresh
// erase last plot
for (int j = 1 ; j < 17 ; j ++)
tft.drawLine (i+j-1, prev_trace[i+j-1], i+j, prev_trace[i+j], HX8357_BLACK) ;
// redraw grid for this column
draw_part_grid (i-1);
// plot new fft data
for (int j = 0 ; j < 16 ; j ++)
{
int p = 0 - dB_mag (fft.read (i+j)) ;
prev_trace[i+j] = p ;
tft.drawLine (i+j-1, pp, i+j, p, HX8357_GREEN) ;
pp = p ;
}
i += 16 ;
}
}