/*********************************************************************************************************************************************
Nitin William Arduino Audio FFT with OLED
Adapted from learnelectronics:https://www.youtube.com/watch?v=5RmQJtE61zE
Aadapted from cbm80amiga:  https://www.youtube.com/watch?v=EnvhEgjrHsw
https://github.com/GadgetReboot/Arduino/blob/master/Uno/Spectrum_Analyzer/Spectrum_Analyzer.ino
https://sites.google.com/site/myscratchbooks/home/projects/proejct-20-oled-spectrum-analizer-using-max9812-microphone-amplifier
**********************************************************************************************************************************************/
//Libraries
#include <Wire.h>                //IDE Standard
#include <Adafruit_GFX.h>        //Adafruit GFX https://github.com/adafruit/Adafruit-GFX-Library
#include <Adafruit_SSD1306.h>    //Adafruit SSD1306 https://github.com/adafruit/Adafruit_SSD1306
#include <fix_fft.h>             //https://www.arduinolibraries.info/libraries/fix_fft

const byte audioIn = A0;         //Define analog pin for FFT
char re[128], im[128];           //Real and Imaginary FFT result arrays
byte yaxis = 63;                 //OLED y-axis drawing boundary limit
byte xaxis = 0;                  //OLED x-axis limit


Adafruit_SSD1306 display = Adafruit_SSD1306(128, 64, &Wire,-1);


void setup() 
{
  Wire.begin();                                           //I2C Master
  pinMode(audioIn,INPUT);                                 //Set A0 as analog input pin
  analogReference(DEFAULT); 
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.setTextColor(WHITE);
  display.clearDisplay();                     
  display.display();
}

/*
The FFT real/imaginary data are stored in a char data type as a signed -128 to 127 number
This allows a waveform to swing centered around a 0 reference data point
The ADC returns data between 0-1023 so it is scaled to fit within a char by dividing by 4 and subtracting 128.
eg (0 / 4) - 128 = -128 and (1023 / 4) - 128 = 127
*/

void loop() 
{
  for (byte i = 0; i < 128; i++)                          //Read 128 analog input samples from ADC
  {
  int sample = analogRead(audioIn);
  re[i] = sample / 4 - 128;                               //Scale the samples to fit within a char variable
  im[i] = 0;                                              //There are no imaginary samples associated with the time domain so set to 0
  }
  fix_fft(re, im, 7, 0);                                  //Send the samples for FFT conversion, returning the real/imaginary results in the same arrays
  display_spectrum();
}
/* 
For a 16 MHz Arduino the ADC clock is set to 16 MHz/128 = 125 KHz. Each conversion in AVR takes 13 ADC clocks so 125 KHz /13 = 9615 Hz.
The data array will contain frequency bin data in locations 0..127 for samples up to the sampling frequency of approx. 9 KHz
Each frequency bin will represent a center frequency of approximately (9 KHz / 128 samples) = 70 Hz
Due to Nyquist sampling requirements, we can only consider sampled frequency data up to (sampling rate / 2) or (9 KHz / 2) = 4.5 KHz
Therefore we only acknowledge the first 64 frequency bins [0..63] = [0..4.5KHz]
*/

void display_spectrum()
{
  display.clearDisplay();  
  for (byte i = 1; i < 64; i++)
  {
    int dat = sqrt(re[i] * re[i] + im[i] * im[i]);            //Frequency magnitude is the square root of the sum of the squares of the real and imaginary parts of a vector
    if (dat >= 32 ) dat = 32;
    display.drawLine(i * 2, yaxis, i * 2, yaxis - dat, WHITE);// draw bars for each frequency bin from 70 Hz to 4.5 KHz
  }
  display.setTextSize(2);
  display.setCursor(16,8);
  display.print("SPECTRUM");     
  display.display(); 
}