/************************************************************************
 *                                                                      *
 *   Copyright 1990, Brown University, Providence, RI.                  *
 *   Permission to use, copy, modify and distribute this software and   *
 *   its documentation for any purpose other than its incorporation     *
 *   into a commercial product, is hereby granted, provided that this   *
 *   copyright notice appears on all copies.                            *
 *                                                                      *
 * Adaptation of this file to Linux and ANSIfication                    *
 * R.W.W. Hooft, hooft@EMBL-Heidelberg.DE, Copyright 1993               *
 * Same conditions as above                                             *
 *                                                                      *
 * Adapted Version 1.0: October 13, 1993                                *
 ************************************************************************/
/************************************************************************
 *                                                                      *
 *      speak.c                                                         *
 *                                                                      *
 ************************************************************************/
#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/dir.h>
#include <errno.h>
#include <stdio.h>

#define DEBUG(x)

/************************************************************************
 *                                                                      *
 *      type and array of phoneme audio samples                         *
 *                                                                      *
 ************************************************************************/
struct phoneme_t
  {
    int size;
    char *data;
  };

static struct phoneme_t phoneme[1024];

/************************************************************************
 *                                                                      *
 *      prototypes                                                      *
 *                                                                      *
 ************************************************************************/
int speak_delay (int delay);	/* in 10ths of a second */
void xlate_file (void);
int ch_to_code (char **cp);
/************************************************************************
 *                                                                      *
 *      names of known phonemes                                         *
 *                                                                      *
 ************************************************************************/
static
char *phoneme_name[] =
{
  "IY", "EY", "AE", "AO", "UH",
  "ER", "AH", "AW", "IH", "EH",
  "AA", "OW", "UW", "AX", "AY",
  "OY", "YU", "p", "t", "k", "f",
  "TH", "s", "SH", "HH", "n",
  "l", "y", "CH", "WH", "b",
  "d", "g", "v", "DH", "z",
  "ZH", "m", "NG", "w", "r", "j",
  (char *) 0
};

extern FILE *In_file;		/* from parse.c */
extern char *sys_errlist[];

static char *buffer;
static int curfd;

int 
speak_load_samples (char *dir)
{
  register i, rv;
  char *cp;
  int fd;
  char cwd[1024];
  DIR *dirp;
  struct direct *dp;
  struct stat st;

  DEBUG (fprintf (stderr, "load_samples called\n"));

  if ((dirp = opendir (dir)) == 0)
    {
      perror (dir);
      return -1;
    }

  (void) getwd (cwd);
  if (chdir (dir))
    {
      perror (dir);
      return -1;
    }

  while ((dp = readdir (dirp)))
    {
      for (cp = phoneme_name[i = 0];
	   cp && strcmp (dp->d_name, cp) != 0;
	   cp = phoneme_name[++i]);
      if (cp == (char *) 0)
	continue;

      if ((fd = open (dp->d_name, O_RDONLY)) < 0)
	{
	  perror (dp->d_name);
	  return -1;
	}
      DEBUG (fprintf (stderr, "Reading : %s\n", dp->d_name));

      lseek (fd, 0, 10);

      i = ch_to_code (&cp);
      if (phoneme[i].data)
	free (phoneme[i].data);

      if (fstat (fd, &st))
	{
	  perror (dp->d_name);
	  return -1;
	}

      phoneme[i].size = st.st_size;

      if ((phoneme[i].data = (char *) malloc (phoneme[i].size)) == 0)
	{
	  fprintf (stderr, "Malloc returned NULL: Out of memory?\n");
	  return -1;
	}

      if ((rv = read (fd, phoneme[i].data, phoneme[i].size)) <= 0)
	{
	  perror (dp->d_name);
	  return -1;
	}

      if (rv < phoneme[i].size)
	phoneme[i].size = rv;

      close (fd);

    }
  closedir (dirp);

  if (chdir (cwd))
    {
      perror (cwd);
      return -1;
    }

  return 0;
}

int
speak_open (char *device, int now)
{
  int fd;
  struct stat st;
  DEBUG (fprintf (stderr, "speak_open calledi\n"));
  if (device == 0)
    device = "/dev/audio";

  if (stat (device, &st))
    {
      perror (device);
      return -1;
    }

  if (!S_ISCHR (st.st_mode))
    {
      fprintf (stderr, "%s is not an audio device", device);
      return -1;
    }

  if ((fd = open (device, O_WRONLY | (now ? O_NDELAY : 0))) < 0)
    if (errno == EBUSY)
      return -1;
    else
      {
	perror (device);
	return -1;
      }
  return fd;
}

int 
speak_string (int fd, char *str)
{
  buffer = str;
  curfd = fd;
  xlate_file ();		/* hook into eng_to_phoneme code */
  buffer = (char *) 0;

  return 0;
}

int 
speak_file (int fd, char *filename)
{
  if ((In_file = fopen (filename, "r")) == 0)
    {
      perror (filename);
      return -1;
    }

  xlate_file ();
  fclose (In_file);
  In_file = (FILE *) 0;

  return 0;
}

int
speak_volume (int fd, double vol)
{
  return 0;
}

int
speak_close (int fd)
{
  close (fd);
  return 0;
}

int
hacked_getc (FILE * fp)
{
  static ix;

  if (In_file)
    return getc (fp);

  if (buffer[ix] == 0)
    {
      ix = 0;
      return EOF;
    }
  else
    return buffer[ix++];
}

void
phoneme_str_to_audio (char *cp)
{
  register i;
  static n = 0;

  while (*cp)
    if (isspace (*cp))
      {
	cp++;
	speak_delay (0);
      }
    else
      {
	i = ch_to_code (&cp);
	write (curfd, phoneme[i].data, phoneme[i].size);
	n++;
      }
}

/************************************************************************
 *                                                                      *
 *      ch_to_code                                                      *
 *      code_to_ch                                                      *
 *                                                                      *
 *      Map a phoneme name to a unique index between 1 and 1023, or     *
 *      vice versa.  Ch_to_code advances it's argument to the next      *
 *      phoneme (or null).  Code_to_ch returns a null-terminated        *
 *      string.                                                         *
 *                                                                      *
 ************************************************************************/
int
ch_to_code (char **cp)
{
  register char *tp = *cp;

  if (islower (*tp))
    {
      *cp += 1;
      return *tp - 'a' + 1;
    }
  else
    {
      *cp += 2;
      return ((*tp - 'A' + 1) << 5) | (*(tp + 1) - 'A' + 1);
    }
}

char *
code_to_ch (int code)
{
  static char ch[3];

  if (code < (1 << 5))
    {
      ch[1] = code + 'a' - 1;
      return &ch[1];
    }
  else
    {
      ch[0] = (code >> 5) + 'A' - 1;
      ch[1] = (code & 0x1f) + 'A' - 1;
      return &ch[0];
    }
}

/************************************************************************
 *                                                                      *
 *      speak_delay                                                     *
 *                                                                      *
 *      Flush the audio device and pause for the given interval.        *
 *      If no interval is specified, pause for 2/10 second.             *
 *                                                                      *
 ************************************************************************/
int 
speak_delay (int delay)		/* in 10ths of a second */
{
  static char dumbuf[800] =
  {0};
  int i;

  if (!delay)
    delay = 2;
  for (i = 0; i < delay; i++)
    write (curfd, dumbuf, 800);
  return 0;
}
