/*
PennyRobot:
		control a robot (or other things) with a penny wistle
		by Chris Merck
		GPL
		this is a modification of String by Sam Tannous
		
		tested on: gcc/Linux (Kubuntu Edgy)
		
		
		depends: fftw (version 2)
		
		
		usage:
		
		this executable's output is sent to the serial port
		where a robot is listening for instructions.
		
		If the serial port is set up correctly with stty,
		 you can just say ./penny > /dev/ttyS0
		
		However, I couldn't get that to work (I think stty
		 haddent turned off flow control properly) so I ran
		 minicom (the terminal app) and set ./penny as the
		 "script program" and then hit the run a script command
		 in minicom, entered some bogus stuff for UN/PASS/NAME
		 and penny was linked to the serial port.
*/
	
//heading from string:
/* this is the non-GTK version of gstring
 * String - Yet another Guitar Tuning Application
 * Copyright (C) 1998-1999 Sam Tannous
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * You should have received a copy of the GNU General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */
  
#include <stdio.h>
#include <math.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <sys/ioctl.h>
#include <sys/soundcard.h>

// in version 2.0 of FFTW, the following line was rfftw.h
//    this changed in version 2.1 because of the new RPM 
//    I was using.
#include <rfftw.h>

static int    cross=0, max1 = 0, max2 = 0;

int fd;

// Your frequecy resolution (smallest frequecy difference we can
//    detect), is simply   f = sampling rate/number of samples
// N is the number of samples we take each time we sample.
//    increasing this value will improve your freq resolution.
//    but if you make it too large, it takes longer to get each
//    sample.
//#define N 16384
#define N 3000

//  sampleing_rate is the sampling frequecy of the microphone...in Hz
//  lowering this value will lower f and improve your resolution.
//  But it will also limit the highest frequency you can detect...
//  ...remember the Nyquist rate:  you must sample at twice the
//  highest frequency in order to prevent aliasing...
int sampling_frequency = 44100; //22050;

//int arg = 0x0002000a;
int channels = 0;   // mono=0, stereo = 1

int M = N/2;
int N2 = N*N;
 
// this is the default 8 bit format
// you must use unsigned char for the data read (in[N])
int format = AFMT_S8;  

// this is for 16 bit samples.  here, you must use
// signed short
//int format = AFMT_S16_LE;  
//signed short in[N];

int i;
int a440 = 440;

// f is the frequency resolution in Hz.  It is commented and calculated
//  below.  mycosine is just an array of cosine values used to improve
//  performance.
float f, mycosine[N];

// delta is how far off we can be from a given note (changes with octaves)
//   before we say it is sharp or flat..
float delta;


unsigned char in[N];
fftw_real freq,maxi,max, temp, in2[N], out[N];
rfftw_plan p;


  char mode = 'm'; //modes are i, j, k, l, m and mean fwd, rev, left, right, and stop

// this routing actually gets data from /dev/dsp...
void get_data(void)
{
  read(fd,in,N);
  for(i=0;i<N;i++)  {

    /* for 8 bit uncomment the following line */
    in2[i]=((float)in[i]-128.0)*(.54 - .46 * mycosine[i]);

    /* for 16 bit, uncomment the following line */
    //    in2[i]=((float)in[i])*(.54 - .46 * cos(2.0 * M_PI * (float)i/(N-1)));

  }
  //  printf("get_data: \n");

  //  p = rfftw_create_plan(N, FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE);
  rfftw_one(p, in2, out);
  //  rfftw_destroy_plan(p);
  
  //  power_spectrum[0] = out[0]*out[0];  /* DC component */
  // for (k = 1; k < N/2; ++k)  /* (k < N/2 rounded up) */
  //    power_spectrum[k] = out[k]*out[k] + out[N-k]*out[N-k];
  // if (N % 2 == 0) /* N is even */
  //  power_spectrum[N/2] = out[N/2]*out[N/2];  /* Nyquist freq. */

  maxi = 0;
  max = 0;
  max1 = 0;
  max2 = 0;
  cross = 0;
  
  for (i = 2; i < N; i++ ) { //for (i = 2; i < N/16; i++ ) {
    temp = out[i]*out[i] + out[N-i]*out[N-i];//power_spectrum[i];
    if (temp > max) {
      max = temp;
      maxi = i;// * f;
    }
  }
  return;
}

void string_loop(void)
{
  int caps = 0;

  char note = 'm';

  get_data();

  if (maxi >= 110)
	note = 'm';
  else if (maxi >= 102)
	note = '0';
  else if (maxi >= 95)
	  note = '3';
  else if (maxi >= 85)
	  note = 'o';
  else if (maxi >= 74)
	  note = 'u';
  else if (maxi >= 68)
	  note = 'l';
  else if (maxi >= 63)
	  note = 'j';
  else if (maxi >= 56)
	  note = 'k';
  else if (maxi >= 50)
	  note = 'i';
  
  if ((max <= 10000000000 && (mode != 'm')) || (note == 'm' && mode != 'm'))
  {
	  printf("m"); fflush(stdout); //fflush() is needed to force display of the charicter without a newline
	  mode = 'm';
  } else if (max > 10000000000 && (mode != note)) {
    printf("%c", note); fflush(stdout);
    mode = note;
  }

  return;
}

int main(int argc, char *argv[])
{

  // save these values so we don't keep recalculating them for each sample
  // if an A440 does not really give you 440 Hz, then you probably
  // need to change the following line a little.  Run the program
  // and use an A440 Hz tuning fork.  The program will tell you what
  // value 'i' is on the line.  f = 440.0/i;
  f = (float) sampling_frequency/N;

  // we store an array of cosine values to improve preformance.
  for(i=0;i<N;i++)  {
    mycosine[i]=cos(2.0 * M_PI * (float)i/N);
  }

  fd = open("/dev/dsp",O_RDONLY);

  p = rfftw_create_plan(N, FFTW_REAL_TO_COMPLEX, FFTW_ESTIMATE);

  if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels)==-1) {
    perror("bad channels setting.");
    exit(2); 
  } 

  if (ioctl(fd, SNDCTL_DSP_SETFMT, &format)==-1) {
    perror("bad sound format.");
    exit(2); 
  } 

  if (ioctl(fd, SNDCTL_DSP_SPEED, &sampling_frequency) == -1) {
    perror("bad sampling rate.");
    exit(2); 
  }


  /*
  if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &arg)) perror("Frag!");
  ioctl(fd, SNDCTL_DSP_SPEED, &sampling_frequency);
  */
  //  printf("main: \n");
  while (1) {
    //    printf("main: loop\n");
    string_loop();
  }
  return 0;
}

