
#include "SPI.h"
#include "Adafruit_GFX.h"
#include "Adafruit_ST7735.h"
#include "esp_dsp.h"

#define TFT_MOSI 10
#define TFT_SCLK 8
#define TFT_CS 21 // Chip select control pin
#define TFT_DC 6  // Data Command control pin
#define TFT_RST 7

Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);

#define VERBOSE false

#define N_SAMPLES 1024

int N = N_SAMPLES;

int pointer = 0;
__attribute__((aligned(16))) float values[N_SAMPLES];

int notes[] = {4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902};
char letters[] = {'C', '#', 'D', '#', 'E', 'F', '#', 'G', '#', 'A', '#', 'B'};
float totals[12];
int int_totals[12];
uint16_t colors[12] = {0xfc14, 0xf800, 0xfc00, 0xff80, 0xcff0, 0x07e0, 0x0520, 0x06ba, 0x039a, 0x001f, 0xa45f, 0xd67f};

// Window coefficients
__attribute__((aligned(16))) float wind[N_SAMPLES];
// working complex array
__attribute__((aligned(16))) float y_cf[N_SAMPLES * 2];
// Pointers to result arrays
float *y1_cf = &y_cf[0];

uint32_t timer = micros();

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

  esp_err_t ret;

  ret = dsps_fft2r_init_fc32(NULL, CONFIG_DSP_MAX_FFT_SIZE);

  dsps_wind_hann_f32(wind, N);

  for (int n = 0; n < 12; n++)
  {
    int_totals[n] = 0;
  }

  tft.initR(INITR_BLACKTAB);
  tft.setRotation(2);
  tft.fillScreen(0x0000);
}

void fft(float *x)
{
  for (int i = 0; i < N; i++)
  {
    y_cf[i * 2 + 0] = x[i] * wind[i];
    y_cf[i * 2 + 1] = 0;
  }

  dsps_fft2r_fc32(y_cf, N);

  dsps_bit_rev_fc32(y_cf, N);

  // Convert one complex vector to two complex vectors
  dsps_cplx2reC_fc32(y_cf, N);

  if (VERBOSE)
  {
    dsps_view(y1_cf, N, 64, 10, 0, 10, '-');
  }
}

int argmax(float *input, int length)
{
  int max_i = -1;
  float max_val = 0;
  for (int i = 0; i < length; i++)
  {
    float val = abs(input[i]);
    if (i == 0 || val > max_val)
    {
      max_i = i;
      max_val = val;
    }
  }
  return max_i;
}

void loop()
{
  // put your main code here, to run repeatedly:
  // delay(1); // this speeds up the simulation
  delayMicroseconds(10);

  float val = (float)(analogRead(2) - 2048) / (2048.0);
  values[pointer] = (float)val;
  pointer = (pointer + 1) % N;

  if (pointer == 0)
  {
    uint32_t T = micros() - timer;

    // int Fs = N*1000000/T;
    fft(values);
    int i = argmax(y1_cf, N / 2);
    Serial.println((float)i * 500000.0 / (float)T);
    float freq = (float)i * 500000.0 / (float)T;

    int octave = 8;

    for (int expn = 0; expn < 8; expn++)
    {
      if (freq > 4000)
      {
        break;
      }
      freq *= 2;
      octave -= 1;
    }

    for (int n = 0; n < 12; n++)
    {

      if (freq < notes[n])
      {
        if (n != 0 && freq - notes[n - 1] < notes[n] - freq)
        {
          n = n - 1;
        }

        if (letters[n] == '#')
        {
          Serial.print(letters[n - 1]);
        }
        Serial.print(letters[n]);
        Serial.println(octave);
        break;
      }
    }

    if (true)
    {
      float ratio = ((float)T / 500000.0);
      for (int n = 0; n < 12; n++)
      {
        int old_total = int_totals[n];
        totals[n] = 0;
        uint32_t start = notes[n] >> 8;
        // ignore octave 0 and 1 since that might be noise.
        for (uint32_t expn = 2; expn <= 8; expn++)
        {
          // in octave 1, notes are spaced about 2 apart so +-1 is about the separation.
          int start_i = (((start << 1) - 1) << (expn - 1)) * ratio;
          int end_i = ((((start << 1) + 1) << (expn - 1))) * ratio;
          for (int i = start_i; i < end_i; i++)
          {

            totals[n] += fabs(y1_cf[i]);
          }
        }

        int_totals[n] = ((int)totals[n]) >> 3;

        if (old_total > int_totals[n])
        {

          tft.fillRect(int_totals[n], 6 + 13 * n, old_total - int_totals[n], 3, 0x0000);
        }
        else
        {
          tft.fillRect(old_total, 6 + 13 * n, int_totals[n] - old_total, 3, colors[n]);
        }
      }
    }

    timer = micros();
  }
}