/*
 * parrot.c
 * Program to illustrate /dev/dsp device
 * Records several seconds of sound, then echoes it back.
 * Runs until Control-C is pressed.
 */

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/soundcard.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <fftw3.h>
#include <string.h>

// Files must be 44100, 16bit wav files

#define LENGTH 1    /* how many seconds of speech to store */
#define CUTOFF 441   /* how many seconds of speech to store */
#define RATE 44100   /* the sampling rate */
#define SIZE 16      /* sample size: 8 or 16 bits */
#define CHANNELS 1  /* 1 = mono 2 = stereo */



//#define RT_SAMPLE_LENGTH 1000
#define RT_SAMPLE_LENGTH (LENGTH*RATE*SIZE*CHANNELS/(8*CUTOFF))
#define NHALF RT_SAMPLE_LENGTH/2

#define ERR_THRESHOLD 10000  // Min power freq to accept a freq as local maximum
#define FREQ_MAXDEV 0.20	//Percentage of freq desviaion to be recognized

#define FREQ_ZERO 2000
#define FREQ_ZERO_MIN FREQ_ZERO-(FREQ_ZERO*FREQ_MAXDEV)
#define FREQ_ZERO_MAX FREQ_ZERO+(FREQ_ZERO*FREQ_MAXDEV)


#define FREQ_ONE 5000
#define FREQ_ONE_MIN FREQ_ONE-(FREQ_ONE*FREQ_MAXDEV)
#define FREQ_ONE_MAX FREQ_ONE+(FREQ_ONE*FREQ_MAXDEV)

#define FREQ_SEPARATION 7000
#define FREQ_SEPARATION_MIN FREQ_SEPARATION-(FREQ_SEPARATION*FREQ_MAXDEV)
#define FREQ_SEPARATION_MAX FREQ_SEPARATION+(FREQ_SEPARATION*FREQ_MAXDEV)

#define FREQ_NEWFILE 12000
#define FREQ_NEWFILE_MIN FREQ_NEWFILE-(FREQ_NEWFILE*FREQ_MAXDEV)
#define FREQ_NEWFILE_MAX FREQ_NEWFILE+(FREQ_NEWFILE*FREQ_MAXDEV)

#define ORIG_SAMPLERATE 44100
#define BANDWIDTH ORIG_SAMPLERATE/2
#define SCALE_PONDERATION (double)ORIG_SAMPLERATE/ (double)RT_SAMPLE_LENGTH

#define MIN_COUNT_ACCEPT 2

/* this buffer holds the digitized audio */
unsigned char buf[LENGTH*RATE*SIZE*CHANNELS/(8*CUTOFF)];

int main(int argc, char *argv[])
{
  	int fd;       /* sound device file descriptor */
  	int arg;      /* argument for ioctl calls */
	int status;   /* return status of system calls */
	int v,m,i;
	double maxval,maxpos;
	double curpos,absval;
	double last_freq=0;
	int count_repeat=MIN_COUNT_ACCEPT;
	int in_value,in_value_2b;
	static double *in_data = NULL, *out_data = NULL;
	static fftw_plan plan;
	char tmp[50];
	char aux_num[50];
	int position=0;
	int final_value;
	//in_data input real data from wavfile
	in_data = (double *)fftw_malloc(RT_SAMPLE_LENGTH * sizeof(double));
	//out_data where the output will reside
	out_data = (double *)fftw_malloc(RT_SAMPLE_LENGTH * sizeof(double));
	// r to r fft 
	plan = fftw_plan_r2r_1d(RT_SAMPLE_LENGTH, in_data, out_data, FFTW_R2HC, FFTW_FORWARD); 
	
	//Vector that will hold the final binary number
	position =0;
	for (i = 0; i < 50 ; i++) tmp[i]=0;	
	int stop=0;
	long double power_org=0;
	long double power_fft=0;
	int newtone=1;
	
	int file_out;
	file_out = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC,00666);
  /* open sound device */
 	fd = open("/dev/dsp", O_RDONLY);
  	if (fd < 0) {
    		perror("open of /dev/dsp failed");
    		exit(1);
  	}

  	/* set sampling parameters */
  	arg = SIZE;      /* sample size */
	status = ioctl(fd, SOUND_PCM_WRITE_BITS, &arg);
  	if (status == -1)
    	perror("SOUND_PCM_WRITE_BITS ioctl failed");
  	if (arg != SIZE)
   		perror("unable to set sample size");

  	arg = CHANNELS;  /* mono or stereo */
  	status = ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &arg);
  	if (status == -1)
    		perror("SOUND_PCM_WRITE_CHANNELS ioctl failed");
  	if (arg != CHANNELS)
    		perror("unable to set number of channels");

  	arg = RATE;      /* sampling rate */
  	status = ioctl(fd, SOUND_PCM_WRITE_RATE, &arg);
  	if (status == -1)
    		perror("SOUND_PCM_WRITE_WRITE ioctl failed");
  	
	int point=0;
	while (1) { /* loop until Control-C */
		status = read(fd, buf, sizeof(buf)); /* record some sound */
    		if (status != sizeof(buf))
      			perror("read wrong number of bytes");
		point=0;
		for (v=0;v<RT_SAMPLE_LENGTH;v++) {
			in_value = (int)buf[point];
			point++;
			in_value_2b = (int)buf[point];
			point++;
			if (in_value_2b > 127) { 
	//			printf ("invalue_2b:%d, neu:%d\n",in_value_2b,in_value_2b - 255);
				in_value_2b = (int)in_value_2b - 255; 
			}
			int vo=256*in_value_2b +in_value;
			in_data[v] =(double)( 256*(int)in_value_2b  + (int)in_value);
	//		printf ("v: %d %g %d\n",256*in_value_2b  + in_value,in_data[v],vo);
	//		printf ("in: %d",in_data[v]);
			//power_org=power_org+pow(in_data[v],2);
		}
		//printf ("POWER ORIG: %d \n",power_org);
		fftw_execute(plan);
	
		m = 0;
		maxval=0;
		maxpos=0;
		for (i = 0; i < (RT_SAMPLE_LENGTH-2); i++) {
			//absval = sqrt(pow(in_data[i],2) + pow(out_data[i],2));
			//absval = sqrt(pow(out_data[i],2) + pow(out_data[i],2));
			absval = sqrt(out_data[i] * out_data[i]);
			//power_fft = power_fft + pow ( absval, 2);
			curpos = (double)m * SCALE_PONDERATION;
			if (absval>maxval && curpos < BANDWIDTH){ 
				maxval=absval;
				maxpos=curpos;
			}
			m++;
		}

		//printf ("POWERFFT: %d \n",maxval);
		if (maxval>ERR_THRESHOLD) { 
			//printf ("Maximum at freq:%f, %f\n",maxpos,maxval);
			if (newtone==0) {
				if ( maxpos > FREQ_ONE_MIN && maxpos < FREQ_ONE_MAX ) {
					if (last_freq != FREQ_ONE ) {
						count_repeat--;
						if (count_repeat == 0 ) {
							count_repeat = MIN_COUNT_ACCEPT;
							last_freq = FREQ_ONE;
							tmp[position]=1;
							position++;
		//					printf ("ONE\n");
						}
						
		
					}
					
				}
				if ( maxpos > FREQ_ZERO_MIN && maxpos < FREQ_ZERO_MAX ) {
					if (last_freq != FREQ_ZERO ) {
						count_repeat--;
       	                                 	if (count_repeat == 0 ) {
	                                                count_repeat = MIN_COUNT_ACCEPT;
							last_freq = FREQ_ZERO;
							//printf ("ZERO\n");
							tmp[position]=0;
							position++;
						}
					}
				}
				if ( maxpos > FREQ_SEPARATION_MIN && maxpos < FREQ_SEPARATION_MAX ) {
					if (last_freq != FREQ_SEPARATION ){
						count_repeat--;
	                                        if (count_repeat == 0 ) {
        	                                        count_repeat = MIN_COUNT_ACCEPT;
							last_freq = FREQ_SEPARATION;
							//printf ("SEPARATION\n");
						}
					}
				}
	
			}	
			if ( maxpos > FREQ_NEWFILE_MIN && maxpos < FREQ_NEWFILE_MAX ) {
				if (last_freq != FREQ_NEWFILE ){
					count_repeat--;
	                                if (count_repeat == 0 ) {
						//Final nou fitxer
						if (newtone==0) {
							stop=1;
							newtone=1;
						}
	                                        count_repeat = MIN_COUNT_ACCEPT;
						last_freq = FREQ_NEWFILE;
						newtone--;
						printf ("NEWFILE\n");
					}
				}
			}
			if (stop==1) {
				final_value=0;
				for (i=0;i<position;i++){ 
					final_value = final_value + tmp[i]*(1<<(position-i-1)); 	
				}
				position=0;
				printf ("Final: %d\n",final_value);
				sprintf(aux_num,"%d\n",final_value);
				write (file_out,aux_num,strlen(aux_num)*sizeof(char));
				stop=0;
			}

		
		}
    	if (status != sizeof(buf))
	      	perror("wrote wrong number of bytes");
    /* wait for playback to complete before recording again */
    	status = ioctl(fd, SOUND_PCM_SYNC, 0); 
  	if (status == -1)
    		perror("SOUND_PCM_SYNC ioctl failed");
  	}
}
