/* * * * * * * * * * * * * * * * * * * * * * 
 *  eSpeakPackage.h  -- Header to utilize the eSpeak package within C
 *
 *  eSpeak's author:  Jonathan Duddington
 *  eSpeak source:  http://espeak.sourceforge.net/
 *
 *  author of this adaptation package:  Henry M. Walker
 *  last revised:  October 3, 2013
 *
 * * * * * * * * * * * * * * * * * * * * * * /

/* Note: each function name starts with eSpeak to make it 
   easier to understand whether it is a speech function. */

#include "eSpeakPackage.h"
#include <sys/types.h>       /* file of data types needed for many compilers */
#include <unistd.h>          /* needed for fork, getpid procedures */
#include <stdlib.h>          /* needed for exit procedures */
#include <string.h>          /* needed for constructing full path name */
#include <stdio.h>
#include <espeak/speak_lib.h>

/* string commands for textlines-to-speech program */
char * terminating_string = "eSpeakABBCCCDDDEEEEFFFFdone";
char * set_voice_female = "eSpeakABBCCCDDDEEEEFFFFfemale";
char * set_voice_male = "eSpeakABBCCCDDDEEEEFFFFmale";

/* directory of textlines-to-speech program */
char * speech_dir = "/home/walker/public_html/bluetooth-with-c/MyroC";

/* variables to record process id of child */
pid_t pid_audio_start, pid_audio_stop, pid_to_speech;                

/* basic pipe structure */
int to_speech[2];   /* communication to textlines-to-speech */
int from_speech[2]; /* communication from textlines-to-speech */
int error_speech[2];/* standard error for textlines-to-speech */

int debug = 0;  /* debugging flag --- 0 for printing; 1 supress printing */

/* set up the local environment to utilize the eSpeak package 
 */
void eSpeakConnect ()
{  
   
  /* start pulseaudio running for audio output */
   pid_audio_start = fork();      /* create new process */
   if ( -1 == pid_audio_start)    /* check for error in spawning child process */
     { perror ("error in fork");  
       exit (1);
     }

   if (0 == pid_audio_start)      /* check if this is the new child process */
     { /* start pulseaudio process */

       /* construct full path name of startPulseAudio program */
       char * prog_call = "startPulseAudio";
       char * full_script = malloc (strlen(speech_dir) + strlen(prog_call) + 2);
       full_script[0] = 0;
       strcpy (full_script, speech_dir);
       strcat (full_script, "/");
       strcat (full_script, prog_call);
       /* printf ("full programm call:  %s\n", full_script);*/

       execlp (full_script, full_script, (char *)0);
       free (full_script);
       exit(0);
     }
  else 
     { /* processing for parent */
       waitpid (pid_audio_start);
     }

   if (debug)
     printf ("pulse audio started\n"); 

   /* create pipes for 2-way communcation with textlines-to-speech program
      pipe to_speech 
           from this eSpeakPackage.c to textlines-to-speech.c
      pipe from_speech
           from textlines-to-speech.c to this eSpeakPackage.c
      pipe error_speech
           standard error for textlinex-to-speech.c, which can be ignored

      within each pipe:   fd[0] will be input end
                          fd[1] will be output end
   */

   /* create pipes and check for errors */
   if (pipe (to_speech) < 0) 
     { perror("pipe error");
       exit (1);
     }
  
   if (pipe (from_speech) < 0) 
     { perror("pipe error");
       exit (1);
     }

     if (pipe (error_speech) < 0) 
     { perror("pipe error");
       exit (1);
       }

   /* fork processing
           parent:  this package
           child:  textlines-to-speech
   */
   if ((pid_to_speech = fork()) < 0)
     { perror ("error in fork");  
       exit (1);
     }

  if (0 == pid_to_speech)             
    { printf ("working to start textlines-to-speech\n");

      /* processing for textlines-to-speech */
       /* to_speech pipe provides input */
       close (to_speech[1]);  /* close output end */
       /* set standard input to to_speech pipe */
       if (to_speech[0] !=  STDIN_FILENO) 
         {
           if (dup2(to_speech[0], STDIN_FILENO) != STDIN_FILENO)
             { perror("dup2 error for standard input");
               exit(1);
             }
           close(to_speech[0]); /* not needed after dup2 */
         }

      /* from_speech pipe provides output */
       close (from_speech[0]);  /* close input end */
       /* set standard output to pipe */
       if (from_speech[1] !=  STDOUT_FILENO) 
         {
           if (dup2(from_speech[1], STDOUT_FILENO) != STDOUT_FILENO)
             { perror("dup2 error for standard output");
               exit(1);
             }
           close(from_speech[1]); /* not needed after dup2 */
         }

        /* error_speech pipe provides standard error */
       close (error_speech[0]);  /* close output end 
       /* set standard input to to_speech pipe */
       if (error_speech[1] !=  STDERR_FILENO) 
         {
           if (dup2(error_speech[1], STDERR_FILENO) != STDERR_FILENO)
             { perror("dup2 error for standard error");
                 exit(1);
               fprintf (stdout, "error connecting stderr\n");
             }
           close(error_speech[1]);  /* not needed after dup2 */
         }
  
     
       /* start program textlines-to-speech running
          to speak successive lines of text
      
          textlines-to-speech takes successive lines from standard input
          input continues until "eSpeakABBCCCDDDEEEEFFFFdone" read
       */

       /* construct full path name of textlines-to-speech program */
       char * prog_call = "textlines-to-speech";
       char * full_call = malloc (strlen(speech_dir) + strlen(prog_call) + 2);
       full_call[0] = 0; /* start with null string */
       strcpy (full_call, speech_dir);
       strcat (full_call, "/");
       strcat (full_call, prog_call);
       /* fprintf (stderr, "full program call for textlines-to-speech:  %s\n", 
          full_call);*/
   
       /* start executing textlines-to-speech for speech synthesis
          with stdin, stdout, and stderr attached to pipes
       */
       execlp (full_call, full_call, (char *) 0);

       /* execle will return only if exec function fails */
       fprintf (stderr, "error in setting up textlines-to-speech\n");
       exit(0);
     }

  else
    { /* prepare eSpeackPackage and user for communications
         to textlines-to-speech */

      close (to_speech[0]);    /* close input end */
      close (from_speech[1]);  /* close output end */

      /* wait for textlines-to-speech process created */
      int chars_processed = -5;
      read (from_speech[0], &chars_processed, 4);
      if (debug)
        printf ("response from textlines-to-speech:  '%d'\n", chars_processed);


      if (debug)
        printf ("eSpeakConnect done\n");

    }
}

/* clean up the local environment when the eSpeak package is no longer needed
 */
void eSpeakDisconnect ()
{  
  /* printf ("starting eSpeakDisconnect\n"); */
  /* stop textlines-to-speech with sentinel string*/
  write (to_speech[1], "\n", 1);
  write (to_speech[1], terminating_string, strlen(terminating_string));
  write (to_speech[1], "\n", 1);
  /* printf ("terminating textlines-to-speech\n"); */
  waitpid(pid_to_speech);
  /* printf ("now cleanup pulseaudio\n"); */

  /* close pipe to textlines-to-speech */
  /*waitpid (pid);*/  /* wait for textlines-to-speech to finish */

  /* terminate pulseaudio daemon */
  pid_audio_stop = fork();      /* create new process */
  if ( -1 == pid_audio_stop)    /* check for error in spawning child process */
    { perror ("error in fork");  
      exit (1);
    }

  if (0 == pid_audio_stop)      /* check if this is the new child process */
    { /* stopping pulseaudio */
      char * prog_call = "stopPulseAudio";
      char * full_script = malloc (strlen(speech_dir) + strlen(prog_call) + 2);
      full_script[0] = 0;
      strcpy (full_script, speech_dir);
      strcat (full_script, "/");
      strcat (full_script, prog_call);
      /* printf ("full programm call:  %s\n", full_script); */

      execlp (full_script, full_script, (char *)0);
      free (full_script);
      exit(0);
    } 
  else 
    { /* processing for parent */
       int chars_processed = -5;
      read (from_speech[0], &chars_processed, 4);
      if (debug)
        printf ("response from textlines-to-speech:  '%d'\n", chars_processed);

      waitpid (pid_audio_stop);
      if (debug)
        printf ("speech processing completed\n");
   }
}

/* create audio for the user
   @param text:  the string to be converted to audible speech
                 any string is valid, as long as it does not begin
                     eSpeakABBCCCDDDEEEEFFFF   
 */
void eSpeakTalk (const char * text)
{ 
  /* send text through pipe to textlines-to-speech program */
  if (debug)
    printf ("starting to speak %s\n", text);
  
  /* copy string, so original string is not changed
     then send working copy to textlines-to-speech one line at a time */
  char working_string [strlen(text)+1];
    
  strcpy (working_string, text);

  char *token = strtok (working_string, "\n");

  while (token != NULL)
    {
      write (to_speech[1], token, strlen(token));
      write (to_speech[1], "\n", 1);
      if (debug)
        printf ("done sending %s\n", token);

      int chars_processed = -5;
      read (from_speech[0], &chars_processed, 4);
      if (debug)
        printf ("response from textlines-to-speech:  '%d'\n", chars_processed);
  
      token = strtok (NULL, "\n");
     }
  
  /* printf ("exiting eSpeakTalk %s\n", text); */
}

/* set quality of voice to female or male 
   @param gender:  "female" sets woman's voice
                   "male" sets man's voice
   if gender is neither "female" nor "male", voice unchanged
*/
void eSpeakSetGender (const char * gender)
{
  int i;
  int len = strlen(gender)+1;

  /* capitalize input string */
  char genderCaps[len];
  for (i = 0; i < strlen(gender); i++)
    genderCaps[i] = tolower(gender[i]);
  genderCaps [len-1] = 0;
  
  if (strcmp("female", genderCaps) == 0)
    {
      /* tell textlines-to-speech to switch to female voice */
      write (to_speech[1], set_voice_female, strlen(set_voice_female));
      write (to_speech[1], "\n", 1);
    }
  else if (strcmp("male", genderCaps) == 0)
    {
      /* tell textlines-to-speech to switch to male voice */
      write (to_speech[1], set_voice_male, strlen(set_voice_male));
      write (to_speech[1], "\n", 1);
    }
  int chars_processed;
  read (from_speech[0], &chars_processed, 4);
  if (debug)
    printf ("response from textlines-to-speech:  '%d'\n", chars_processed); 
}

