
                     /*--------------------------*|
                     |     TalkerII.c   10/01/90  |
                     |*--------------------------*/
 /*=====================================================================*/
 /*    Talker II Display Hack   --                                      */
 /*    VERSION 1                                         REVISION 0     */
 /*---------------------------------------------------------------------*/
 /*   This is the primary file for Talker II.                           */
 /* It encompasses all major routines, including those dealing with     */
 /* Message retrieval.                                                  */
 /*---------------------------------------------------------------------*/
 /*   Talker II is based around the example routines for using the      */
 /* narrator/translator devices from "Inside The Amiga". Because of     */
 /* this, even though I made changes to the original code (to make it   */
 /* work), I don't feel I can retain a copyright on it. Special thanks  */
 /* to Keith Young (Access!) for his help with dealing with command     */
 /* line arguments, along with much other assistance.                   */
 /*=====================================================================*/

 /*        Auto: cc -su TalkerII.c
 
 */

#define ERR 1L
#define SUCCESS 0L
#define REV 0L

#include "exec/types.h"
#include "exec/exec.h"
#include "intuition/intuition.h"
#include "intuition/intuitionbase.h"
#include "devices/keymap.h"
#include "hardware/blit.h"
#include "devices/narrator.h"
#include "libraries/translator.h"
#include "graphics/view.h"
#include "stdio.h"
#include "stdlib.h"
#include "clib/macros.h"
#include "faceII.h"
#include "eyesII.h"
#include "mouthyII.h"
#include "mouthxII.h"
#include "browsII.h"
#include "stdio.h"

ULONG mclass;
struct Image *newmouth,*neweyes;
char in_string[256], out_string[1024];

         /* select which audio channels to use */

UBYTE audio_chan[] = {
                      3,5,10,12
                     };

       struct Library *OpenLibrary();
       struct Library *TranslatorBase = NULL;
       struct MsgPort *voice_port; /* audio message port */
       struct MsgPort *mouth_port; /* video message port */
       struct mouth_rb *mouth_io;
       struct narrator_rb *voice_io;
       struct IntuitionBase *IntuitionBase = NULL;
       struct GfxBase   *GfxBase   = NULL;
       struct Screen *TalkerScreen = NULL;
       struct Window *TalkerWindow = NULL;
       struct ViewPort *Vprt       = NULL;
       struct RastPort *rast       = NULL;
       struct IntuiMessage *mesg   = NULL;


          /*====================================================*/
          /*  LookArray[][] is for determining which eye image  */
          /*  to show. The first 6 elements look left, the 2nd  */
          /*  6 elements look right.                            */
          /*====================================================*/

struct Image *LookArray[2][6] = {
                                 &eyes0_Image,&eyes3_Image,&eyes4_Image,
                                 &eyes4_Image,&eyes3_Image,&eyes0_Image,
                                 &eyes0_Image,&eyes5_Image,&eyes6_Image,
                                 &eyes6_Image,&eyes5_Image,&eyes0_Image
                                };

            /*=================================================*/
            /*  EyeArray[] is for determining which eye image  */
            /*  to show as the Talker blinks. Note that the    */
            /*  sequence is open, mid, closed, open. There is  */
            /*  NOT an element for mid as the eye reopens.     */
            /*  This appears more realistic, to me.            */
            /*=================================================*/

struct Image *EyeArray[4] = {
                             &eyes0_Image,
                             &eyes1_Image,
                             &eyes2_Image,
                             &eyes0_Image
                            };

             /*===============================================*/
             /*  HArray[] is used to determine which mouth    */
             /*  image to show. It is used for the sequences  */
             /*  where the height value is greater than the   */
             /*  width value.                                 */
             /*===============================================*/

struct Image *HArray[16] = {
                            &mouthx0_Image, &mouthx0_Image,
                            &mouthx1_Image, &mouthx3_Image,
                            &mouthx3_Image, &mouthx4_Image,
                            &mouthx5_Image, &mouthx6_Image,
                            &mouthx7_Image, &mouthx7_Image,
                            &mouthx8_Image, &mouthx9_Image,
                            &mouthx10_Image, &mouthy11_Image,
                            &mouthy11_Image, &mouthy12_Image 
                           };


             /*===============================================*/
             /*  WArray[] is used to determine which mouth    */
             /*  image to show. It is used for the sequences  */
             /*  where the width value is greater than the    */
             /*  height value.                                */
             /*===============================================*/

struct Image *WArray[16] = {
                            &mouthy0_Image, &mouthy0_Image,
                            &mouthy1_Image, &mouthy2_Image,
                            &mouthy3_Image, &mouthy3_Image,
                            &mouthy4_Image, &mouthy5_Image,
                            &mouthy6_Image, &mouthy7_Image,
                            &mouthy7_Image, &mouthy8_Image,
                            &mouthy9_Image, &mouthy10_Image,
                            &mouthy11_Image, &mouthy12_Image 
                           };

             /*===============================================*/
             /*  BArray[] is used to determine which mouth    */
             /*  image to show. It is used for the sequences  */
             /*  where the height value is equal to the       */
             /*  width value.                                 */
             /*===============================================*/

struct Image *BArray[16] = {
                            &mouthx0_Image, &mouthx1_Image,
                            &mouthx2_Image, &mouthy3_Image,
                            &mouthy4_Image, &mouthy5_Image,
                            &mouthy6_Image, &mouthy7_Image,
                            &mouthy8_Image, &mouthy8_Image,
                            &mouthy9_Image, &mouthy9_Image,
                            &mouthy10_Image, &mouthy11_Image,
                            &mouthy12_Image, &mouthy12_Image 
                           };

                   /*===================================*/
                   /*  old_palette is a pointer to the  */
                   /*  allocated memory for palette[]   */
                   /*  and colourarray[]                */
                   /*===================================*/

       UWORD         *old_palette  = NULL;

                 /*======================================*/
                 /*  Palette[] (note the capital P) is   */
                 /*  used to hold the values of my 5     */
                 /*  palette colours for Talker's face.  */
                 /*======================================*/

USHORT Palette[] = {
   0x0620,   /* color #8 */
   0x0c85,   /* color #9 */
   0x0ea8,   /* color #10 */
   0x0fb9,   /* color #11 */
   0x0841,   /* color #12 */
#define PaletteColorCount 5
};

#define PALETTE Palette

static struct NewWindow NewWindowStructure1 = {
	400,100,	/* window XY origin relative to TopLeft of screen */
	100,86, /* window width and height */
	0,1,	/* detail and block pens */
	CLOSEWINDOW+DISKINSERTED+DISKREMOVED+INTUITICKS+ACTIVEWINDOW, /* IDCMP */
	WINDOWDRAG+WINDOWCLOSE+WINDOWDEPTH+
   WINDOWACTIVE+ACTIVATE+SMART_REFRESH, /* window flags */
	NULL,	/* first gadget in gadget list */
	NULL,	/* custom CHECKMARK imagery */
	(UBYTE *)"][",	/* window title */
	NULL,	/* custom screen pointer */
	NULL,	/* custom bitmap */
	100,86, /* minimum width and height */
	-1,-1, /* maximum width and height  */
	WBENCHSCREEN	/* destination screen type */
};
      /* end of PowerWindows source generation */


               /*===========================================*/
               /*    CloseWind()  --                        */
               /*-------------------------------------------*/
               /*      This routine is the program's final  */
               /*  exit point. This shuts down all opened   */
               /*  windows, ports, and deallocates all      */
               /*  allocated memory.                        */
               /*===========================================*/

VOID
CloseWind(errorstring)
 char *errorstring;
 {
  if(errorstring)
   {
    puts(errorstring);
   };

  if(voice_port)
   {
    DeletePort(voice_port);
   };

  if(mouth_port)
   {
    DeletePort(mouth_port); 
   };

  if(voice_io)
   {
    CloseDevice(voice_io); 
    DeleteExtIO(voice_io); 
   };

  if(mouth_io)
   {
    DeleteExtIO(mouth_io);
   };

  if(TalkerWindow)
   {
    if(old_palette != NULL)
     {
      restore_screen(old_palette);
      FreeMem(old_palette,(long)(16 * sizeof(UWORD)));
     };

    while( mesg = (struct IntuiMessage *)GetMsg(TalkerWindow->UserPort) )
         ReplyMsg(mesg);
    CloseWindow(TalkerWindow);
   };

  exit(0);
 }


           /*===================================================*/
           /*    changestring()   --                            */
           /*---------------------------------------------------*/
           /*      This routine translates the string           */
           /*  passed to it into something the narrator.device  */
           /*  can handle. "in" points to the incoming string,  */
           /*  "out" points to the outgoing string. "inlen"     */
           /*  is the length of the incoming string, "outlen"   */
           /*  is the length of the outgoing string.            */
           /*===================================================*/

VOID
changestring(in,inlen,out,outlen)
 UBYTE *in,*out;
 SHORT inlen,outlen;
 {
  if(Translate(in,inlen,out,outlen))
   {
    CloseWind("translation error");
   };

 }


            /*================================================*/
            /*    Blink()  --                                 */
            /*------------------------------------------------*/
            /*      This routine handles the blinking of the  */
            /*  eyes and controls the speed of the blink.     */
            /*================================================*/

VOID 
Blink(pauseme)
ULONG pauseme;
 {
  int x;
  for(x = 0; x < 4; x++)
   {
    neweyes = EyeArray[x];
    WaitTOF();
    DrawImage(rast,neweyes,7,15);
    Delay(pauseme);
    };

 }


                 /*=======================================*/
                 /*    Closemouth()  --                   */
                 /*---------------------------------------*/
                 /*      This routine closes the mouth    */
                 /*  after a 'sentence' has been spoken.  */
                 /*  It determines which array to use,    */
                 /*  based upon height vs width.          */
                 /*=======================================*/

VOID
Closemouth(high,wide)
UBYTE high,wide;
 {
  ULONG x;
  if(high > wide)
   {
    for(x = high; x > 0; x--)
      {
       WaitTOF();
       DrawImage(rast,HArray[x],7,15);
      };
   };

  if(wide >= high)
   {
    for(x = wide; x > 0; x--)
      {
       WaitTOF();
       DrawImage(rast,WArray[x],7,15);
      };
   };

 }


           /*==================================================*/
           /*    NewPalette()  --                              */
           /*--------------------------------------------------*/
           /*      This routine sets up the 5 color registers  */
           /*  used to make the colors of the face. Note that  */
           /*  colors 1 and 2 are used as as well as colors    */
           /*  8-12. Color 1 must be black, color 2 must be    */
           /*  white or it'll look weird.                      */
           /*==================================================*/

VOID
NewPalette()
 {
 long x;
 UWORD discolour;
 for(x = 0;x < 5;x++)
   {
    discolour = Palette[x];
    SetRGB4(Vprt,x + 8,((discolour&0xf00)>>8),(discolour&0xf0)>>4,
           (discolour&0x0f));
   };

 }


               /*===========================================*/
               /*     HandleEvent()  --                     */
               /*-------------------------------------------*/
               /*   This routine deals with all events that */
               /* are captured with GetEvents(). It first   */
               /* copies the message into variables, and    */
               /* then calls ReplyMsg() to free up the msg  */
               /* port. It is called from GetEvents(),      */
               /* and returns nothing.                      */
               /*===========================================*/

VOID
HandleEvent(EventMessage)
 struct IntuiMessage *EventMessage;
 {
  ULONG  class;
  USHORT code, qual;
  struct Gadget   *address;
  SHORT  MooseX,MooseY;
  struct Window  *disone;

  class     = EventMessage->Class;
  code      = EventMessage->Code;
  qual      = EventMessage->Qualifier;
  disone    = EventMessage->IDCMPWindow;
  address   = (struct Gadget *)EventMessage->IAddress;
  MooseX    = EventMessage->MouseX;
  MooseY    = EventMessage->MouseY;

  ReplyMsg((struct Message *)EventMessage);

  if(class == DISKINSERTED)
   {
    restore_screen(old_palette);
    NewPalette();
   };

  if(class == DISKREMOVED)
   {
    blank_screen();
   };

  if(class == CLOSEWINDOW)
   {
    CloseWind("We're outta here.\n");
   };

 }


              /*=============================================*/
              /*    GetEvents()                              */
              /*---------------------------------------------*/
              /*       This routine is the loop for getting  */
              /*  and processing Window IDCMP events. It is  */
              /*  affected by clicking on the close gadget,  */
              /*  and by inserting or removing a diskette.   */
              /*  It is called by, and therefore is only     */
              /*  effective through, LookAround(), restore_  */
              /*  screen() and blank_screen.                 */
              /*=============================================*/

VOID
GetEvents()
 {
  struct IntuiMessage *EventMsg;
  WaitPort(TalkerWindow->UserPort);
  while(EventMsg = (struct IntuiMessage *)GetMsg(TalkerWindow->UserPort))
       HandleEvent(EventMsg);
 }


   /*==================================================================*/
   /*    LookAround()  --                                              */
   /*------------------------------------------------------------------*/
   /*      This routine makes the Talker look around awhile. You pass  */
   /*  a value for how long you want it to look around, it uses that   */
   /*  as a seed for the random number generator, and then generates   */
   /*  a random number to determine which way the Talker will look.    */
   /*  The random value will be either 0 or 1 because of the modulo    */
   /*  performed on it. This value is plugged in as the first          */
   /*  dimension of the array LookArray[][], and then a for loop       */
   /*  determines the 2nd dimension This walks you halfway through the */ 
   /*  array, either to the left (number equals zero) or to the right  */
   /*  (number equals 1). It also calls GetEvents() in the middle of   */
   /*  this loop, to determine if the user wishes to blank the screen  */
   /*  (or restore it).                                                */
   /*==================================================================*/
     
VOID
LookAround(waiter)
 int waiter;
 {
 int iterations,disimage,whichway;
 struct Image *newlook;
  srand(waiter);
  for(iterations = 0;iterations < waiter;iterations++)
    {
     whichway = rand() % 2;
     for(disimage = 0;disimage < 6;disimage++)
       {
        newlook = LookArray[whichway][disimage];
        WaitTOF();
        DrawImage(rast,newlook,7,15);
        if(disimage == 3)
         {
          Delay(((rand() % 50) + 1) * 2);
         };

        GetEvents();
       };

     if(!(rand() % 10))
      {
       Blink(2);
      };

     Delay(((rand() % 100) + 1) * 2);
    };

 }


             /*==============================================*/
             /*   talk_to_me()  --                           */
             /*----------------------------------------------*/
             /*      This routine does the actual output of  */
             /*  speech. It sets up the parameters of the    */
             /*  narrator_rb and mouth_rb structures to      */
             /*  output a 'sentence' of speech, as well as   */
             /*  the 'format' of that speech. It then gets   */
             /*  the data for the shape of the Talker's      */
             /*  mouth. That data is a value from 0 to 15    */
             /*  for width, and a value from 0 to 15 for     */
             /*  height.                                     */
             /*==============================================*/

VOID
talk_to_me(speech)
char *speech;
 {
  voice_io->message.io_Data = (APTR)speech;
  voice_io->message.io_Length = strlen(speech);
  voice_io->ch_masks = audio_chan;
  voice_io->nm_masks = sizeof(audio_chan);

  voice_io->mouths   =     1;
  voice_io->volume   =    64;
  voice_io->rate     =   150;
  voice_io->sex      = MALE;
  voice_io->pitch    = DEFPITCH;
  voice_io->sampfreq = 23000;
  mouth_io->voice.message.io_Error = 0;

  SendIO(voice_io);

  while(mouth_io->voice.message.io_Error == 0)
      {
       DoIO(mouth_io);
       if(mouth_io->height >= mouth_io->width)
        {
         newmouth = HArray[mouth_io->height];
        }
       else
         {
          newmouth = WArray[mouth_io->width];
         };

       WaitTOF();
       DrawImage(rast,newmouth,7,15);
      }

  WaitIO(voice_io);
  Closemouth(mouth_io->height,mouth_io->width);
  if(!(rand() % 100))
   {
    LookAround(rand());
   };

  if(!(rand() % 10))
   {
    Blink(2);
   };

 }

             /*===============================================*/
             /*    prep_to_talk()  --                         */
             /*-----------------------------------------------*/
             /*      This routine initializes the system to   */
             /*  speak. It creates and initializes the audio  */
             /*  message port, and the 'video' message port,  */
             /*  the port we use to get the mouth shapes.     */
             /*===============================================*/

int
prep_to_talk()
{
 if((voice_port = (struct MsgPort *)CreatePort(0,0)) == NULL)
  {
   return(ERR);
  };

 if((mouth_port = (struct MsgPort *)CreatePort(0L,0L)) == NULL)
  {
   return(ERR);
  };

 if((voice_io = (struct narrator_rb *)
 CreateExtIO(voice_port,sizeof(struct narrator_rb))) == NULL)
  {
   return(ERR);
  };

 if((mouth_io = (struct mouth_rb *)
 CreateExtIO(mouth_port,sizeof(struct mouth_rb))) == NULL)
  {
   return(ERR);
  };

 voice_io->message.io_Command=CMD_WRITE;

 if(OpenDevice("narrator.device",0L,voice_io,0L) != 0L)
  {
   return(ERR);
  };

 mouth_io->voice.message.io_Device=voice_io->message.io_Device;
 mouth_io->voice.message.io_Unit=voice_io->message.io_Unit;
 mouth_io->width = 0;
 mouth_io->height = 0;
 mouth_io->voice.message.io_Command = CMD_READ;
 mouth_io->voice.message.io_Error = 0;
 return(SUCCESS);

}

         /*======================================================*/
         /*    OpenLibs()  --                                    */
         /*------------------------------------------------------*/
         /*      This routine opens all the libraries accessed   */
         /*  by the program. It bails out with an error message  */
         /*  if it cannot open any of the libraries.             */
         /*======================================================*/

VOID
OpenLibs()
 {
  TranslatorBase = OpenLibrary("translator.library",REV);
  if(TranslatorBase == NULL)
   {
    CloseWind("couldn't open translator library");
   };

  IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
  if(IntuitionBase == NULL)
   {
    CloseWind("couldn't open Intuition library");
   };

  GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0);
  if(GfxBase == NULL)
   {
    CloseWind("couldn't open Graphics library");
   };

 }



int
Preserve_Colours(colourarray)
 UWORD colourarray[16];
 {
 long x;
  for(x = 0;x < 16; x++)
    {
     if((colourarray[x] = GetRGB4(Vprt->ColorMap,x)) < 0)
      {
       CloseWind("couldn't save old palette, sorry");
      };
    };

  return(1);
 }

        /*=========================================================*/
        /*    restore_screen() --                                  */
        /*---------------------------------------------------------*/
        /*      This routine restores the workbench's _original_   */
        /*  palette in its entirety. Which means that the colors   */
        /*  used to draw the Talker will revert to their original  */
        /*  values. It does the restore by incrementing the value  */
        /*  of 16 color registers until each is equal to its       */
        /*  original value, preserved (via PreserveColours()) by   */
        /*  the array pointed at by old_palette.                   */
        /*=========================================================*/

restore_screen(palette)
 UWORD palette[16];
 {
 long discolour,x,y;
  for(x = 0;x < 16;x++)
    {
     for(y = 0;y < 16;y++)
       {
        discolour = GetRGB4(Vprt->ColorMap,y);
        discolour += 273;

        if(discolour > (long)palette[y])
         {
          discolour = (long)palette[y];
         };

        SetRGB4(Vprt,y,((discolour&0xf00)>>8),(discolour&0xf0)>>4,
               (discolour&0x0f));
       };

     Delay(x);
    };
 }

            /*=================================================*/
            /*    blank_screen()  --                           */
            /*-------------------------------------------------*/
            /*      This routine fades the screen colours      */
            /*  to black. Note that this does not affect the   */
            /*  pointer, because the colour registers          */
            /*  themselves are unaffected (I think that's      */
            /*  why :). It works the opposite of restore_      */
            /*  screen(), in that colors are decremented       */
            /*  til they reach zero. Note the majick number:   */
            /*  273, which is 0x0111 hex, or 0000000100010001  */
            /*  in binary. Since the r-g-b values are stored   */
            /*  with r having a value of 0-15 times 2^8th, g   */
            /*  having a value of 0-15 times 2^4th, and then   */
            /*  b having a value of 0-15 times 2^0th, in a     */
            /*  long value (top 4 bits are ignored), the       */
            /*  majick number will subtract 1 from each r-g-b  */
            /*  value. Subtracting one would only change the   */
            /*  b value, which will mess things up once you    */
            /*  do the shift (in SetRGB4()).                   */
            /*=================================================*/

blank_screen()
 {
 long discolour,x,y;
  for(x = 0;x < 16;x++)
    {
     for(y = 0;y < 16;y++)
       {
        discolour = GetRGB4(Vprt->ColorMap,y);
        discolour -= (long)273;

        if(discolour < (long)0)
         {
          discolour = (long)0;
         };

        SetRGB4(Vprt,y,((discolour&0xf00)>>8),(discolour&0xf0)>>4,
               (discolour&0x0f));
       };

     Delay(x);
    };
 }


          /*====================================================*/
          /*    OpenWind()  --                                  */
          /*----------------------------------------------------*/
          /*      This routine opens the window which contains  */
          /*  the Talker imagery. It also allocates the memory  */
          /*  for the array of values for the old palette, and  */
          /*  points old_palette at it. It then saves the  old  */
          /*  colours (via Preserve_Colours()), and sets up a   */
          /*  new palette using the 5 colours in Palette[]. It  */
          /*  then draws the initial facial image.              */
          /*====================================================*/

VOID
OpenWind()
 {
  if((TalkerWindow = (struct Window *)OpenWindow(&NewWindowStructure1)) == NULL)
   {
    CloseWind("Couldn't Open window");
   };

  Vprt = (struct ViewPort *)ViewPortAddress(TalkerWindow);
  if((old_palette = (UWORD *)AllocMem(sizeof(UWORD) * 16,MEMF_CLEAR)) != NULL)
   {
    if(Preserve_Colours(old_palette) == NULL)
     {
      CloseWind("Can't restore old palette");
     };

      NewPalette();
   };
  rast = TalkerWindow->RPort;
  WaitTOF();
  DrawImage(rast,&face0_Image,7,15);

 }




                   /*===================================*/
                   /*    enter_text()  --               */
                   /*-----------------------------------*/
                   /*      This routine is called when  */
                   /*  TalkerII is invoked without any  */
                   /*  command line arguments. It       */
                   /*  prompts the user for text to be  */
                   /*  spoken, then speaks that text.   */
                   /*===================================*/

VOID
enter_text()
 { 
  Delay(25);
  printf("enter string: ");
  gets(in_string);
  Delay(10);
  Blink(10L);
  changestring(in_string,strlen(in_string),out_string,1024L);
  Delay(5);
  Blink(5L);
  talk_to_me(out_string);

 }


                /*=========================================*/
                /*    say_file()  --                       */
                /*-----------------------------------------*/
                /*      This routine speaks the text in a  */
                /*  file. It opens the file given in the   */
                /*  command line argument(s), translates   */
                /*  the text, and outputs it.              */
                /*=========================================*/

VOID
say_file(name)
char *name;
 {
  FILE   *f;      /* file pointer */
  if((f = fopen(name,"r")) == NULL)
   {
    CloseWind("can't open file\n" );
   };
  while(fgets(in_string,255,f) != 0L)
      {
       changestring(in_string,(long)strlen(in_string),out_string,1024L); 
       talk_to_me(&out_string);
      };

 }


                /*========================================*/
                /*    Howlong()  --                       */
                /*----------------------------------------*/
                /*      This routine determines how long  */
                /*  the Talker should look around before  */
                /*  speaking the file.                    */
                /*========================================*/

int
Howlong(timerstring)
 char *timerstring;
 {
 int i;
  i = atoi((timerstring + 2));
  return(i);
 }


main(argc,argv)
 int argc;
 char *argv[];
 {
  OpenLibs();
  OpenWind();
  prep_to_talk();

  if(!argv[1])
   {
    enter_text();
   }
  else if(strncmp(argv[1],"-l",2) == NULL)
        {
         LookAround(Howlong(argv[1]));
         Blink(5);
         if(argv[2])
          {
           say_file(argv[2]);
          };
        }
      else
        {
         Blink(5);
         say_file(argv[1]);
        };

  Delay(25);
  CloseWind(NULL);
 }
