/*
 * esempio di unita' echo e riverbero
 *
 * il file  dati.echo contiene il numero di echo e per ciascun echo
 * delay (in secondi) e ampiezza  (somma 1.0)           
 * il primo dato deve avere delay=0.0 
 *
 * formato dei suoni: (16bit big/little endian stereo PCM senza headers) 
 *
 *
 * Sergio Steffe' - Laboratorio Sperimentale di Matematica Computazionale
 * Dipartimento di Matematica - Universita' di Pisa - AA 2004-2005
 *  ultima modifica: 2009-05-13
 */

#include <stdio.h>
#include <math.h>
#include <unistd.h> /* per getopt */
#include <string.h>
#include <stdlib.h> /* per exit e return */
#include <ctype.h> /* per isprint */

/* prototipi C99 */
double round(double);
/* fine prototipi */

static void usage(char *command)
{
        fprintf(stderr,
"Usage: %s FILE [OPTION]... \n"
"-h,            help\n"
"-i input_file	input file \n"
"-o output_file	output file\n"
"-c config_file echo configuration data lines:"
"               delay (sec) amplitude\n"
"               (first delay = 0.0, sum of amplitudes =1.0, no more than 190 lines)\n"
"    stereo, sampling rate 44100Hz, add echo or reverb to sound\n"
	 ,command);
}

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

{
/* dichiarazioni      */
  FILE *cdrout, *cdrin, *echodata, *fopen();
  char nomein[256], nomeout[256], nomedata[256]; /* nomi dei files */
  char *nomecomando; /*nome del comando */
  int c; /* per il getopt */
  char *cvalue = NULL ; /* per le stringhe delle opzioni */
  int i,j;  /* per i cicli */
  short int ii;   /* 2 bytes */
  char buf[256],cc;  /* per la conversione little-endian big-endian */
  int rressize,wressize; /* buffer intero */
  double maxint; /* max integer value 16 bit */
  double fsample; /* frequenza di campionamento */
  double ttime; /* durata del suono generato in secondi */
  double amp; /* ampiezza del suono generato */
  double  dsin,snin,ds,sn; /*  ampiezza ds e sn ingresso e uscita*/
  long posin; /* numero totale campioni */
  double delayecho[200],ampecho[200]; /* delay, ampiezza  */
  int puntecho[200]; /* posizione */
  int necho; /* numero di linee del file nomedata  < 190 */
  double maxdelay; /* max delay prodotto da un echo */
  int nn, puntatore; /* grandezza buffer FIFO e puntatore */
  double *bufds,*bufsn; /* buffer per l'elaborazione dell'echo */
/* fine dichiarazioni */
/* parametri fissi */
fsample=44100.0;
maxint=32767.0;
nomecomando=argv[0];
if ((argc != 7)  ) {
                usage(nomecomando);
		exit(EXIT_FAILURE);
                };
/* getopt */

while ((c = getopt (argc, argv, "i:o:c:")) != -1)
switch (c)
           {
	    case 'i':
		cvalue = optarg;
		if( 1 != sscanf(cvalue,"%s\n",nomein) ) {printf("parametro -i %s non valido\n",cvalue); exit(EXIT_FAILURE);}
	    	break; 
	    case 'o':
	    	cvalue = optarg;
		if( 1 != sscanf(cvalue,"%s\n",nomeout)) {printf("parametro -o %s non valido\n",cvalue); exit(EXIT_FAILURE);}
		break;
            case 'c':
                cvalue = optarg;
                if( 1 != sscanf(cvalue,"%s\n",nomedata)) {printf("parametro -c %s non valido\n",cvalue); exit(EXIT_FAILURE);}
                break;  
	    case '?':
		if (isprint (optopt))
               fprintf (stderr, "Unknown option `-%c'.\n", optopt);
             else
		fprintf (stderr, "Unknown option character `\\x%x'.\n", optopt);
             	exit(EXIT_FAILURE);
           default:
             abort ();
          } 
/* apri in lettura il file di configurazione*/

if ((echodata = fopen(nomedata,"r")) == NULL) {
        printf("\n non posso aprire il file  %s \n",nomedata);
        exit(EXIT_FAILURE);
        };

/* apri in lettura il file del suono di ingresso */
if ((cdrin = fopen(nomein,"r")) == NULL) {
        printf("\n non posso aprire il file  %s \n",nomein);
        exit(EXIT_FAILURE);
        };

/* apri in scrittura il file del suono prodotto */
if ((cdrout = fopen(nomeout,"w")) == NULL) {
        printf("\n non posso aprire il file  %s \n",nomeout);
        exit(EXIT_FAILURE);
        };

/* lettura dei dati dal file nomedata */
fscanf(echodata,"%d",&necho); /* numero delle linee dati echo */
if ( (necho > 191) || (necho < 1)) {
	        printf("\n il numero di linee in %s deve essere tra 1 e 191",nomedata);
                exit(EXIT_FAILURE);
		}
for(i=0; i<necho;i++) 
    if( 2 != fscanf(echodata,"%lf %lf\n",&(delayecho[i]),&(ampecho[i]))) {
	printf("\n il numero di linee in %s non coincide con %d\n",nomedata,necho);
	exit(EXIT_FAILURE);
	}
/* stampa di prova 
for(i=0; i<necho;i++) printf(" %d, %f %f\n", i, delayecho[i],ampecho[i]);
*/
/* controllo della coerenza dei dati degli echi */
amp=0.0;
maxdelay=0.0;
for(i=0;i<necho;i++) {
	if (delayecho[i] < 0.0) delayecho[i] = -delayecho[i];
	if(ampecho[i] < 0.0) ampecho[i] = -ampecho[i];
	amp=amp+ampecho[i];
	if( maxdelay < delayecho[i]) maxdelay = delayecho[i];
	}
if( amp > 1.000000001) {
	printf("warning: riscaling echo amplitude data(%f)\n",amp);
	for(i=0;i<necho;i++) ampecho[i] = ampecho[i]/amp;
	}

nn = maxdelay*fsample + 1.5; /* grandezza dei buffer FIFO */

/* allocazione dei due buffer FIFO */

bufds =  calloc(nn, sizeof(amp));
bufsn =  calloc(nn, sizeof(amp));

if((bufds == NULL)||(bufsn == NULL)) {
	printf("No memory :(%d) double required\n",nn);
	exit(EXIT_FAILURE);
	}
/* printf("FIFO da %d double\n",nn);*/
/* inizio elaborazione */

for(i=0;i<nn;i++) {
	bufds[i]=0.0;
	bufsn[i]=0.0;
	}
/* calcolo degli indici dei delay */
for(i=0;i<necho;i++) {
	puntecho[i] = nn - (delayecho[i]*fsample +0.5);
	/* printf("puntecho[%d]=%d\n",i,puntecho[i]);*/
/*	puntecho[i]=puntecho[i]+1; */
	}
puntatore=nn-1;
posin=0;
/* ripeti fino alla fine del file */
rressize = 4;
while (rressize == 4) { 
/* leggi i due canali  4 bytes */
rressize=fread(buf,1,4,cdrin);
/* scambio dei due bytes */
cc=buf[1];
buf[1]=buf[0];
buf[0]=cc;
/* scambio dei due bytes */
cc=buf[3];
buf[3]=buf[2];
buf[2]=cc;
/* assegnazione dei valori ai canali ds e sn */
memcpy(&ii,buf,2);
dsin=ii;
memcpy(&ii,&buf[2],2);
snin=ii;
/* aggiorna il puntatore del FIFO scartando la posizione piu vecchia */
if (puntatore == nn-1) puntatore=0;
else puntatore=puntatore + 1;
/* inserisci i dati nel loro posto nel FIFO */
bufds[puntatore]=dsin;
bufsn[puntatore]=snin;
/* aggiorna i puntatori alle posizioni dei delay */
for(i=0;i<necho;i++) {
	if (puntecho[i]==nn-1) puntecho[i]=0;
	else puntecho[i]=puntecho[i] + 1;
	}
/* somma gli echi coi ritardi richiesti */
ds=0.0;
sn=0.0;
 for (i=0;i<necho;i++) {
	ds=ds+ampecho[i]*bufds[puntecho[i]];
	sn=sn+ampecho[i]*bufsn[puntecho[i]];
	}
/* scrivi il dato nel file di output */
/* canale destro */
 ii=round(ds); /* short int 2 bytes */
/* big endian: scambio dei due bytes */
 memcpy(buf,&ii,2);
 wressize=fwrite(&buf[1],1,1,cdrout);
 wressize=fwrite(buf,1,1,cdrout);
/* canale sinistro */
 ii=round(sn); /* short int 2 bytes */
/* big endian: scambio dei due bytes */
 memcpy(buf,&ii,2);
 wressize=fwrite(&buf[1],1,1,cdrout);
 wressize=fwrite(buf,1,1,cdrout);
/* incrementa il contatore della posizione */
posin++;
}
/* quando non ci sono altri dati nel file di ingresso
   ci sono ancora degli echi in arrivo, e supponiamo
   che in ingresso ci sia silenzio.
   restano nn dati da scrivere
   */
  for(j=0;j<nn;j++) {
  /* assegnazione del valore 0 ai canali ds e sn */
  dsin=0;
  snin=0;
  /* aggiorna il puntatore del FIFO scartando la posizione piu vecchia */
  if (puntatore == nn-1) puntatore=0;
  else puntatore=puntatore + 1;
  /* inserisci i dati nel loro posto nel FIFO */
  bufds[puntatore]=dsin;
  bufsn[puntatore]=snin;
  /* aggiorna i puntatori alle posizioni dei delay */
  for(i=0;i<necho;i++) {
        if (puntecho[i]==nn-1) puntecho[i]=0;
        else puntecho[i]=puntecho[i] + 1;
        }
  /* somma gli echi coi ritardi richiesti */
  ds=0.0;
  sn=0.0;
  for (i=0;i<necho;i++) {
        ds=ds+ampecho[i]*bufds[puntecho[i]];
        sn=sn+ampecho[i]*bufsn[puntecho[i]];
        }
  /* scrivi il dato nel file di output */
  /* canale destro */
  ii=round(ds); /* short int 2 bytes */
  /* big endian: scambio dei due bytes */
  memcpy(buf,&ii,2);
  wressize=fwrite(&buf[1],1,1,cdrout);
  wressize=fwrite(buf,1,1,cdrout);
  /* canale sinistro */
  ii=round(sn); /* short int 2 bytes */
  /* big endian: scambio dei due bytes */
  memcpy(buf,&ii,2);
  wressize=fwrite(&buf[1],1,1,cdrout);
  wressize=fwrite(buf,1,1,cdrout);
  }
/* finita elaborazione */
ttime=posin;
ttime=ttime/fsample;

fclose(cdrout);

printf("scritto in %s %f sec\n     [da %s con echo descritto in %s]  \n",nomeout,ttime,nomein,nomedata);

return EXIT_SUCCESS;
}
