/*
 * TALK.C  Written by Jon L. Sherling, 6/18/88
 * This program may be distributed anywhere provided that the
 * source including these credits are provided as well.
 */
#include <stdio.h>
#include <ctype.h>
#include <exec/types.h>
#include <exec/memory.h>
/*
 * External functions
 */
extern void *OpenLibrary();
extern int  strlen();
extern long Translate();
extern void fclose();
extern FILE *fopen();
extern char *fgets();
extern void *AllocMem();
extern struct MsgPort *CreatePort();

/*
 * local functions
 */
void bye(),
     hello(),
     main();

#define BUFLEN 80L			/* Translation buffer size	*/	
char phoneme[BUFLEN+1];			/* Translation buffer output	*/
char english[BUFLEN+1];			/* Translation buffer input	*/
char errormsg[80];

struct Library *TranslatorBase = 0L;	/* Translator library base ptr	*/
FILE *infile = 0L;

#define NUMBUFS 56

struct SOUNDBUFFER
{   char *data;
    ULONG  len;
    UWORD  rate;
}  buffer[NUMBUFS];

struct IOAudio *ioa = 0L;
struct MsgPort *port;
char *PortName;

/*
 * Phoneme file names.  The $ should be interpreted as a /
 */
char names[NUMBUFS][3] =
{
    "$C",
    "$H",
    "AA",
    "AE",
    "AH",
    "AO",
    "AW",
    "AX",
    "AY",
    "B",
    "CH",
    "D",
    "DH",
    "DX",
    "EH",
    "ER",
    "EY",
    "F",
    "G",
    "IH",
    "IL",
    "IM",
    "IN",
    "IX",
    "IY",
    "J",
    "K",
    "L",
    "LX",
    "M",
    "N",
    "NX",
    "OH",
    "OW",
    "OY",
    "P",
    "Q",
    "QX",
    "R",
    "RX",
    "S",
    "SH",
    "T",
    "TH",
    "UH",
    "UL",
    "UM",
    "UN",
    "UW",
    "V",
    "W",
    "WH",
    "Y",
    "Z",
    "ZH",
    "__",
};

/*
 * Start of program
 */
void main(argc,argv)
int argc;
char *argv[];
{  register int i;
   for (i = 0; i < NUMBUFS; i++)
       buffer[i].data = 0L;
   if (argc != 2)
      bye("Usage: talk file");

   hello(argv[1]);

   while (fgets(english,(int)BUFLEN,infile))
      GenPhonemeStr();
   bye("\0");

}

/*
 * GenPhonemeStr() is used to generate phoneme strings from 
 * english text.  It then calls GenPhoneme to generate 
 * individual phoneme references.
 */
GenPhonemeStr()
{  register long p,pp;
   int len;
   for (pp = 0; p; pp += p)
   {  len = strlen(&english[-pp])-1;
      p = Translate(&english[-pp],(long)len,phoneme,BUFLEN);
      if (p)
         Translate(&english[-pp],(long)-p,phoneme,BUFLEN);
      GenPhoneme(phoneme);
   }
}

#define PACK(a,b) ((((int)(a)) << 8) + ((int)(b)))

/*
 * GenPhoneme() separates a phoneme string into individual
 * phonemes.
 */
GenPhoneme(s)
char *s;
{  register char *current,*next;
   int value;
   printf("%s\n",s);
   for (current = s,next = &s[1]; *current; current++,next++)
   {   if (isdigit(*current))
       {  /*
           * stress value
	   */
	   value = *current - '0';
       }
       else
       {  switch(*current)
          {   case ' ':
	         value = 10;
                 break;
	      case 'B':   case 'F':   case 'G':   case 'J':   case 'K':
	      case 'M':   case 'P':   case 'V':   case 'Y':
	         value = PACK(*current,0);
	         break;
	      case 'Z':   case 'T':   case 'S':   case 'D':   case 'W':
	      case 'L':   case 'N':   case 'Q':   case 'R':
	         if (*next == 'H' || *next == 'X')
	         {  value = PACK(*current,*next);
		    current++;next++;
	         }
	         else
	         {  value = PACK(*current,0);
	         }
	         break;
	      case 'A':   case 'I':  case 'O':  case 'U':  case 'E':
	      case 'C':   case '/':
	         value = PACK(*current,*next);
	         current++;next++;break;
	      case '.':   case '?':  case '-':  case ',':  case '(':
	      case ')':
	         value = 11;
	         break;
	      default:
	         value = 12;
	         break;
          }
       }
       speak(value);
   }
}

/*
 *  speak(value) will speak the phoneme associated with the value.
 */
speak(value)
int value;
{  /*
    * 0-9 is stress value
    * 10 is pause
    * 11 is punctuation
    * 12 is error
    * all others are phoneme combinations
    */
    if (value < 10)
       return;
    switch(value)
    {   case PACK('/','C'):
           playbuf(0);
           break;
        case PACK('/','H'):
           playbuf(1);
           break;
        case PACK('A','A'):
           playbuf(2);
           break;
        case PACK('A','E'):
           playbuf(3);
           break;
        case PACK('A','H'):
           playbuf(4);
           break;
        case PACK('A','O'):
           playbuf(5);
           break;
        case PACK('A','W'):
           playbuf(6);
           break;
        case PACK('A','X'):
           playbuf(7);
           break;
        case PACK('A','Y'):
           playbuf(8);
           break;
        case PACK('B',0):
           playbuf(9);
           break;
        case PACK('C','H'):
           playbuf(10);
           break;
        case PACK('D',0):
           playbuf(11);
           break;
        case PACK('D','H'):
           playbuf(12);
           break;
        case PACK('D','X'):
           playbuf(13);
           break;
        case PACK('E','H'):
           playbuf(14);
           break;
        case PACK('E','R'):
           playbuf(15);
           break;
        case PACK('E','Y'):
           playbuf(16);
           break;
        case PACK('F',0):
           playbuf(17);
           break;
        case PACK('G',0):
           playbuf(18);
           break;
        case PACK('I','H'):
           playbuf(19);
           break;
        case PACK('I','L'):
           playbuf(20);
           break;
        case PACK('I','M'):
           playbuf(21);
           break;
        case PACK('I','N'):
           playbuf(22);
           break;
        case PACK('I','X'):
           playbuf(23);
           break;
        case PACK('I','Y'):
           playbuf(24);
           break;
        case PACK('J',0):
           playbuf(25);
           break;
        case PACK('K',0):
           playbuf(26);
           break;
        case PACK('L',0):
           playbuf(27);
           break;
        case PACK('L','X'):
           playbuf(28);
           break;
        case PACK('M',0):
           playbuf(29);
           break;
        case PACK('N',0):
           playbuf(30);
           break;
        case PACK('N','X'):
           playbuf(31);
           break;
        case PACK('O','H'):
           playbuf(32);
           break;
        case PACK('O','W'):
           playbuf(33);
           break;
        case PACK('O','Y'):
           playbuf(34);
           break;
        case PACK('P',0):
           playbuf(35);
           break;
        case PACK('Q',0):
           playbuf(36);
           break;
        case PACK('Q','X'):
           playbuf(37);
           break;
        case PACK('R',0):
           playbuf(38);
           break;
        case PACK('R','X'):
           playbuf(39);
           break;
        case PACK('S',0):
           playbuf(40);
           break;
        case PACK('S','H'):
           playbuf(41);
           break;
        case PACK('T',0):
           playbuf(42);
           break;
        case PACK('T','H'):
           playbuf(43);
           break;
        case PACK('U','H'):
           playbuf(44);
           break;
        case PACK('U','L'):
           playbuf(45);
           break;
        case PACK('U','M'):
           playbuf(46);
           break;
        case PACK('U','N'):
           playbuf(47);
           break;
        case PACK('U','W'):
           playbuf(48);
           break;
        case PACK('V',0):
           playbuf(49);
           break;
        case PACK('W',0):
           playbuf(50);
           break;
        case PACK('W','H'):
           playbuf(51);
           break;
        case PACK('Y',0):
           playbuf(52);
           break;
        case PACK('Z',0):
           playbuf(53);
           break;
        case PACK('Z','H'):
           playbuf(54);
           break;
	default:
	   /*
	    * Treat this as a pause for now....
	    */
           playbuf(55);
           break;
    }
}

/*
 * playbuf takes its index and plays the buffer specified.
 */
playbuf(idx)
int idx;
{   PlaySound(buffer[idx].data,buffer[idx].len,1,(int)(3579545/buffer[idx].rate),64);
    WaitIO(ioa);
}

/*
 * The initialization routine
 */
void hello(file)
char *file;
{  int i;
   if (!(TranslatorBase = OpenLibrary("translator.library",0L)))
      bye("Error opening translator device");
   if (!(infile = fopen(file,"r")))
   {  sprintf(errormsg,"Could not open \"%s\"",file);
      bye(errormsg);
   }
   for (i = 0; i < NUMBUFS; i++)
       loadsound(names[i],&buffer[i]);
   StartSound();
}

/*
 * The cleanup routine
 */
void bye(s)
char *s;
{  int i;
   if (s[0]) printf("%s\n",s);
   if (ioa) StopSound();
   for (i = 0; i < NUMBUFS; i++)
   {   if (buffer[i].data)
          FreeMem(buffer[i].data,buffer[i].len);
   }
   if (TranslatorBase) CloseLibrary(TranslatorBase);
   if (infile) fclose(infile);
   exit(0);
}

loadsound(filename,buffer)
char *filename;
struct SOUNDBUFFER *buffer;
{  FILE *fp;
   ULONG len;
   UWORD rec_rate;

   if (!(fp = fopen(filename,"r")))
   {  printf("Missing sound file : \"%s\"\n",filename);
      if (!(buffer->data = AllocMem(1000L,MEMF_CHIP|MEMF_CLEAR)))
         bye("Out of memory");

      buffer->rate = 10000;
      buffer->len  = 1000;
      return;
   }

   if (!fread(&len,sizeof(len),1,fp))
   {  fclose(fp);
      sprintf(errormsg,"Error reading sound file: \"%s\"",filename);
      bye(errormsg);
   }

   if (!fread(&rec_rate,sizeof(rec_rate),1,fp))
   {  fclose(fp);
      sprintf(errormsg,"Error reading sound file: \"%s\"",filename);
      bye(errormsg);
   }

   if (!(buffer->data = AllocMem(len,MEMF_CHIP|MEMF_CLEAR)))
   {  fclose(fp);
      bye("Out of memory");
   }

   buffer->rate = rec_rate;
   buffer->len  = len;

   if (!fread(buffer->data,len,1,fp))
   {  fclose(fp);
      sprintf(errormsg,"Error reading sound file: \"%s\"",filename);
      bye(errormsg);
   }
   fclose(fp);
}

#include "devices/audio.h"

#define LEFT0F     1
#define RIGHT0F    2
#define RIGHT1F    4
#define LEFT1F     8

UBYTE allocationMap[] = {LEFT0F,LEFT1F,RIGHT0F,RIGHT1F};

StartSound()
{
   if (!(ioa = AllocMem((long)(sizeof(struct IOAudio)),MEMF_PUBLIC|MEMF_CLEAR)))
      bye("Could not allocate IOAudio structure");
   ioa->ioa_Request.io_Message.mn_Node.ln_Pri = 10;
   PortName = "Sound Channel";
   if (!(port = CreatePort(PortName,0L)))
   {  FreeMem(ioa,(long)(sizeof(struct IOAudio)));
      bye("Could not create port");
   }
   ioa->ioa_Request.io_Message.mn_ReplyPort = port;
   ioa->ioa_Data = allocationMap;
   ioa->ioa_Length = sizeof(allocationMap);
   if (OpenDevice(AUDIONAME,0L,ioa,0L))
   {  DeletePort(port);
      FreeMem(ioa,(long)(sizeof(struct IOAudio)));
      bye("Could not open audio device");
   }
}

PlaySound(buffer,buflen,repeat,period,volume)
char *buffer;
ULONG buflen;
int repeat,period,volume;
{
    ioa->ioa_Request.io_Flags = ADIOF_PERVOL;
    ioa->ioa_Request.io_Command = CMD_WRITE;
    ioa->ioa_Period = period;
    ioa->ioa_Volume = volume;
    ioa->ioa_Cycles = repeat;
    ioa->ioa_Length = buflen;
    ioa->ioa_Data = (UBYTE *)buffer;
    BeginIO(ioa);
}

StopSound()
{
   AbortIO(ioa);
   if (ioa->ioa_Request.io_Device)
      CloseDevice(ioa);
   if (ioa->ioa_Request.io_Message.mn_ReplyPort)
      DeletePort(ioa->ioa_Request.io_Message.mn_ReplyPort);
   if (ioa)
      FreeMem(ioa,(long)(sizeof(struct IOAudio)));
}
