Audio spectrum analysis all in Software

I want to analyse the spectrum of an audio input with FFT.

First of all, I want to done everything with software. That's to say, there won't be any spectrum analyzer IC or signal input through Hardware.

What I need to know is, how to get the audio signal as input while I am also playing the sound on computer. So, what I want to achieve is simply playing a song on PC and get this as input to arduino software to be able to process it simultaneously and send this data to analog outputs to be able to visualize them through LEDs.

1- How can I get the audio signal ?
2- Is there any "straight forward" FFT code to use easily. Asking cause I am not good at signal processing. ( Using an IC instead is not an option for me, since me to get one takes weeks due to shipping)

Thanks in advance.

Are you trying to feed the audio output via sound waves or via an electrical signal? Either way you'll probably need some form of signal conditioning to actually make them usable. There is no physical way to just feed sound into the arduino without a Mic and the electrical signal is probably 1) too small and 2) Bi-polar making it hard to do anything with it.

Otherwise, if all your Arduino is doing, you can probably do a DFT. Look up online for a DFT (Digital Fourier Transform) algorithm. It shouldn't be too hard.

Lots of useful stuff here: ArduinoFFT - Open Music Labs Wiki

If you're looking to do this on an ATmega (Uno) class device, you'll most likely be disappointed with the results. FFT is pretty heavy math for a 16MHz IC with no floating point support. Also, the ADCs aren't really fast enough for "CD-quality" audio sampling either. That may or may not matter to you, depending on what bandwidth you care to analyze.

Bipolar electrical signals aren't really a problem. You can just provide a 1/2 Vcc ground reference and AC-couple the audio input (as you should anyway).

So that's where you're headed with this project. If you still want to do this, you'll either have to be content with low temporal resolution telephone-grade audio bandwidth analysis, or use something ARM-based and with a suitably fast ADC.

What are you doing with the FFT? Are you sure you don't need the power spectrum rather than the spectrum of the signal itself?

I run this on atmega1284, but it will run on the UNO…
Pleased reference:

For library files…

/* FFT_TEST4
 Ray Burnette 20130810 function clean-up & 1284 port (328 verified)
 Uses 2x16 Parallel LCD in 4-bit mode, see LiquidCrystal lib call for details
 http://forum.arduino.cc/index.php?PHPSESSID=4karr49jlndufvtlqs9pdd4g96&topic=38153.15
 Modified by varind in 2013: this code is public domain, enjoy!
 http://www.variableindustries.com/audio-spectrum-analyzer/
 328P = Binary sketch size: 5,708 bytes (of a 32,256 byte maximum)
 1284P= Binary sketch size: 5,792 bytes (of a 130,048 byte maximum) Free RAM = 15456
        Binary sketch size: 8,088 bytes (of a 130,048 byte maximum) (Debug)
 */

#include <LiquidCrystal.h>
#include "fix_fft.h"  // fix_fft.ccp & fix_fft.h in same directory as sketch

#define DEBUG 0
#define LCHAN 1
#define RCHAN 0

const int Yres = 8;
const int gain = 3;
float peaks[64];
char im[64], data[64];
char Rim[64], Rdata[64];
char data_avgs[64];
int debugLoop;

//LiquidCrystal(rs, enable, d4, d5, d6, d7) 1284P Physical: 6,  5, 4, 3, 2, 1
LiquidCrystal lcd(11, 10, 7, 6, 5, 4); // saves all analog pins port PA

// Custom CHARACTERS
byte v1[8] = {
  B00000,B00000,B00000,B00000,B00000,B00000,B00000,B11111};
byte v2[8] = {
  B00000,B00000,B00000,B00000,B00000,B00000,B11111,B11111};
byte v3[8] = {
  B00000,B00000,B00000,B00000,B00000,B11111,B11111,B11111};
byte v4[8] = {
  B00000,B00000,B00000,B00000,B11111,B11111,B11111,B11111};
byte v5[8] = {
  B00000,B00000,B00000,B11111,B11111,B11111,B11111,B11111};
byte v6[8] = {
  B00000,B00000,B11111,B11111,B11111,B11111,B11111,B11111};
byte v7[8] = {
  B00000,B11111,B11111,B11111,B11111,B11111,B11111,B11111};
byte v8[8] = {
  B11111,B11111,B11111,B11111,B11111,B11111,B11111,B11111};


void setup() {

  if (DEBUG) {
    Serial.begin(9600); // hardware serial
    Serial.print("Debug ON");
    Serial.println("");
  }

  lcd.begin(16, 2);
  lcd.clear();
  lcd.createChar(1, v1);
  lcd.createChar(2, v2);
  lcd.createChar(3, v3);
  lcd.createChar(4, v4);
  lcd.createChar(5, v5);
  lcd.createChar(6, v6);
  lcd.createChar(7, v7);
  lcd.createChar(8, v8);
}

void loop() {

  for (int i = 0; i < 64; i++) {    // 64 bins = 32 bins of usable spectrum data
    data[i]  = ((analogRead(LCHAN) / 4 ) - 128);  // chose how to interpret the data from analog in                                      
    im[i]  = 0;   // imaginary component
    Rdata[i] = ((analogRead(RCHAN) / 4 ) - 128);  // chose how to interpret the data from analog in                                      
    Rim[i] = 0;   // imaginary component
  }

  fix_fft(data, im, 6, 0);   // Send Left channel normalized analog values through fft
  fix_fft(Rdata, Rim, 6, 0); // Send Right channel normalized analog values through fft

  // At this stage, we have two arrays of [0-31] frequency bins deep [32-63] duplicate

  // calculate the absolute values of bins in the array - only want positive values
  for (int i = 0; i < 32; i++) {
    data[i] = sqrt(data[i]  *  data[i] +  im[i] *  im[i]);
    Rdata[i] = sqrt(Rdata[i] * Rdata[i] + Rim[i] * Rim[i]);

    // COPY the Right low-band (0-15) into the Left high-band (16-31) for display ease
    if (i < 16) {
      data_avgs[i] = data[i];
    } 
    else {
      data_avgs[i] = Rdata[i - 16];
    }

    // Remap values to physical display constraints... that is, 8 display custom character indexes + "_"
    data_avgs[i] = constrain(data_avgs[i], 0, 9 - gain);     //data samples * range (0-9) = 9
    data_avgs[i] = map(data_avgs[i], 0, 9 - gain, 0, Yres);  // remap averaged values
  }

  Two16_LCD();
  decay(1);
}


void Two16_LCD(){
  lcd.setCursor(0, 0); 
  lcd.print("L"); // Channel ID replaces bin #0 due to hum & noise
  lcd.setCursor(0, 1); 
  lcd.print("R"); // ditto

  for (int x = 1; x < 16; x++) {  // init 0 to show lowest band overloaded with hum
    int y = x + 16; // second display line
    if (data_avgs[x] > peaks[x]) peaks[x] = data_avgs[x];
    if (data_avgs[y] > peaks[y]) peaks[y] = data_avgs[y];

    lcd.setCursor(x, 0); // draw first (top) row Left
    if (peaks[x] == 0) {
      lcd.print("_");  // less LCD artifacts than " "
    }
    else {
      lcd.write(peaks[x]);
    }

    lcd.setCursor(x, 1); // draw second (bottom) row Right
    if (peaks[y] == 0){
      lcd.print("_");
    }
    else {
      lcd.write(peaks[y]);
    }
  }
  
  debugLoop++;
  if (DEBUG && (debugLoop > 99)) {
    Serial.print( "Free RAM = " );
    Serial.println( freeRam(), DEC);
    Serial.println( millis(), DEC);
    debugLoop = 0;
  } 
}


int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}


void decay(int decayrate){
  int DecayTest = 1;
  // reduce the values of the last peaks by 1 
  if (DecayTest == decayrate){
    for (int x = 0; x < 32; x++) {
      peaks[x] = peaks[x] - 1;  // subtract 1 from each column peaks
      DecayTest = 0;
    }
  }

  DecayTest++;
}