Audio Visualizer using ArduinoFFT Library

Hello everyone,

I'm having trouble creating an audio visualizer using the downloadable arduinoFFT.h (Version 2.0.4). My two concerns are the library itself and if my microphone that's connected to a preamplifier circuit is actually producing a usable signal.

Microphone:
The model is the Cylindrical Electret Condenser Microphone Pickup (Amazon). When I read its analog signal it's consistently around the 340-370 (1.7V) range, no matter if there is audio playing or not. Is that enough voltage? If not, is there a pre-amped microphone I can purchase instead?

FFT:
I would just like to check if the code posted below is correct. Please ignore the commented code as they are for my display. Please let me know if you need more information and thank you in advanced.

#include <Adafruit_GFX.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#include "arduinoFFT.h"
#ifndef PSTR
#define PSTR 
#endif

#define PIN 6
#define MATRIX_WIDTH 32
#define MATRIX_HEIGHT 8


//Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(32, 8, PIN,
  //NEO_MATRIX_TOP     + NEO_MATRIX_RIGHT +
  //NEO_MATRIX_COLUMNS + NEO_MATRIX_ZIGZAG,
  //NEO_GRB            + NEO_KHZ800); // Width = 32, Height = 8

const int samples = 64;
const double samplingFrequency = 8000.0; //The Nyquist-Shannon sampling theorem states that your sampling frequency must be at least twice the highest frequency present in your signal
const int microphonePin = A4;
int sensorValue;
double peakFrequency;
int height;
int counter = 0;

double realComponent[samples];
double imagComponent[samples];
ArduinoFFT<double> FFT = ArduinoFFT<double>(realComponent, imagComponent, samples, samplingFrequency);
//double fftBins[samples/2];

void setup() 
{
  //matrix.begin();
  //matrix.setTextWrap(false);
  //matrix.setBrightness(20);
  Serial.begin(9600);
}
void loop() 
{
  //matrix.fillScreen(0); 
  for (int i = 0; i < samples;i++)
  {
    realComponent[i] = analogRead (microphonePin);
    imagComponent[i] = 0.0;
    //Serial.println(realComponent[i]);
  }
  FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward);
  FFT.compute(FFTDirection::Forward);
  FFT.complexToMagnitude();

Yes, we need a link to the actual microphone you are using! We need to know what you need for a "usable signal".

1 Like

Below is the link. I thought it was the Arduino itself that needed a certain signal strength for it to process the microphone data.

1 Like

An Arduino cannot interface to that microphone which requires a bias voltage and a transistor amplifier to produce any output. Use Google to find circuits using electret microphones.

This microphone comes with a built-in amplifier, produces a DC bias voltage, and is supposedly designed to directly plug into an Arduino.
Amazon.com: HiLetgo 2pcs Electret Microphone Amplifier MAX4466 Module Adjustable Gain Blue Breakout Board for Arduino : Electronics)

Yes, that unit would work for your project.

Great! Does the code look correct?

Then why did you post a link to something else?

Perhaps you could show how you have connected it. And what to (ie what "arduino" are you using.

and how you are "reading" it.

The audio waveform will come out of the OUT pin. The output will have a DC bias of VCC/2 so when its perfectly quiet, the voltage will be a steady VCC/2 volts (it is DC coupled).

So you would expect a reading of around 1.7V if you are supplying it with 3.3V as reccommended.

I'd suggest you start with a simple sketch to read the adc and print the raw data.
and why

Serial.begin(9600);

are you really outputting to a teletype?

Not at all. For one thing there is no bracket at the end of loop.
You also don't sample the data at 8kHz.

I just discovered the second microphone today as I continue researching this project. To test my original microphone setup, I printed the raw data as you described and was able to get the range. I'm not sure what you mean about outputting to a teletype.

I had to cut out some of my code as it's for my matrix display. I chose 8kHz as half of that is 4kHz which is the peak frequency I want to measure.

See the FFT_03.ino example for sampling with the ADC.

However, funny thing about the FFT is that if the frequency you want to measure is right at half the sampling rate, it won't detect it.

So if I wanted to measure 4000 Hz, I should set the sampling Frequency to 8004Hz for example. Until my new microphone arrives, is there a way I can check if the FFT is producing the correct values? For example, can I just create an array of integers and compare my values with an FFT calculator or do I have to use a sine wave?

See FFT_01.ino example.

Maybe you should have a look at all the examples.

Using such a low baud rate is nonsense if you are using modern equipment.

And I say again - I'd suggest you start with a simple sketch to read the adc and print the raw data.
Unless you know that is OK youre wasting your time. Garbage in - garbage out.

You must use a low pass filter to make sure than no signal component with frequency higher than 4 kHz is in the input sample, or the result will be aliased, possibly making the result of the FFT calculation uninterpretable.

The other option is to use a sample frequency at least 2X that of any signal present in the input, and ignore output components with frequency > 4 kHz.

Got it thanks.

Makes sense, thx.

After plugging in multiple values using the code I've pasted below and comparing it with MathLab and an online FFT calculator, I still haven't been able to produce correct results.
Code:

#include "arduinoFFT.h"

const uint16_t samples = 8; //This value MUST ALWAYS be a power of 2
const double signalFrequency = 1000;
const double samplingFrequency = 5000;

double vReal[samples];
double vImag[samples];

ArduinoFFT<double> FFT = ArduinoFFT<double>(vReal, vImag, samples, samplingFrequency);
int num[samples] = {0, 3, 0, 3, 0, 3, 0, 3};


void setup() 
{
  // put your setup code here, to run once:
  Serial.begin(115200);
}

void loop() 
{
  // put your main code here, to run repeatedly:
  double ratio = twoPi * signalFrequency / samplingFrequency; // Fraction of a complete cycle stored at each sample (in radians)
  for (uint16_t i = 0; i < samples; i++)
  {
    vReal[i] = num[i];
    vImag[i] = 0.0; //Imaginary part must be zeroed in case of looping to avoid wrong calculations and overflows
  }
  FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward);
  FFT.compute(FFTDirection::Forward); 
  FFT.complexToMagnitude();
  for (int i = 0; i < (samples); i++)
  {
    Serial.println(vReal[i]);
  }
  Serial.println();
  double x = FFT.majorPeak();
  Serial.println(x, 7);
  delay (5000);
  exit(0);
}

Arduino Results


MathLab Code and Results

You are applying a Hamming window with arduinoFFT.

What does Matlab do?

Eliminating the windowing step produced this result on an Uno R3:
12.00
0.00
0.00
0.00
12.00
0.00
0.00
0.00

2500.0000000