/***********************************************************************/
/*  This file contains the main loop to generate .par files to be 
    processed by klatt.

    Aaron Smith 
    Brown University
    20 April 1991

    This version uses a second order diff eq to do smoothing between 
    segments.
*/

#include <stdio.h>
#include <curses.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "phonemes.h"
#include "control.h"

extern void segdur();
extern int yylex();
extern char *yytext;

extern long Features[ALL_END+1];
extern int InhDur[ALL_END+1];
extern int Vowels[23][2][6];
extern int Sonorants[10][8];
extern int Nasals[5][9];
extern int Fricatives[8][15];
extern int Affricates[10][15];
extern int Plosives[10][15];
extern int Psuedo_vowels[1][16];
 
int  ph_list[50];
int  syll_count[10];
int  dur_list[50];
int  word_count;

int Frames[MAX_FRAMES][20];
int LastTarget[20];
float f0[2],f1[2],f2[2],f3[2],bw1[2],bw2[2],bw3[2],av[2];
float length,s_f0,s_f1,s_f2,s_f3,s_bw1,s_bw2,s_bw3,s_av,fnp,fnz;
float af,ab,a1,a2,a3,a4,a5,a6,asp;
int   total_frame_count;

int CumDur[20];
int SegDur[20];
int Mintime[20];
int Trantype[20];
int Target[20];
int Diptar[20];
int Oldval[20];
int Nextar[20];
int Tcf[20];
int Tcb[20];
int Bper[20];
int Bvf[20];
int Bvb[20];

int f0_change=0;

void save_file();
void gen_pause();

int bPlay=1;

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

{
  FILE *file,*temp;
  int i,j,sel=0,count=0,off,type,phone1,total_dur=0,total_frames,features;
  int last_phone,next_phone,list_count,h,k,new_word,s_count;
  char buf[80],ch;
  int interp;

  if (argc == 2) {
    if (!strcmp(argv[1],"-ns"))
      bPlay = 0;
  }

  do {
    printf("VOWELS\n");
    printf("aa Bob   ae bat   ah but   ao bought    aw bout\n");
    printf("ax about axr bar  ay bite  eh bet       er bird\n");
    printf("exr bear ey bait  ih bit   ix impunity  ixr beer\n");
    printf("iy beet  ow boat  oxr boar oy boy       uh book\n");
    printf("uw boot  uxr poor yu beauty\n");
    printf("\n");
    printf("NASALS\n");
    printf("mm met   n net\n");
    printf("\n");
    printf("Separate phonemes with space end input with return\n ");
    printf(": ");
    
    count=0;
    do {
      ph_list[count] = yylex(); /* get the phoneme from yylex() */
      if (ph_list[count] == UNKNOWN_PHONEME) {
        printf("Unknown phoneme\n");
        printf("Hit return to continue ");
        getchar();
        continue;
      }
      if (ph_list[count] == QUIT) { /* user typed quit? */
        exit(0);
      }
      else if (ph_list[count] == SAVE_MOST_RECENT) {
        printf("Filename: ");
        scanf("%s",buf);
        sprintf(buf,"%s.par",buf);
        if ((file = fopen(buf,"w+")) == NULL) {
          printf("Cannot open %s.par\n",buf);
          exit(1);
        }
        save_file(total_frame_count,file);
        continue;
      }
      else if (ph_list[count] == END_OF_LIST) { /* return ends a phrase */
        count++;
        break;
      }
      else if (ph_list[count] == PLAY_AGAIN) {
        system("sklatt tmp.par");
        continue;
      }
      else if (ph_list[count] == PAUSE) {
        gen_pause();
        continue;
      }
      count++;
    } while (1);                /* continue until quit or end of input */
    
    if (count == 0)
      exit(0);
    
    a1 = 0;

    f0[0] = 1300;               /* inital F0 */
    f0[1] = 1300;    
    total_frame_count = 0;

    segdur(ph_list,dur_list,&count); /* calculate the length of each segment */


    /* 
      the following loop parses the input string to remove everything but
       phoneme information
    */

    j=0;
    word_count=0;
    s_count=0;
    for (i=0;i < count;i++) {
      if (ph_list[i] == SYLLABLE_MARKER) /* / is syllable marker */
        continue;
      if (ph_list[i] == WORD_BREAK) {
        syll_count[word_count] = s_count;
        s_count=-1;
        word_count++;
      }
      if (ph_list[i] == END_OF_LIST) {
        syll_count[word_count] = s_count;
        word_count++;
        break;
      }
      s_count++;
      ph_list[j++] = ph_list[i];
    }
    count = j;

    /* generation loop */
    h=0;
    list_count=0;
    while (h < word_count) {
      new_word = 1;
      i=0;
      while (i < syll_count[h]) {
        if (i == syll_count[h]-1 && h == word_count-1)
          f0_change = -1000;
        else 
          f0_change = 0;

        /* translate to array type and offset */
        if (ph_list[list_count] == WORD_BREAK) {
          list_count++;
          continue;
        }
        type = translate_phone(ph_list[list_count],&off);       
        length = dur_list[list_count]/10;
        interp = 0;
        
        if (i < (syll_count[h]-1))
          interp = set_params(type,off,ph_list[list_count+1]);
        else
          interp = set_params(type,off,-1);
        
        features = Features[ph_list[list_count]];
        
        next_phone = -1;
        last_phone = -1;

        /* calculate previous and next phones if they exist */
        if (i < (syll_count[h]-1))
          next_phone = ph_list[list_count+1];
        if (!new_word)
          last_phone = ph_list[list_count-1];
        
        /* generate according to phoneme type */
        if (features & PLOSI) {
          gen_plosive(ph_list[list_count],dur_list[list_count],last_phone,next_phone);
        }
        else if (features & FRICA) {
          gen_fricative(ph_list[list_count],dur_list[list_count],last_phone,next_phone);
        }
        else if (features & VOWEL) {
          gen_vowel(ph_list[list_count],dur_list[list_count],last_phone,next_phone);
        }
        else if (features & SONOR) {
          gen_sonorant(ph_list[list_count],dur_list[list_count],last_phone,next_phone);
        }
        else {
	  printf("Silence\n");
  
          for (j=0;j < length;j++) {
            Frames[total_frame_count][0] = (int)f0[0];
            Frames[total_frame_count][1] = (int)f1[0];
            Frames[total_frame_count][2] = (int)f2[0];
            Frames[total_frame_count][3] = (int)f3[0];
            Frames[total_frame_count][4] = (int)bw1[0];
            Frames[total_frame_count][5] = (int)bw2[0];
            Frames[total_frame_count][6] = (int)bw3[0];
            Frames[total_frame_count][7] = (int)fnz;
            Frames[total_frame_count][8] = (int)fnp;
            Frames[total_frame_count][9] = (int)av[0];
            Frames[total_frame_count][10] = 0;
            Frames[total_frame_count][11] = (int)asp;
            Frames[total_frame_count][12] = (int)af;
            Frames[total_frame_count][13] = (int)a1;
            Frames[total_frame_count][14] = (int)a2;
            Frames[total_frame_count][15] = (int)a3;
            Frames[total_frame_count][16] = (int)a4;
            Frames[total_frame_count][17] = (int)a5;
            Frames[total_frame_count][18] = (int)a6;
            Frames[total_frame_count][19] = (int)ab;
            

            /* the following is a hack to give the F0 some life */
/*
            if (j < (total_frames/3)) 
              f0[0] += 10;
            else
              f0[0] -= 10;
*/            
            if (interp) {
              f0[0] += s_f0;
              f1[0] += s_f1;
              f2[0] += s_f2;
              f3[0] += s_f3;
              bw1[0] += s_bw1;
              bw2[0] += s_bw2;
              bw3[0] += s_bw3;
              av[0] += s_av;
            }
            total_frame_count++;
          }
        }
        i++;
        list_count++;
        new_word = 0;
      }

      /* give a WORD_BREAK_TIME pause in between words */
      if (++h < word_count) {
        for (k=0;k < WORD_BREAK_TIME/10;k++) {
          Frames[total_frame_count][0] = LastTarget[0];
          Frames[total_frame_count][1] = LastTarget[1];
          Frames[total_frame_count][2] = LastTarget[2];
          Frames[total_frame_count][3] = LastTarget[3];
          Frames[total_frame_count][4] = LastTarget[4];
          Frames[total_frame_count][5] = LastTarget[5];
          Frames[total_frame_count][6] = LastTarget[6];
          Frames[total_frame_count][7] = LastTarget[7];
          Frames[total_frame_count][8] = LastTarget[8];
          Frames[total_frame_count][9] = 0;
          Frames[total_frame_count][10] = LastTarget[10];
          Frames[total_frame_count][11] = 0;
          Frames[total_frame_count][12] = 0;
          Frames[total_frame_count][13] = LastTarget[13];
          Frames[total_frame_count][14] = LastTarget[14];
          Frames[total_frame_count][15] = LastTarget[15];
          Frames[total_frame_count][16] = LastTarget[16];
          Frames[total_frame_count][17] = LastTarget[17];
          Frames[total_frame_count][18] = LastTarget[18];
          Frames[total_frame_count++][19] = LastTarget[19];
        }
      }
    }
    if (bPlay) {                /* play using sklatt */
      temp = fopen("tmp.par","w+");
      save_file(total_frame_count,temp);
      fclose(temp);
      system("sklatt tmp.par");
    }
  } while (1);
  ex:
    system("rm tmp.par");
    fclose(file);
}

void gen_pause()
{
  int k;

  for (k=0;k < 50;k++) {
    Frames[total_frame_count][0] = 100;
    Frames[total_frame_count][1] = 100;
    Frames[total_frame_count][2] = 200;
    Frames[total_frame_count][3] = 300;
    Frames[total_frame_count][4] = 100;
    Frames[total_frame_count][5] = 200;
    Frames[total_frame_count][6] = 300;
    Frames[total_frame_count][7] = 200;
    Frames[total_frame_count][8] = 200;
    Frames[total_frame_count][9] = 0;
    Frames[total_frame_count][10] = 0;
    Frames[total_frame_count][11] = 0;
    Frames[total_frame_count][12] = 0;
    Frames[total_frame_count][13] = 0;
    Frames[total_frame_count][14] = 0;
    Frames[total_frame_count][15] = 0;
    Frames[total_frame_count][16] = 0;
    Frames[total_frame_count][17] = 0;
    Frames[total_frame_count][18] = 0;
    Frames[total_frame_count++][19] = 0;
  }
}

int translate_phone(ph,offset)
int ph;
int *offset;
{
  if (ph <= VOWELS_END) {
    *offset = ph;
    return VOWEL_TYPE;
  }
  else if (ph <= SONORANTS_END) {
    *offset = ph - SONORANTS_START;
    return SONORANT_TYPE;
  }
  else if (ph <= NASALS_END) {
    *offset = ph - NASALS_START;
    return NASAL_TYPE;
  }
  else if (ph <= FRICATIVES_END) {
    *offset = ph - PLOSIVES_START;
    return PLOSIVE_TYPE;
  }
  else if (ph <= AFFRICATES_END) {
    *offset = ph - AFFRICATES_START;
    return AFFRICATE_TYPE;
  }
  else if (ph <= PSUEDO_VOWELS_END) {
    *offset = ph - PSUEDO_VOWELS_START;
    return PSUEDO_VOWEL_TYPE;
  }
  else {
    *offset =-1;
    return -1;
  }
}

/* the following looks up initial values for each of the parameters in the
   database.  If the phoneme is a vowel with a dipthong, f[1] is filled with
   the dipthong target.  Otherwise, f[1] = f[0].
*/
int set_params(type,off,next_phone)
int type;
int off;
int next_phone;
{
  int return_val = 0;

  fnp = 250;
  fnz = 250;
  
  if (type == VOWEL_TYPE) {
    asp = ab = af = 0;
    a1 = a2 = a3 = a4 = a5 = a6  = 60;
    av[0] = VOWEL_AV;
    av[1] = VOWEL_AV;
    f1[GEN_START] = (float) Vowels[off][GEN_START][F1]; 
/* START = 0, END = 1 */
    f1[GEN_STOP] = (float) Vowels[off][GEN_STOP][F1];
    f2[GEN_START] = (float) Vowels[off][GEN_START][F2];
    f2[GEN_STOP] = (float) Vowels[off][GEN_STOP][F2];
    f3[GEN_START] = (float) Vowels[off][GEN_START][F3];
    f3[GEN_STOP] = (float) Vowels[off][GEN_STOP][F3];
    bw1[GEN_START] = (float) Vowels[off][GEN_START][BW1];
    bw1[GEN_STOP] = (float) Vowels[off][GEN_STOP][BW1];
    bw2[GEN_START] = (float) Vowels[off][GEN_START][BW2];
    bw2[GEN_STOP] = (float) Vowels[off][GEN_STOP][BW2];
    bw3[GEN_START] = (float) Vowels[off][GEN_START][BW3];
    bw3[GEN_STOP] = (float) Vowels[off][GEN_STOP][BW3];
    
    s_f0 = (f0[1] - f0[0]) / (float) length;
    s_f1 = (f1[1] - f1[0]) / (float) length;
    s_f2 = (f2[1] - f2[0]) / (float) length;
    s_f3 = (f3[1] - f3[0]) / (float) length;
    s_bw1 = (bw1[1] - bw1[0]) / (float) length;
    s_bw2 = (bw2[1] - bw2[0]) / (float) length;
    s_bw3 = (bw3[1] - bw3[0]) / (float) length;
    s_av = (av[1] - av[0]) /(float) length;     
    return_val = 1;
  }
  else if (type == SONORANT_TYPE) {
    a1 = a2 = a3 = a4 = a5 = a6  = 60;
    ab = 0;
    af = 0;
    asp = (float) Sonorants[off][7];
    av[0] = (float) Sonorants[off][AV];
    f1[0] = (float) Sonorants[off][F1];
    f2[0] = (float) Sonorants[off][F2];
    f3[0] = (float) Sonorants[off][F3];
    bw1[0] = (float) Sonorants[off][BW1];
    bw2[0] = (float) Sonorants[off][BW2];
    bw3[0] = (float) Sonorants[off][BW3];
    fnp = 250.0;
    fnz = 250.0;
  }
  else if (type == NASAL_TYPE) {
    a1 = a2 = a3 = a4 = a5 = a6  = 60;
    ab = af = asp = 0;
    av[0] = (float) Nasals[off][8];
    f1[0] = (float) Nasals[off][F1];
    f2[0] = (float) Nasals[off][F2];
    f3[0] = (float) Nasals[off][F3];
    bw1[0] = (float) Nasals[off][BW1];
    bw2[0] = (float) Nasals[off][BW2];
    bw3[0] = (float) Nasals[off][BW3];
    fnp = (float) Nasals[off][FNP];
    fnz = (float) Nasals[off][FNZ];
  }
  else if (type == FRICATIVE_TYPE) {
    a1 = 0;
    a2 = (float) Fricatives[off][A2];
    a3 = (float) Fricatives[off][A3];
    a4 = (float) Fricatives[off][A4];
    a5 = (float) Fricatives[off][A5];
    a6 = (float) Fricatives[off][A6];
    ab = (float) Fricatives[off][AB];
    
    av[0] = (float) Fricatives[off][AV];
    f1[0] = (float) Fricatives[off][F1];
    f2[0] = (float) Fricatives[off][F2];
    f3[0] = (float) Fricatives[off][F3];
    bw1[0] = (float) Fricatives[off][BW1];
    bw2[0] = (float) Fricatives[off][BW2];
    bw3[0] = (float) Fricatives[off][BW3];
    asp = (float) Fricatives[off][ASP];
    af  = (float) Fricatives[off][AF];
  }
  else if (type == PLOSIVE_TYPE) {
    a1 = 0;
    a2 = (float) Plosives[off][A2];
    a3 = (float) Plosives[off][A3];
    a4 = (float) Plosives[off][A4];
    a5 = (float) Plosives[off][A5];
    a6 = (float) Plosives[off][A6];
    ab = (float) Plosives[off][AB];
    
    av[0] = (float) Plosives[off][AV];
    f1[0] = (float) Plosives[off][F1];
    f2[0] = (float) Plosives[off][F2];
    f3[0] = (float) Plosives[off][F3];
    bw1[0] = (float) Plosives[off][BW1];
    bw2[0] = (float) Plosives[off][BW2];
    bw3[0] = (float) Plosives[off][BW3];
    asp = (float) Plosives[off][ASP];
    af  = (float) Plosives[off][AF];
  }
  else if (type == AFFRICATE_TYPE) {
    a1 = 0;
    a2 = (float) Affricates[off][A2];
    a3 = (float) Affricates[off][A3];
    a4 = (float) Affricates[off][A4];
    a5 = (float) Affricates[off][A5];
    a6 = (float) Affricates[off][A6];
    ab = (float) Affricates[off][AB];
    
    av[0] = (float) Affricates[off][AV];
    f1[0] = (float) Affricates[off][F1];
    f2[0] = (float) Affricates[off][F2];
    f3[0] = (float) Affricates[off][F3];
    bw1[0] = (float) Affricates[off][BW1];
    bw2[0] = (float) Affricates[off][BW2];
    bw3[0] = (float) Affricates[off][BW3];
    asp = (float) Affricates[off][ASP];
    af  = (float) Affricates[off][AF];
  }
  else if (type == PSUEDO_VOWEL_TYPE) {
    a1 = 0;
    a2 = (float) Psuedo_vowels[off][A2];
    a3 = (float) Psuedo_vowels[off][A3];
    a4 = (float) Psuedo_vowels[off][A4];
    a5 = (float) Psuedo_vowels[off][A5];
    a6 = (float) Psuedo_vowels[off][A6];
    ab = (float) Psuedo_vowels[off][AB];
    
    av[0] = (float) Psuedo_vowels[off][AV];
    f1[0] = (float) Psuedo_vowels[off][F1];
    f2[0] = (float) Psuedo_vowels[off][F2];
    f3[0] = (float) Psuedo_vowels[off][F3];
    bw1[0] = (float) Psuedo_vowels[off][BW1];
    bw2[0] = (float) Psuedo_vowels[off][BW2];
    bw3[0] = (float) Psuedo_vowels[off][BW3];
    asp = (float) Psuedo_vowels[off][ASP];
  }
  return return_val;
}



void save_file(frame_count,file)

int frame_count;
FILE *file;

{       
  char buf[20];
  int i;

  for (i=0;i < frame_count;i++) 
  {
    fprintf(file,
	   " %d  %d %d %d  %d %d %d  %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
	    Frames[i][0],Frames[i][1],Frames[i][2],Frames[i][3],Frames[i][4],
	    Frames[i][5],Frames[i][6],Frames[i][7],Frames[i][8],Frames[i][9],
	    Frames[i][10],Frames[i][11],Frames[i][12],Frames[i][13],
	    Frames[i][14],Frames[i][15],Frames[i][16],Frames[i][17],
	    Frames[i][18],Frames[i][19]);
  }
}
 
