#include <LiquidCrystal_I2C.h>
#include "fix_fft.h"

#define LCHAN 1

const int yres = 8;
const int gain = 3;
int i = 0, val;
int lmax[2];                  // level max memory
int dly[2];                   // delay & speed for peak return
char im[64], data[64];
char data_avgs[32];

// VU METER CHARACTERS
byte v1[8] = {0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F};
byte sp[8] = {0x03, 0x05, 0x19, 0x11, 0x11, 0x19, 0x05, 0x03};

LiquidCrystal_I2C lcd(0x27, 16, 2); //saves 5 pwm pins for servos, leds, etc

void setup()
{
  lcd.init();
  lcd.backlight();

  lcd.createChar(0, sp);
  lcd.createChar(1, v1);

  lcd.setCursor(2, 0);
  lcd.print("FM  99.9 MHz");
}

// the loop routine runs over and over again forever;
void loop()
{
  bars();
}

void vu()
{
  for (i = 0; i < 64; i++)
  {
    val = ((analogRead(LCHAN)) - 96);  // chose how to interpret the data from analog in
    data[i] = val;
    im[i] = 0;
  }

  fix_fft(data, im, 6, 0); // Send the data through fft

  // get the absolute value of the values in the array, so we're only dealing with positive numbers
  for (i = 0; i < 32 ; i++)
  {
    data[i] = sqrt(data[i] * data[i] + im[i] * im[i]);
  }

  // todo: average as many or as little dynamically based on yres
  for (i = 0; i < 32; i++)
  {
    data_avgs[i] = (data[i]);                               // add 3 samples to be averaged, use 4 when yres < 16
    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
  }
} // end loop

void bars()
{
  vu();

  int anL = map((data_avgs[18]), 0, 11, 0, 80); // sqrt to have non linear scale (better was log)
  bar(1, anL);
}

void  bar(int rows, int levs)
{
#define T_REFRESH    100              // msec bar refresh rate
#define T_PEAKHOLD   5 * T_REFRESH    // msec peak hold time before return

  lcd.setCursor(0, rows);
  lcd.write(0);
  lcd.setCursor(2, rows);

  for (int i = 1 ; i < 16 ; i++)
  {
    int f = constrain(levs       - i * 5, 0, 5);
    int p = constrain(lmax[rows] - i * 5, 0, 6);

    if (f)
      lcd.write(1);
    else
      lcd.write(' ');
  }
  if (levs > lmax[rows])
  {
    lmax[rows] = levs;
    dly[rows]  = -(T_PEAKHOLD) / T_REFRESH;              // Starting delay value. Negative=peak don't move
  }
  else
  {
    if (dly[rows] > 0)
      lmax[rows] -= dly[rows];

    if (lmax[rows] < 0)
      lmax[rows] = 0;
    else
      dly[rows]++;
  }
}