
#define _GNU_SOURCE

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stropts.h>
#include <linux/soundcard.h>
#include <sys/stat.h>
       #include <fcntl.h>
#include <math.h>
//int total=0;

#define mifprintf(...) ;

void * mihandle = NULL; 
int (*old_open)(const char *, int);
FILE * (*old_fopen)(const char *, const char *);
size_t (*old_fread)(void * ptr, size_t size, size_t nitems, FILE * stream);
size_t (*old_fwrite)(void * ptr, size_t size, size_t nitems, FILE * stream);
ssize_t (*old_write)(int, const void *, size_t);
ssize_t (*old_read)(int, const void *, size_t);
int (*old_ioctl) (int , unsigned long int , ...);
int (*old_close) (int);
int fddsp=-1;
FILE * filedsp=NULL;
int initlevel=-2;

int fctl=-1;

void *get_libc(){

#ifndef RTLD_NEXT
	 void *lib=0;
	  if(!lib){
		     lib= dlopen("/lib/libc.so.6",RTLD_LAZY);
		      }
	   if (NULL==lib) {
		      mifprintf(stderr, "Couldn't find libc at: %s\n", LIBCPATH);
		         abort();
			  }
	    return lib;
#else
	      return RTLD_NEXT;
#endif
}

char tocho[4096];
int it=0;

void inicializa() {
	int i;
 switch(initlevel) {
	 case -2:
	      initlevel=-1;
	      break;
	 case -1:
	      return;
	 case 0:
	      return;
 }

 
mihandle = get_libc();
 if (!mihandle) {
	fputs(dlerror(), stderr);
	exit(-1);
 }
 old_fopen = dlsym(mihandle, "fopen");
 if (dlerror()!=NULL) {
	fputs("ERROR", stderr);
	exit(-1);
 }
 old_open = dlsym(mihandle, "open");
 if (dlerror()!=NULL) {
	fputs("ERROR", stderr);
	exit(-1);
 }
 old_fread = dlsym(mihandle, "fread");
 if (dlerror()!=NULL) {
	fputs("ERROR", stderr);
	exit(-1);
 }
 old_fwrite = dlsym(mihandle, "fwrite");
 if (dlerror()!=NULL) {
	fputs("ERROR", stderr);
	exit(-1);
 }
 old_read = dlsym(mihandle, "read");
 if (dlerror()!=NULL) {
	fputs("ERROR", stderr);
	exit(-1);
 }
 old_write = dlsym(mihandle, "write");
 if (dlerror()!=NULL) {
	fputs("ERROR", stderr);
	exit(-1);
 }
 old_ioctl = dlsym(mihandle, "ioctl");
 if (dlerror()!=NULL) {
	fputs("ERROR", stderr);
	exit(-1);
 }
 old_close = dlsym(mihandle, "close");
 if (dlerror()!=NULL) {
	fputs("ERROR", stderr);
	exit(-1);
 }
 initlevel=0;	 
 for (i=0; i<4096; i++) {
	tocho[i]=0;
 }
 fctl=old_open("fifo",O_RDONLY);
 if (fctl==-1)
	 exit(-1);
}

#include <stdarg.h>


int ioctl (int __fd, unsigned long int __request, ...) __THROW {
	va_list ap;
	int v;
	int ret;
	int kk;

	inicializa();
//puts("JE");
//fflush(stdout);
	va_start(ap, __request);
	v=va_arg(ap, int);
	va_end(ap);
	if (__fd == fddsp) {
		if (__request == SNDCTL_DSP_STEREO) {
		*(int *)v=1;
		}
	}
	ret=old_ioctl(__fd, __request, v);
	if (__fd == fddsp) {
		if (__request == SNDCTL_DSP_STEREO /*|| __request == SNDCTL_DSP_CHANNELS || __request==SNDCTL_AUDIOINFO*/) {
		kk=*(int *)v;
		mifprintf(stderr, "IOCTL: %08X - %08X ( %08X ) = %08X\n", (int) __request, kk,  *(int*)  v, ret);
		//*(int *)v=44000;
		//	ret=-1;
		}
	}
	return ret;

}

int total_count=0;

float tabla[]={0.5,0.4,0.35,0.30,0.27,0.25,0.24,0.22,0.20,0.18,0.17};


int close(int fd) {
	inicializa();

	if (fd==fddsp)
		fddsp=-1;

	return old_close(fd);
}

float avg=0;

float iam=1;	
float oam=1;	
//#define SUBE 1.00001
//#defien BAJA 0.99
#define SUBE 1
#define BAJA 1

const int cons=15000;
void dsp_amp(signed short * b, int n, float am) {
	int iz, der, i;
	double kki=0;
	double kkd=0;
	n=n/(sizeof(b[0]));
	for (i=0; i<n-1; i+=2) {
			iz=b[i];
			der=b[i+1];
			if (iz!=0 && der!=0) {
				if ( am*iz>=cons || am*iz<=-cons || am*der>=cons || am*der<=-cons ) {
					am*=BAJA;
					//fprintf(stderr,".");
				} else am*=SUBE;
			}
			b[i+0]=iz*am;
			b[i+1]=der*am;
//			kki+=iz;
//			kkd+=der;
	}
//	fprintf(stderr, "%+.4f\t%+.4f\n", kki/(n/2.0)/32768, kkd/(n/2.0)/32768);

}

#include <sys/poll.h>

void get_command() {
	struct pollfd pfd;
	int gotpoll=0;
	char comando[256];
	char * p;

	if (fctl != -1) 	
	do {
		pfd.fd=fctl;
		pfd.events=POLLIN;
		gotpoll=poll(&pfd, 1, 0);
		
		if (gotpoll>0) {
			p=comando;
			do {
				if (read(fctl, p, 1)!=1) {
					close(fctl);
					fctl=-1;
					gotpoll=-1;
					break;
				}
				p++;
			} while (*(p-1) != '\n') ;
			*p='\0';	

			switch(comando[0]) {
				case 'I':
					sscanf(comando+1, "%f", &iam);
					//fprintf(stderr, "%s %f ", comando, am);
					iam = floor ( pow(1.01, iam) * 10.0 ) / 100.0;
					//fprintf(stderr, " %f \n", comando, am);
					break;
				case 'O':
					sscanf(comando+1, "%f", &oam);
					//fprintf(stderr, "%s %f ", comando, am);
					oam = floor ( pow(1.01, oam) * 10.0 ) / 100.0;
					//fprintf(stderr, " %f \n", comando, am);
					break;
			}
		}
		
	} while (gotpoll>0);
	
}

ssize_t read(int fd, void * buf, size_t n) {
	int res;
	
	inicializa(); 

	res=old_read(fd, buf, n);
	if (fd==fddsp) {
		get_command();
		//fprintf(stderr,",");
		dsp_amp(buf, n, iam);
	}
	return res;
}


ssize_t write(int fd, const void * buf, size_t n) {
	int i;
	int j;
	float k;
	signed char * b=(unsigned char*)buf;
	
	inicializa();

	if (fd==fddsp) {

	  get_command();
	
//	 fprintf(stderr,"AJA");
		
	  dsp_amp(buf, n, oam);
	mifprintf(stderr,">> %d %d <<", fd, fddsp);
	}
	return old_write(fd, buf, n);
}

int open(const char * p, int o, ...) {
	int fd;
	inicializa();
	fd=old_open(p, o);
	if (strcmp(p, "/dev/snd/pcmC0D0p")==0 || strcmp(p, "/dev/dsp")==0 ) {
		fddsp=fd;
		mifprintf(stderr, "[[[[[[[ %s %d ]]]]]]]]", p, fd);
	}
	return fd;
}



FILE *fopen(const char *filename, const char * mode) {
	FILE * f;
	inicializa();

	f=old_fopen(filename, mode);
	if (strcmp(filename, "/dev/dsp")==0)  {
		filedsp=f;
		if (f)
			fddsp=fileno(f);
		mifprintf(stderr, "ABRIENDO DSP\n");
	}
	return f;
}


/*  size_t fread(void *ptr, size_t size, size_t nitems, FILE * stream) {
	size_t ret;
	int i;
	unsigned char * aptr;

	inicializa();
	ret=old_fread(ptr, size, nitems, stream);
	if (filedsp && filedsp==stream) {
		aptr=(unsigned char*)ptr;
		for(i=1; i<size*nitems; i+=2) {
			aptr[i]+=i%32;
		}
	}

	return ret;
}*/
size_t fwrite(const void *ptr, size_t size, size_t nitems, FILE * stream) {
	size_t ret;
	int i;
	signed short * aptr;
	int iz,der;

	inicializa();
	
	if (filedsp && filedsp==stream) {
		dsp_amp(ptr, size*nitems, oam);
	}

	ret=old_fwrite((void *)ptr, size, nitems, stream);

	return ret;
}

/*size_t strlen(const char *s) {
	char *t=(char*)s;

	while (*t!='\0')
		t++;
	//write(1, "A", 1);
//	total++;
//	if (total%5==0)
//		s-=10;
	return t-s+0;
}*/

/*void * opendir(const char *nombre) {
	return 0;
}*/
