/* SpeechToy.c  Original by Dave Lucas, Enhancements by Rob Peck.
 *
 * o  Can now read and write its own icon to get the defaults you set.
 * o  New menu item added to save current speech settings.
 * o  Can be started from WBENCH or CLI; if from CLI, can still
 *    read the icon file to get the default values.
 *
 * In the toolstrings, the data takes the form:
 *
 * SEX=1
 * SEX=0
 * for female (1) or male (0) voice.
 *
 * MODE=0
 * MODE=1
 * for expressive (0) or robotic (1) voice.
 *
 * FREQUENCY=20000
 * numeric range from sampling frequency MIN to 
 * frequency MAX (see the audio.h file)
 * 
 * RATE=150
 * words per minute rate, see audio.h for range.
 *
 * VOLUME=64
 * volume range from 0 to 64 (max).
 *
 * PITCH=224
 * pitch value for speech, range see audio.h
 *
 * These values can be saved in the icon by using the save menu item
 * --------------------------------------------------------------------
 *
 * WORDS=This is Amiga Speaking
 * anything you want it to insert in the English Phrase area.
 *
 * TRANS=1
 * means: Translate whatever words there are in the english.
 *
 * SPEAK=1
 * means: Say it.  When this is in the icon, it opens up talking.
 *
 * PHON=DHIHS IHZ AHMIY3GAH SPIY4KIHNX.
 * This is the way you insert your own custom pronunciation.
 * Note that it MUST be legal phonetics per the Speech docs.
 */

/* AREXX support:
 *
 * AREXX port is named SPEECHTOY, so that AREXX, which capitalizes
 * things automatically, doesn't need quotes on the name to
 * prevent it from capitalizing and not finding the port.
 *
 * To change any parameter, do EXACTLY as the toolstrings description
 * shows... I use the SAME routines to parse the command that AREXX
 * sends as are used to decode the icon's toolstrings.
 *
 * To send a command directly from a CLI interface, using AREXX
 * support function, do this:
 *
 * rx "address speechtoy 'WORDS=Say this you turkey'"
 * rx "address speechtoy 'TRANS=1'"
 * rx "address speechtoy 'SPEAK=1'"
 */

/* CLI startup:
 *
 * To allow speechtoy to read its own icon, either CD to the
 * directory where speechtoy is located, then:  run speechtoy
 * OR CD anywhere, then:
 *
 * 	run speechtoy <dir-where-icon-is-located>
 *
 * example:
 *
 * CD DF0:
 * RUN df1:speechtoy df1:
 */

/* COMPILING:  Lattice - compiles cleanly under Lattice 3.03 and 4.0
 *             if stack is set to 20000
 *
 *             Manx 3.4b compiles cleanly, used for this object file.
 *
 * Original LUCAS comments (for most part) removed, see the FISH
 * disk with original speechtoy to see what was there before.
 * File got up over 65k source when I was through with it.
 *
 * Enhancements needed:  Add capability to input PHONETIC strings.
 *                       Strip it down into just a speech server
 *                       with no windowing interface, but still read
 *                       the icon file for the defaults.
 */

/* Written by David M Lucas. */
/* Enhanced by Rob Peck */

/* If you find somthing you don't like, fix it! Have fun! */
/* Send complaints to /dev/null. really. */
/* Thanks Amiga. */

#include "exec/types.h"
#include "exec/exec.h"
#include "intuition/intuition.h"
#include "intuition/intuitionbase.h"
#include "devices/narrator.h"
#include "libraries/translator.h"
#include "exec/memory.h"
#include "libraries/dosextens.h"
#include "workbench/workbench.h"
#include "workbench/icon.h"
#include "workbench/startup.h"

#undef LIBRARY_VERSION
#undef LIBRARY_VERSION
#define LIBRARY_VERSION 0

/* forward declarations and external functions */

extern struct Library *OpenLibrary();
extern APTR AllocMem();
extern struct FileLock *CurrentDir();
extern struct DiskObject *GetDiskObject();
extern struct Task *FindTask();
extern struct Message *GetMsg();
extern long   WriteIcon();
extern struct Window *OpenWindow();
extern void Delay();

char **toolsave;	/* place to save the old pointer */
char *toolstrings[7];   /* a new set of toolstrings to use for writing */

struct MyDefaults {
	int freq; int rate; int pitch;
	int vol;  int sex;  int mode;
};

long speakdone;		/* if TRUE, (nonzero), voice_io is back */

struct MyDefaults Defaults;

struct Library *IconBase;
extern struct DiskObject *ReadInfoFile();
void menumessage();
void gadgetmessage();
void DrawFace();
void freeimages();

/* #define DEBUG */

#define CONTWINDW 321
#define CONTWINDH 123
#define FACEWINDW 32
#define FACEWINDH 44

/* Pen numbers to draw gadget borders/images/text with */
#define REDP   3
#define BLKP   2
#define WHTP   1
#define BLUP   0

/* the length of the English and phonetic buffers */
#define ESTRINGSIZE 512
#define PSTRINGSIZE 768 /* phonemes are longer than english */

#define NUMPROPS 4

/* Ranges of proportional data */
#define RNGFREQ  (MAXFREQ  - MINFREQ)  +1
#define RNGRATE  (MAXRATE  - MINRATE)  +1
#define RNGPITCH (MAXPITCH - MINPITCH) +1
#define RNGVOL   (MAXVOL   - MINVOL)   +1

#define DEFFREQ2    Defaults.freq
#define DEFRATE2    Defaults.rate
#define DEFPITCH2   Defaults.pitch
#define DEFVOL2     Defaults.vol
#define DEFSEX2     Defaults.sex
#define DEFMODE2    Defaults.mode


/* This section is part of the AREXX distribution disk,
 * Copyright 1987, 1988 W. Hawes, reprinted by permission.
 */

/* This structure is used for passing arguments to external programs.
 * It is usually passed as an "argstring", a pointer to the string buffer.
 */
struct RexxArg {
   LONG     ra_Size;    /* total allocated length        */
   UWORD    ra_Length;  /* length of string              */
   UBYTE    ra_Flags;   /* attribute flags               */
   UBYTE    ra_Hash;    /* hash code                     */
   BYTE     ra_Buff[8]; /* buffer area                   */
   };                   /* size: 16 bytes (minimum)      */

/* The RexxMsg structure is used for all communications with Rexx programs.
 * It is an EXEC message with a parameter block appended.
 */

struct RexxMsg {
   struct Message rm_Node;  /* EXEC message structure        */
   APTR     rm_TaskBlock;   /* pointer to global structure   */
   APTR     rm_LibBase;     /* library base                  */
   LONG     rm_Action;      /* command (action) code         */
   LONG     rm_Result1;     /* primary result (return code)  */
   LONG     rm_Result2;     /* secondary result              */
   STRPTR   rm_Args[16];    /* argument block (ARG0-ARG15)   */

   struct MsgPort *rm_PassPort; /* forwarding port               */
   STRPTR   rm_CommAddr;        /* host address (port name)      */
   STRPTR   rm_FileExt;         /* file extension                */
   LONG     rm_Stdin;           /* input stream (filehandle)     */
   LONG     rm_Stdout;          /* output stream (filehandle)    */
   LONG     rm_avail;           /* future expansion              */
   };                           /* size: 128 bytes               */
/* end of selected portion of rexx/storage.h */

/* This is a dummy array, identical in form to the toolstrings array.
 * Only one pointer (the first one) gets initialized to point to
 * the argstring for the command that is transmitted by AREXX.
 * The second pointer is NULL, ending what looks like a toolstrings
 * array.  This way we can use a single routine to obey both icon
 * toolstring commands and AREXX commands.
 */

char *rexxstrings[2];

struct DiskObject *diskobj;

struct TextAttr TestFont = /* Needed for opening screen */
   {
   (STRPTR)"topaz.font",
   TOPAZ_EIGHTY, 0, 0
   };

/* Which audio channels to use */
UBYTE audio_chan[] = {3, 5, 10, 12};
/* Pointer to translator library vectors */
struct Library *TranslatorBase = 0;
struct MsgPort talk_port; /* Message port for the say's I/O  */
struct MsgPort read_port; /* Message port for the say's I/O  */


struct MsgPort *rexxport, *CreatePort();  /* added by RAP */


struct mouth_rb mouth_io;  /* IO Request block, mouth flavor */
/* IO Request block, narrator flavor */
struct narrator_rb voice_io;
/* indicative of the Open return */
UBYTE NarratorOpenError = -1;		/* not yet opened */
/* indicative of a Translations success */
UBYTE TranslatorError = 0;
USHORT i;

/* These are used to draw the eyes and mouth size relative */
USHORT MouthWMult;
USHORT EyesLeft;
USHORT MouthHMult;
USHORT EyesTop;
USHORT EyesBottom;
USHORT YMouthCenter;  /* Pixels from top edge */
USHORT XMouthCenter;  /* Pixels from left edge */
USHORT yaw;
USHORT LipWidth, LipHeight;

/* String Gadgets */

USHORT StrVectors[] = {
   0, 0,   297, 0,   297, 14,   0, 14,
   0, 1,   296, 1,   296, 13,   1, 13,
   1, 1
};
struct Border StrBorder = {
   -4, -3,           
   WHTP, BLUP, JAM1,
   9,               
   StrVectors,
   NULL
};

/* The same undo buffer is used for both string gadgets,
 * this is sized to largest so that largest fits.
 */
UBYTE UndoBuffer[PSTRINGSIZE];

/* default text */
UBYTE EnglBuffer[ESTRINGSIZE] = "This is amiga speaking.";
struct StringInfo EnglInfo = {
   EnglBuffer,
   UndoBuffer,
   0,         
   ESTRINGSIZE,
   0, 0,
   24,
   0, 0, 0,
   NULL,
   0,
   NULL
};
struct IntuiText EnglText = {
   WHTP, BLUP,
   JAM1,
   0, -13,
   &TestFont,
   "English:",
   NULL
};
struct Gadget EnglStrGadget = {
   NULL,
   11, 63, 290, 10,
   GADGHCOMP,
   RELVERIFY,
   STRGADGET,
   (APTR)&StrBorder,
   NULL,
   &EnglText,
   0,
   (APTR)&EnglInfo,
   0,
   NULL
};

/* Phonetic string gadget is where the program puts the
 * translated string, necessating a call to RefreshGadgets(),
 * and is where the user can type in Phonemes.
 */
UBYTE PhonBuffer[PSTRINGSIZE] =
  "DHIHS IHZ AHMIY3GAH SPIY4KIHNX.";
struct StringInfo PhonInfo = {
   PhonBuffer,
   UndoBuffer,
   0,
   PSTRINGSIZE,
   0, 0,
   32,
   0, 0, 0,
   NULL,
   NULL,
   NULL
};
struct IntuiText PhonText = {
   WHTP, BLUP,
   JAM1,
   0, -13,
   &TestFont,
   "Phonetics:",
   NULL
};
struct Gadget PhonStrGadget = {
   &EnglStrGadget,
   11, 94, 290, 10,
   GADGHCOMP,
   RELVERIFY,
   STRGADGET,
   (APTR)&StrBorder,
   NULL,
   &PhonText,
   0,
   (APTR)&PhonInfo,
   0,
   NULL
};
/* Female Toggle (Highlight Image)
   (Quasi mutual exclude with Male Toggle) */
struct Image FemaleImage = {
   0, 0,
   20, 10, 1,
   NULL,
   0x1, 0x0
};
struct Gadget FemaleGadget = {
   &PhonStrGadget,
   134, 34, 20, 10,
   GADGIMAGE | GADGHCOMP,
   /* Activation flags */
   RELVERIFY | GADGIMMEDIATE | TOGGLESELECT,
   BOOLGADGET,
   (APTR)&FemaleImage,
   NULL,
   NULL,
   0,
   NULL,
   0,
   NULL
};

/* Male Toggle (Highlight Image)
   (Quasi mutual Exclude with above) */
struct Image MaleImage = {
   0, 0,
   20, 10, 1,
   NULL,
   0x1, 0x0
};
struct Gadget MaleGadget = {
   &FemaleGadget,
   154, 34, 20, 10,
   GADGIMAGE | GADGHCOMP | SELECTED,
   /* Activation flags */
   RELVERIFY | GADGIMMEDIATE | TOGGLESELECT | ACTIVATE,
   BOOLGADGET,
   (APTR)&MaleImage,
   NULL,
   NULL,
   0,
   NULL,
   0,
   NULL
};

/* This boolean toggle gadget has an
 * alternate image that indicates
 * selection. The image stays flipped
 * until it gets another hit. (it toggles)
 */
/* Inflection Mode Toggle (AltImage) *************************/
struct Image HumanImage = {
   0, 0,
   40, 20, 1,
   NULL,
   0x1, 0x0
};
struct Image RobotImage = {
   0, 0,
   40, 20, 1,
   NULL,
   0x1, 0x0
};
struct Gadget ModeGadget = {
   &MaleGadget,
   134, 2, 40, 20,
   GADGIMAGE | GADGHIMAGE,
   RELVERIFY | GADGIMMEDIATE | TOGGLESELECT,
   BOOLGADGET,
   (APTR)&HumanImage,
   (APTR)&RobotImage,
   NULL,
   0,
   NULL,
   0,
   NULL
};

/* Face Toggle (image and text) */
struct IntuiText FaceIText = {
   WHTP, BLUP,
   JAM2,
   4, 1,
   &TestFont,
   "Face",
   NULL
};
struct Image FaceImage = {
   0, 0,
   40, 10, 1,
   NULL,
   0x1, 0x0
};
struct Gadget FaceGadget = {
   &ModeGadget,
   134, 23, 40, 10,
   GADGIMAGE | GADGHCOMP,
   RELVERIFY | GADGIMMEDIATE | TOGGLESELECT,
   BOOLGADGET,
   (APTR) &FaceImage,
   NULL,
   &FaceIText,
   0,
   NULL,
   0,
   NULL
};

/* Stop Hit (image and text) */
struct IntuiText StopIText = {
   WHTP, BLUP,
   JAM2,
   4, 1,
   &TestFont,
   "Stop",
   NULL
};
struct Image StopImage = {
   0, 0,
   40, 10, 1,
   NULL,
   0x1, 0x0
};
struct Gadget StopGadget = {
   &FaceGadget,
   134, 45, 40, 10,
   GADGIMAGE | GADGHCOMP,
   RELVERIFY | GADGIMMEDIATE,
   BOOLGADGET,
   (APTR) &StopImage,
   NULL,
   &StopIText,
   0,
   NULL,
   0,
   NULL
};

/* This is a hit (as opposed to toggle)
   gadget that starts the translation.*/
/* Translate Hit (Highlight image) */
USHORT TransVectors[] = {
   0, 0,    79, 0,    79, 13,   0, 13,
   0, 1,    78, 1,    78, 12,   1, 12,
   1, 1
};
struct Border TransBorder = {
   -4, -3,
   WHTP, BLUP, JAM1,
   9,
   TransVectors,
   NULL
};
struct IntuiText TranslateIText = {
   WHTP, BLUP,
   JAM2,
   0, 0,
   &TestFont,
   "Translate",
   NULL
};
struct Gadget TranslateGadget = {
   &StopGadget,
   229, 48, 71, 8,
   GADGHCOMP,
   RELVERIFY | GADGIMMEDIATE,
   BOOLGADGET,
   (APTR)&TransBorder,
   NULL,
   &TranslateIText,
   0,
   NULL,
   0,
   NULL
};

/* This is a hit (as opposed to toggle) Starts the narration */
/* Speak Hit (Highlight Image) */
USHORT SpeakVectors[] = {
   0, 0,    47, 0,    47, 13,   0, 13,
   0, 1,    46, 1,    46, 12,   1, 12,
   1, 1
};
struct Border SpeakBorder = {
   -4, -3,
   WHTP, BLUP, JAM1,
   9,
   SpeakVectors,
   NULL
};
struct IntuiText SpeakIText = {
   WHTP, BLUP,
   JAM2,
   0, 0,
   &TestFont,
   "Speak",
   NULL
};
struct Gadget SpeakGadget = {
   &TranslateGadget,
   261, 79, 40, 8,
   GADGHCOMP,
   RELVERIFY | GADGIMMEDIATE,
   BOOLGADGET,
   (APTR)&SpeakBorder,
   NULL,
   &SpeakIText,
   0,
   NULL,
   0,
   NULL
};

/* Now the proportional gadgets. */
/* Proportional Gadgets **************************************/
/* The following variables are used to create proportional
 * Gadgets. These variables will be filled in with copies of
 * the generic Gadgetry below.
 */
SHORT PropCount = 0;       /* index to next available Gadget */
struct IntuiText PTexts[NUMPROPS];/* get copies of TPropText */
/* dummy AUTOKNOB Images are required */
struct Image PImages[NUMPROPS];
/* These get copies of TPropInfo */
struct PropInfo PInfos[NUMPROPS];
/* These get copies of TPropGadget */
struct Gadget Props[NUMPROPS];
struct IntuiText TPropText = {
   WHTP, BLUP,
   JAM1,
   0, -10,
   &TestFont,
   NULL,
   NULL
};   
struct PropInfo TPropInfo = {
   AUTOKNOB | FREEHORIZ,
   0, 0,
   0x1FFF, 0x1FFF,
   0, 0, 0, 0, 0, 0
};
/* this is the template for the Gadget of a horizontal */
/* Proportional Gadget */
struct Gadget TPropGadget = {
   &SpeakGadget,
   7, 12, 115, 10,
   GADGHCOMP | GADGIMAGE,
   GADGIMMEDIATE | RELVERIFY,
   PROPGADGET,
   NULL,
   NULL,
   NULL,
   0,
   NULL,
   0,
   NULL
};

struct IntuitionBase *IntuitionBase = 0;
struct GfxBase *GfxBase = 0;

/* Only one menu. */

ULONG MenuNumber;
ULONG TheMenu;
ULONG TheItem;

struct IntuiText MenuItemText = {
   BLUP,
   WHTP,
   JAM2,
   0,
   0,
   &TestFont,
   "About SpeechToy...",
   NULL
};
struct IntuiText MenuItemText2 = {
   BLUP,
   WHTP,
   JAM2,
   0,
   0,
   &TestFont,
   "Save Current Speech Settings",
   NULL
};

struct MenuItem MyMenuItem2 = {
   NULL,
   0,
   10,
   250,
   8,
   ITEMTEXT | ITEMENABLED | HIGHCOMP,
   0,
   (APTR)&MenuItemText2,
   NULL,
   NULL,
   NULL,
   MENUNULL
};
struct MenuItem MyMenuItem = {
   &MyMenuItem2,
   0,
   0,
   250,
   8,
   ITEMTEXT | ITEMENABLED | HIGHCOMP,
   0,
   (APTR)&MenuItemText,
   NULL,
   NULL,
   NULL,
   MENUNULL
};

struct Menu MyMenu = {
   NULL,
   0,0,150,0,
   MENUENABLED,
   "SpeechToy Menu",
   &MyMenuItem
};

struct IntuiText ReqText1 = {
   BLUP,
   WHTP,
   JAM2,
   5,
   23,
   &TestFont,
   "Version 1.2  15 Apr, 1988",
   NULL
};
struct IntuiText ReqText2 = {
   BLUP,
   WHTP,
   JAM2,
   5,
   13,
   &TestFont,
   "Written by David M Lucas ",
   &ReqText1
};
struct IntuiText ReqText3 = {
   BLUP,
   WHTP,
   JAM2,
   5,
   3,
   &TestFont,
   "Enhancements by Rob Peck",
   &ReqText2
};

struct IntuiText OKIText = {
   BLUP, WHTP,
   JAM2,
   6, 3,
   &TestFont,
   "OK",
   NULL
};

struct Requester *AboutRequester;

USHORT autoret;
struct Window *ControlWindow = NULL;
struct Window *FaceWindow = NULL;
struct IntuiMessage *MyIntuiMessage;
struct NewWindow NewControlWindow = {
   00, 11,
   CONTWINDW, CONTWINDH,
   -1, -1,
   GADGETUP | CLOSEWINDOW | MENUPICK,
   WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE
   | GIMMEZEROZERO | ACTIVATE,
   &Props[NUMPROPS-1],
   NULL,
   (UBYTE *)"SpeechToy",
   NULL,
   NULL,
   20, 20,
   CONTWINDW, CONTWINDH,
   WBENCHSCREEN
};

struct NewWindow NewFaceWindow = {
   CONTWINDW, 11,
   FACEWINDW * 2, FACEWINDH,
   -1, -1,
   SIZEVERIFY | NEWSIZE | MENUPICK,
   /* Flags (can be NULL) */
   WINDOWDRAG | WINDOWDEPTH | WINDOWSIZING
    | SIZEBBOTTOM | GIMMEZEROZERO | ACTIVATE,
   NULL,
   NULL,
   (UBYTE *)"Face",
   NULL,
   NULL,
   FACEWINDW, FACEWINDH,
   640, 200,
   WBENCHSCREEN
};

USHORT FemaleIData[] = {
/*   ----    -  These nibbles matter to image. */
   0x0000, 0x0000,
   0x00F0, 0x0000,
   0x0198, 0x0000,
   0x030C, 0x0000,
   0x0198, 0x0000,
   0x00F0, 0x0000,
   0x0060, 0x0000,
   0x01F8, 0x0000,
   0x0060, 0x0000,
   0x0000, 0x0000
};

USHORT MaleIData[] = {
/*   ----    -   These nibbles matter to image. */
   0x0000, 0x0000,
   0x003E, 0x0000,
   0x000E, 0x0000,
   0x0036, 0x0000,
   0x01E0, 0x0000,
   0x0330, 0x0000,
   0x0618, 0x0000,
   0x0330, 0x0000,
   0x01E0, 0x0000,
   0x0000, 0x0000
};
USHORT HumanIData[] = {
/*   ----   ----   --   These nibbles matter to image. */
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0007,0x9E00,0x0000,
   0x0001,0x8600,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x2000,0x0000,
   0x0000,0x1000,0x0000,
   0x0000,0x0800,0x0000,
   0x0000,0x7C00,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x7800,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000
};
USHORT RobotIData[] = {
/*   ----   ----   --   These nibbles matter to image. */
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0007,0x9E00,0x0000,
   0x0004,0x9200,0x0000,
   0x0007,0x9E00,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0001,0xF800,0x0000,
   0x0001,0x0800,0x0000,
   0x0001,0xF800,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000
};
USHORT FaceIData[] = {
/*   ----   ----   --   These nibbles matter to image. */
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000
};
USHORT StopIData[] = {
/*   ----   ----   --   These nibbles matter to image. */
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000,
   0x0000,0x0000,0x0000
};
USHORT *FemaleIData_chip = 0;
USHORT *MaleIData_chip = 0;
USHORT *HumanIData_chip = 0;
USHORT *RobotIData_chip = 0;
USHORT *FaceIData_chip = 0;
USHORT *StopIData_chip = 0;

LONG foundicon = FALSE;		/* status variable */
LONG clistart = FALSE;		/* status variable */

extern struct WBStartup *WBenchMsg;
char *iconname;
BPTR  testlock, iconlock;
char namebuff[256];	/* where to store icon name if start from CLI */
char dirbuff[256];	/* store icon directory name if started from CLI */
ULONG parseresult;	/* what happened when we parsed the strings */
ULONG ParseStrings();
void ParseRexxStrings();
void SetValues();
ULONG GetDefaults();
struct RexxMsg *myrexmsg;


/** start of code ***************************/

void
ModGadget(g)
struct Gadget *g;
{
	USHORT i;
	i = RemoveGadget(ControlWindow, g);
	if(g == &ModeGadget)
		ModeGadget.Flags ^= SELECTED;
	if(g == &MaleGadget)
		MaleGadget.Flags ^= SELECTED;
	if(g == &FemaleGadget)
		FemaleGadget.Flags ^= SELECTED;
	AddGadget(ControlWindow, g, i);
	RefreshGadgets(g, ControlWindow, NULL);	
}



#define IssueTranslate()  gadgetmessage(&TranslateGadget,ControlWindow)
#define IssueSpeak()      gadgetmessage(&SpeakGadget,ControlWindow)

void
InitVoiceValues()
{
   voice_io.sex      = DEFSEX2;
   voice_io.mode     = DEFMODE2;
   voice_io.rate     = DEFRATE2;
   voice_io.pitch    = DEFPITCH2;
   voice_io.volume   = DEFVOL2;
   voice_io.sampfreq = DEFFREQ2;
}

void
main(argc, argv)
int argc;
char **argv;
{
   struct WBArg *wbarg;	
   USHORT i;

   ULONG Signals;        /* Wait() tells me which to look at */
   ULONG MIClass;        /* Save quickly, ReplyMsg() asap */
   USHORT MICode;        /* These hold my needed information */
   APTR MIAddress;

   iconlock = NULL;
   iconname = NULL;
   speakdone = TRUE;	/* no speech started yet, so it MUST be done. */

   clistart = FALSE;
   if(argc != 0) clistart = TRUE;

   /* let MyCleanup know these signals not allocated yet */
   talk_port.mp_SigBit = -1;
   read_port.mp_SigBit = -1;

   /* Open those libraries that the program uses directly */
   if ((IntuitionBase = (struct IntuitionBase *)
    OpenLibrary("intuition.library", LIBRARY_VERSION)) == 0) {
#ifdef DEBUG
   if(clistart)
      printf("Can't open the intuition library\n");
#endif
      MyCleanup(1);
      exit(FALSE);
   }

   if ((GfxBase = (struct GfxBase *)
    OpenLibrary("graphics.library", LIBRARY_VERSION)) == 0) {
#ifdef DEBUG
   if(clistart)
      printf("Can't open the graphics library\n");
#endif
      MyCleanup(2);
      exit(FALSE);
   }

   if ((TranslatorBase = (struct Library *)
    OpenLibrary("translator.library", LIBRARY_VERSION)) == 0) {
#ifdef DEBUG
   if(clistart)
      printf("Can't open the translator library\n");
#endif
      MyCleanup(3);
      exit(FALSE);
   }

   if ((IconBase = (struct Library *)
    OpenLibrary("icon.library", 1)) == 0) {
#ifdef DEBUG
   if(clistart)
      printf("Can't open the icon library\n");
#endif
      MyCleanup(4);
      exit(FALSE);
   }

   /* Open the device */
   if ((NarratorOpenError = OpenDevice("narrator.device", 0,
    &voice_io, 0)) != 0) {
#ifdef DEBUG
   if(clistart)
      printf("Can't open the narrator device\n");
#endif
      MyCleanup(5);
      exit(FALSE);
   }

   /* **RAP */

   if(argc == 0)	/* opened from Workbench!!, Good! */
   {
	wbarg    = WBenchMsg->sm_ArgList;
	iconlock = wbarg->wa_Lock;
	iconname = wbarg->wa_Name;
	clistart = FALSE;
   }
   else		/* open from CLI, see if currentdir has icon */
   {
	clistart = TRUE;

	namebuff[0] = '\0';
	strcat(namebuff, argv[0]);
	strcat(namebuff, ".info");

	/* Look in the CURRENT directory to see if file is there */

	testlock = Lock(namebuff, ACCESS_READ);

	/* To see if the icon file exists, we have to put .info
	 * onto the name of the file itself.  BUT,
	 * when we read the icon, we do it without '.info' on it */

	if(testlock == NULL)
	{
	    /* the icon file is not in the CURRENT dir. */
	    iconlock = NULL;
	    iconname = NULL;
	
	    /* Now see if the user specified a directory
	     * in which the icon IS located; passed as argv[1] */

	    if(argc == 2)
	    {
		dirbuff[0] = '\0';
		strcat(dirbuff, argv[1]);
		iconlock = Lock(dirbuff, ACCESS_READ);

		/* If THAT directory exists, will pass it
		 * as a parameter IF we can also find that
		 * the icon is really there.  More than one
		 * way to do this - could lock directory,
		 * go in there, then search for the file.
		 * Instead I chose to build directory path
		 * then try to lock the file.  Same difference.
		 */
		if(iconlock != NULL)   /* directory exists */

			/* actually, this could be a 
			 * file instead of a directory
			 * but who cares. */
		{
		   namebuff[0] = '\0';
		   strcat(namebuff, argv[1]);
		   if(namebuff[strlen(argv[1])-1] != ':')
			strcat(namebuff,"/");
		   strcat(namebuff, argv[0]);

		   /* This makes a complete path name to the icon */

	   /* WARNING - reusing variable named testlock here */

		   testlock = Lock(namebuff, ACCESS_READ);
		   if(testlock != NULL)
		   {
			/* OK, the icon exists, lets hand it over 
			 * (without the .info attached to it)
			 */
			namebuff[0] = '\0';
			strcat(namebuff, argv[0]);
			iconname = namebuff;
			UnLock(testlock);
		   } /* end of if testlock (inner) */
		}    /* end of if iconlock  */
	    }	     /* end of if argc == 2 */
	}	     /* end of if testlock (outer) */
	else	     /* else associated with if testlock (outer) */
	{
	    /* testlock (first try) was non-NULL, icon IS in current dir */

	    /* OK, the icon exists in the current dir;
	     * lets hand it over 
	     * (without the .info attached to it)
	     */
	    namebuff[0] = '\0';
	    strcat(namebuff, argv[0]);
	    iconname = namebuff;
	    UnLock(testlock);

            /* pass a lock on CURRENT dir. */

	    iconlock = Lock("",ACCESS_READ); 
	}   /* end of else */
   }        /* end of else from test argc == 0 */

   parseresult = GetDefaults(iconlock,iconname);

   if(parseresult == FALSE)   
		/* added - read the icon toolstrings */
   {
	MyCleanup(6);
	exit(FALSE);
   }
#ifdef DEBUG
   if(clistart)
      printf("Got past getting the defaults\n");
#endif

   /* This is where the proportional gadgets are set up, using
    * the templates that were declared staticly.
    */
   for(PropCount = 0; PropCount < NUMPROPS; PropCount++) {
      PTexts[PropCount] = TPropText;
      Props[PropCount] = TPropGadget;
      PInfos[PropCount] = TPropInfo;
      Props[PropCount].GadgetText = (struct IntuiText *)
       &PTexts[PropCount];
      Props[PropCount].GadgetRender = (APTR)
       &PImages[PropCount];
      Props[PropCount].SpecialInfo = (APTR)&PInfos[PropCount];
      switch (PropCount) {
      case 0:
         PTexts[PropCount].IText = "Sample Freq:";
         if (DEFFREQ2 == MAXFREQ)
            PInfos[PropCount].HorizPot = 65535;
         else
            PInfos[PropCount].HorizPot = ((DEFFREQ2 - MINFREQ)
             << 16) / (MAXFREQ - MINFREQ);
         break;
      case 1:
         PTexts[PropCount].IText = "Rate:";
         Props[PropCount].TopEdge += 22;
         Props[PropCount].NextGadget = &Props[PropCount-1];
         if (DEFRATE2 == MAXRATE)
            PInfos[PropCount].HorizPot = 65535;
         else
            PInfos[PropCount].HorizPot = ((DEFRATE2 - MINRATE)
             << 16) / (MAXRATE - MINRATE);
         break;
      case 2:
         PTexts[PropCount].IText = "Pitch:";
         Props[PropCount].LeftEdge += 183;
         Props[PropCount].NextGadget = &Props[PropCount-1];
         if (DEFPITCH2 == MAXPITCH)
            PInfos[PropCount].HorizPot = 65535;
         else
            PInfos[PropCount].HorizPot = ((DEFPITCH2-MINPITCH)
             << 16) / (MAXPITCH - MINPITCH);
         break;
      case 3:
         PTexts[PropCount].IText = "Volume:";
         Props[PropCount].TopEdge += 22;
         Props[PropCount].LeftEdge += 183;
         Props[PropCount].NextGadget = &Props[PropCount-1];
         if (DEFVOL2 == MAXVOL)
            PInfos[PropCount].HorizPot = 65535;
         else
            PInfos[PropCount].HorizPot = ((DEFVOL2 - MINVOL)
             << 16) / (MAXVOL - MINVOL);
         break;
      }
   }

   /* Now allocate memory accessable by the chips for images */
   if (InitImages() != TRUE) {
#ifdef DEBUG
   if(clistart)
      printf("Couldn't Allocate Images in chip memory.\n");
#endif
      MyCleanup(7);
      exit(FALSE);
   }

   /* Set up the write port, allocate the signal, */
   /* and the message */
   talk_port.mp_Node.ln_Type = NT_MSGPORT;
   talk_port.mp_Flags = 0;
   if ((talk_port.mp_SigBit = AllocSignal(-1)) == -1) {
#ifdef DEBUG
   if(clistart)
      printf("Couldn't Allocate talk Signal bit\n");
#endif
      MyCleanup(8);
      exit(FALSE);
   }
   talk_port.mp_SigTask = (struct Task *) FindTask((char *)
    NULL);
   NewList(&talk_port.mp_MsgList);

   /* Set up the read port, allocate the signal, */
   /*  and the message */
   read_port.mp_Node.ln_Type = NT_MSGPORT;
   read_port.mp_Flags = 0;
   if ((read_port.mp_SigBit = AllocSignal(-1)) == -1) {
#ifdef DEBUG
   if(clistart)
      printf("Couldn't Allocate read Signal bit\n");
#endif
      MyCleanup(9);
      exit(FALSE);
   }
   read_port.mp_SigTask = (struct Task *)
    FindTask((char *) NULL);
   NewList(&read_port.mp_MsgList);

   /* Set up the write channel information */

   /* **RAP */
   rexxport = CreatePort("SPEECHTOY",0);
   if(rexxport == NULL)
   {
#ifdef DEBUG
   if(clistart)
   	printf("Couldn't create rexxport\n");
#endif
      MyCleanup(10);
      exit(FALSE);
   }
   InitVoiceValues();

   voice_io.ch_masks = (audio_chan);
   voice_io.nm_masks = sizeof(audio_chan);
   voice_io.mouths = 0;
   voice_io.message.io_Message.mn_ReplyPort = &talk_port;
   voice_io.message.io_Command = CMD_WRITE;
   voice_io.message.io_Offset = 0;
   voice_io.message.io_Data = (APTR)PhonBuffer;
   voice_io.message.io_Message.mn_Length = sizeof(voice_io);

  /* Set up the read channel information */
   mouth_io.voice = voice_io;
   mouth_io.width = 0;
   mouth_io.height = 0;
   mouth_io.voice.message.io_Message.mn_ReplyPort = &read_port;
   mouth_io.voice.message.io_Command = CMD_READ;
   mouth_io.voice.message.io_Error = 0;
   if (FaceWindow == NULL) {
      if ((ControlWindow = (struct Window *)OpenWindow
       (&NewControlWindow)) == NULL) {
#ifdef DEBUG
   if(clistart)
         printf("Couldn't open the control window.\n");
#endif
         MyCleanup(11);
         exit(FALSE);
      }
   }

   /* fill background of window */
   SetAPen(ControlWindow->RPort, BLKP);
   RectFill(ControlWindow->RPort,0,0,
    ControlWindow->GZZWidth, ControlWindow->GZZHeight);
   RefreshGadgets(&Props[NUMPROPS-1],ControlWindow,NULL);

   SetMenuStrip(ControlWindow, &MyMenu);

   SetValues();

/* !!! Ah, But what if FaceWindow's not been opened? */
   for (;;) {  /* ever wait for a signal and process it */
      /* wait lets the rest of the system run, */
      /* this program sleeps */
      Signals = Wait((1 << ControlWindow->UserPort->mp_SigBit)
| (1 << FaceWindow->UserPort->mp_SigBit)
| (1 << voice_io.message.io_Message.mn_ReplyPort->mp_SigBit)
| (1 << mouth_io.voice.message.io_Message.mn_ReplyPort->mp_SigBit)
| (1 << rexxport->mp_SigBit));
      /* now check to see to what we owe the intrusion */

      if (Signals & (1<< ControlWindow->UserPort->mp_SigBit)) {
         /* Process the Intuition message */
         while (MyIntuiMessage=(struct IntuiMessage *)
                      GetMsg(ControlWindow->UserPort)) {
            /* Get all the needed info and give message back */
            MIClass = MyIntuiMessage->Class;
            MICode = MyIntuiMessage->Code;
            MIAddress = MyIntuiMessage->IAddress;
            ReplyMsg(MyIntuiMessage);
            /* Now, what was it you wanted? */
            switch (MIClass) {
               case MENUPICK:
                  menumessage(MICode, ControlWindow);
                  break;
               case GADGETUP:         /* reply, then process */
                  gadgetmessage(MIAddress, ControlWindow);
                  break;
               case CLOSEWINDOW:       /* bye! */
		  if(speakdone)
			/* don't accept close window 
			 * if I/O in process 
			 * (avoids crash) 
			 */
		  {
                   while (MyIntuiMessage = (struct
                   IntuiMessage *) GetMsg(
                   ControlWindow->UserPort))
                    ReplyMsg(MyIntuiMessage);
                  MyCleanup(12);
                  exit(TRUE);
                  break;
		  }
		  else
		  {
			break; 
		  }
               default:
#ifdef DEBUG
   if(clistart)
                  printf("Unhandled Message Received.\n");
#endif
                  break;
            }  /* switch */
         } /* while */
      } /* if */


      /* Woken by intuition for FaceWindow*/
      if (Signals & (1<< FaceWindow->UserPort->mp_SigBit)) {
         /* Process the Intuition message */
         while (MyIntuiMessage=(struct IntuiMessage *)
          GetMsg(FaceWindow->UserPort)) {
            switch (MyIntuiMessage->Class) {
               case SIZEVERIFY:
                  ReplyMsg(MyIntuiMessage);
                  break;
               case MENUPICK:
                  menumessage(MyIntuiMessage->Code,FaceWindow);
                  ReplyMsg(MyIntuiMessage);
                  break;
               case NEWSIZE:  /* Don't reply until processed */
                  DrawFace();
                  ReplyMsg(MyIntuiMessage);
                  break;
               default:
#ifdef DEBUG
   if(clistart)
                  printf("Unhandled Message Received.\n");
#endif
                  ReplyMsg(MyIntuiMessage);
                  break;
            }  /* switch */
         } /* while */
      } /* if */

      /* A voice SendIO (Write) has completed */
      if (Signals & (1 <<
       voice_io.message.io_Message.mn_ReplyPort->mp_SigBit)) {
         /* Was it Sucessful? filter out the abort error */
         if (voice_io.message.io_Error == -2)
            voice_io.message.io_Error = 0;
         if (voice_io.message.io_Error != 0) {
#ifdef DEBUG
   if(clistart)
            printf("Narrator won't. (%ld)\n",
             voice_io.message.io_Error);
#endif
            /* flash this screen */
            DisplayBeep(ControlWindow->WScreen);
            /* let user see where phoneme string was bad. */
            i = RemoveGadget(ControlWindow, &PhonStrGadget);
            /* move the cursor to the error char */
            PhonInfo.BufferPos = voice_io.message.io_Actual -1;
            /* assure cursor (error point) is shown in gad. */
            /* within 29 (number of chars shown) of front */
            if (voice_io.message.io_Actual < 29)
               PhonInfo.DispPos = 0;
            /* within 29 of end */
            else if ((voice_io.message.io_Length -
                      voice_io.message.io_Actual) < 29)
               PhonInfo.DispPos = voice_io.message.io_Length
                                  - 29;
            else
               PhonInfo.DispPos = voice_io.message.io_Actual
                                  - 15;
            AddGadget(ControlWindow, &PhonStrGadget, i);
            RefreshGadgets(&PhonStrGadget, ControlWindow,
             NULL);
            voice_io.message.io_Error = 0;
         }
/*       SpeakGadget.Flags ^= GADGDISABLED;
         FaceGadget.Flags ^= GADGDISABLED;
*/
         OnGadget(&SpeakGadget, ControlWindow, NULL);
         OnGadget(&FaceGadget,  ControlWindow, NULL);
	 speakdone = TRUE;	/* signal to accept more arexx msgs */

      }

      /* A mouth DoIO (Read) has completed */
      if (Signals & (1 <<
  mouth_io.voice.message.io_Message.mn_ReplyPort->mp_SigBit)) {
         WaitBOVP(&FaceWindow->WScreen->ViewPort);
         SetAPen(FaceWindow->RPort, WHTP);
         RectFill(FaceWindow->RPort, 0, EyesBottom,
          FaceWindow->GZZWidth, FaceWindow->GZZHeight);
         if (MouthWMult == 0)
            LipWidth = mouth_io.width >> 1;
         else
            LipWidth = mouth_io.width * MouthWMult;
         if (MouthHMult == 0)
            LipHeight = mouth_io.height >> 1;
         else
            LipHeight = mouth_io.height * (MouthHMult);
         SetAPen(FaceWindow->RPort, REDP);
         Move(FaceWindow->RPort,
          XMouthCenter - LipWidth, YMouthCenter);
         Draw(FaceWindow->RPort,
          XMouthCenter, YMouthCenter - LipHeight);
         Draw(FaceWindow->RPort,
          XMouthCenter + LipWidth, YMouthCenter);
         Draw(FaceWindow->RPort,
          XMouthCenter, YMouthCenter + LipHeight);
         Draw(FaceWindow->RPort,
          XMouthCenter - LipWidth, YMouthCenter);
         /* the narrator will give an error when the */
         /* write has completed and I've tried to read */
         /* so I stop trying when that happens */
         if (mouth_io.voice.message.io_Error == 0)
            SendIO(&mouth_io);
      }
	/* a REXX message has arrived */
	if(Signals & 1 << rexxport->mp_SigBit)
	{
		while(myrexmsg = (struct RexxMsg *)GetMsg(rexxport))
		{
			/* EMPTIES the port, good practice!! */
			ParseRexxStrings(myrexmsg);
		}
	}

   }  /* for */
}  /* main */

/* a MENUPICK has been received, this
 * routine takes the appropriate action
 */
void
menumessage(code, w)
USHORT code;
struct Window *w;
{
   int freq, rate, pitch, vol, sex, mode;

   char toolbuff[560];  /* 20 characters times 7 strings (7th is null) */
   int i,j;
   int success;

   j=0;
   for(i=0; i<7; i++)
   {
	toolstrings[i] = &toolbuff[j];
	j += 20;
   }
   switch (MENUNUM(code)) {
      case 0:
         switch (ITEMNUM(code)) {
            case 0:
               AutoRequest(w, &ReqText3, NULL,
                           &OKIText, 0, 0, 280, 47);
               break;

	    case 1:
		sprintf(toolstrings[0],"FREQUENCY=%ld",DEFFREQ2);      
		sprintf(toolstrings[1],"RATE=%ld",DEFRATE2);		
		sprintf(toolstrings[2],"PITCH=%ld",DEFPITCH2);
		sprintf(toolstrings[3],"VOLUME=%ld",DEFVOL2);
		sprintf(toolstrings[4],"MODE=%ld",DEFMODE2);
		sprintf(toolstrings[5],"SEX=%ld",DEFSEX2);

		toolstrings[6] = NULL;
		/* NULL value terminates it */

		toolsave = diskobj->do_ToolTypes; /* save pointer */
		diskobj->do_ToolTypes = &toolstrings[0];

		if(foundicon)
		{
		    success = WriteIcon(iconlock, iconname, diskobj);
		}
		diskobj->do_ToolTypes = toolsave; /* restore it */

		/* We restore it because the icon library routines
		 * have allocated a block of memory associated with
		 * this material and we have to give it all back at exit.
		 */
		break;
	 default:
		break;
         }
      break;
   }
}

/* Read the icon and if all of the tool strings are present, then
 * use them to set the default values for the speechtoy.  Return
 * values are as follows:  decimal 63 if all defaults are set, less
 * than 63 if at least one default is messed up.  Causes system to 
 * establish defaults on its own.   ... Rob Peck
 */
extern struct DiskObject *ReadIcon();

ULONG
GetDefaults(idir, iname)
struct FileLock *idir;
char        *iname;
{
	char *FindToolType();
	char **ts;

	/* Establish all defaults on entry, then change them
	 * if new values are found in the toolstrings.
	 */

	DEFSEX2    = DEFSEX;
	DEFMODE2   = DEFMODE;
	DEFPITCH2  = DEFPITCH;
	DEFVOL2    = DEFVOL;
	DEFFREQ2   = DEFFREQ;
	DEFRATE2   = DEFRATE;
	if(iname == NULL)
	{
		/* Started from the CLI, and NO ICON FILE
		 * in the current directory.  So this is IT! */
		/* This is a successful initialization */
		return(TRUE);
	}
#ifdef DEBUG
   if(clistart)
      printf("Going to try to readicon now\n");
#endif

	diskobj = ReadIcon(idir, iname);    /* diskobj is a global */
#ifdef DEBUG
   if(clistart)
      printf("Returned from readicon with diskobj value: %lx\n",diskobj);
#endif

	if(diskobj == NULL) return(FALSE);

	foundicon = TRUE;		    /* another global */

	ts = diskobj->do_ToolTypes; /* get ptr to tool strings */

return(ParseStrings(ts));
}

/* This routine is separated from the others to allow a command
 * from the outside world to affect the defaults.   Returns a mask
 * with 1 bits in the position indicating which parameters it
 * recognized and whose values it has set.  That is, a 1 in bit
 * position 0 means parameter 0 was recognized, a 1 in bit position
 * 1 means it set parameter 1 and so on.  ... Rob Peck
 *
 * Anything out of bounds results in setting things to their defaults.
 */

ULONG
ParseStrings(ts)
char **ts;
{
	ULONG rtnval;
	char *value;
	int temp;

	rtnval = 0;

	value = FindToolType(ts, "FREQUENCY");
	if(value)
	{
		DEFFREQ2 = atoi(value); 
		if(DEFFREQ2 < MINFREQ || DEFFREQ2 > MAXFREQ)
			DEFFREQ2 = DEFFREQ;
		rtnval |= 0x1;
	}
	value = FindToolType(ts, "RATE");
	if(value)
	{
		DEFRATE2 = atoi(value);
		if(DEFRATE2 < MINRATE || DEFRATE2 > MAXRATE)
			DEFRATE2 = DEFRATE;
		rtnval |=0x02;
	}
	value = FindToolType(ts, "PITCH");
	if(value)
	{
		DEFPITCH2 = atoi(value);
		if(DEFPITCH2 < MINPITCH || DEFPITCH2 > MAXPITCH)
			DEFPITCH2 = DEFPITCH;
		rtnval |=0x04;
	}
	value = FindToolType(ts, "VOLUME");
	if(value)
	{
		DEFVOL2 = atoi(value);
		if(DEFVOL2 < MINVOL || DEFVOL2 > MAXVOL)
			DEFVOL2 = DEFVOL;
		rtnval |=0x08;
	}
	value = FindToolType(ts, "MODE");
	if(value)
	{
		temp = atoi(value);
		if(DEFMODE2 < 0 || DEFMODE2 > 1)
			DEFMODE2 = DEFMODE;
		if(temp != DEFMODE2)
		{
			/* time for a change in the display */
			rtnval |= 0x10;
			DEFMODE2 = temp;
		}
	}
	value = FindToolType(ts, "SEX");
	if(value)
	{
		temp = atoi(value);
		if(DEFSEX2 < 0 || DEFSEX2 > 1)
			DEFSEX2 = DEFSEX;
		if(temp != DEFSEX2)
		{
			/* time for a change in the display */
			rtnval |= 0x20;
			DEFSEX2 = temp;
		}
	}
	/* for AREXX support, accept a string to be said */
	value = FindToolType(ts, "WORDS");
	if(value)
	{
		EnglBuffer[0] = '\0';
		strncat(EnglBuffer, value, ESTRINGSIZE);
		rtnval |= 0x40;
	}
	value = FindToolType(ts, "TRANS");
	if(value)
	{
		rtnval |= 0x80;
	}
	value = FindToolType(ts, "SPEAK");
	if(value)
	{
		rtnval |= 0x100;
	}
	value = FindToolType(ts, "PHON");
	if(value)
	{
		PhonBuffer[0] = '\0';
		strncat(PhonBuffer, value, PSTRINGSIZE);
		rtnval |= 0x200;
	}

return(rtnval);
}

/* Maximum total length of string passed from REXX is 1024 bytes,
 * including the terminating null (my own arbitrary limit, from
 * the declaration of rexxstrings[maxstringsize] in the global area.
 */
/* Only one argument, ARG0, is accepted by this program,
 * passed with a command (RX) from AREXX.
 */

void
ParseRexxStrings(rxmsg)
struct RexxMsg *rxmsg;
{
	int timeout;	/* only wait for just SO long, then quit */

	timeout = 0;
	/* parseresult is a global ULONG */

	rexxstrings[0] = (char *)rxmsg->rm_Args[0];
	rexxstrings[1] = NULL;

	parseresult = ParseStrings(&rexxstrings[0]);

	if(speakdone)
	{
		SetValues();
		rxmsg->rm_Result1 = 0;
	}
	else
	{
		/* TRIED this, but changed my mind...
		   New method is to HANG this task until
		   the speech I/O has completed!  Might
		   as well if we want to assure that
		   an external AREXX program can get
		   all of its commands executed!
		   Some of you may want to go thru the work
		   to build a queue of commands, or to add
		   more voice_io blocks and queue THEM.
		   I just wanted to get the project DONE.
		   Up to you.
		 */

		/* rxmsg->rm_Result1 = 5;  */
		/* AmigaDOS "warn" value, this one did not speak */

		/* NEW method, hang if speaking */
		WaitIO(&voice_io);  /* HANG and wait */
		speakdone = TRUE;   
		SetValues();
			/* we KNOW this otherwise hung forever */
	}
	ReplyMsg(rxmsg);
}



/* a GADGETUP has been received, this
 * routine takes the appropriate action
 */
void
gadgetmessage(address, w)
APTR address;
struct Window *w;
{
   USHORT i;
   long PropRange;
   if (address == (APTR)&ModeGadget) {
      if (ModeGadget.Flags & SELECTED)
         DEFMODE2 = ROBOTICF0;
      else
         DEFMODE2 = NATURALF0;
   }
   else if (address == (APTR)&FaceGadget) {
      /* tell the write that reads will be forthcomming */
      if (FaceGadget.Flags & SELECTED) {
         voice_io.mouths = 1;
         if ((FaceWindow = (struct Window *)
          OpenWindow(&NewFaceWindow)) == NULL) {
#ifdef DEBUG
   if(clistart)
            printf("Couldn't open the face window.\n");
#endif
            MyCleanup(13);
            exit(FALSE);
         }
         SetMenuStrip(FaceWindow, &MyMenu);
         DrawFace();
      }
      else { /* FaceGadget de-SELECTed */
         voice_io.mouths = 0;
         NewFaceWindow.LeftEdge = FaceWindow->LeftEdge;
         NewFaceWindow.TopEdge = FaceWindow->TopEdge;
         NewFaceWindow.Width = FaceWindow->Width;
         NewFaceWindow.Height = FaceWindow->Height;
         CloseWindow(FaceWindow);
         FaceWindow = NULL;
      }
   }
   else if (address == (APTR)&StopGadget) {
      AbortIO(&voice_io);
      voice_io.message.io_Error = 0;
      mouth_io.voice.message.io_Error = 0;
   }
   /* Since this program changes a flag that intuition expects
    * only the user to change (SELECTED bit), this program has
    * to remove, then change, then add this gadget. Then by
    * passing the address of this gadget to RefreshGadgets(),
    * only the gadgets from here to the start of the list will
    * be refreshed, which minimizes the visible flash that
    * RefreshGadgets() can introduce.
    * If one of the two gadgets (female/male) is hit, toggle
    * the selection of the other gadget (since the gadget hit
    * was toggled by intuition when it was hit).
    */
   else if (address == (APTR)&FemaleGadget) {
      if (FemaleGadget.Flags & SELECTED)
      DEFSEX2 = FEMALE;
      else
      DEFSEX2 = MALE;
      ModGadget(&MaleGadget);
   }
   else if (address == (APTR)&MaleGadget) {
      if (MaleGadget.Flags & SELECTED)
      DEFSEX2 = MALE;
      else
      DEFSEX2 = FEMALE;
      ModGadget(&FemaleGadget);
   }
   /* Since the program changes the contents of the string
    * gadgets' buffer and it's size, which is something else
    * intuition doesn't expect a program (as opposed to the
    * user) to do. The program must remove, then change, then
    * add this gadget, and then by passing the address of this
    * gadget to RefreshGadgets(), only the gadgets from here
    * to the start of the list will be refreshed, which
    * minimizes the visible flash that RefreshGadgets() can
    * introduce.
    */
   else if (address == (APTR)&TranslateGadget) {
      i = RemoveGadget(ControlWindow, &PhonStrGadget);
      if ((TranslatorError = Translate((APTR)EnglBuffer,
       EnglInfo.NumChars, (APTR)PhonBuffer, PhonInfo.MaxChars))
       != 0) {
#ifdef DEBUG
   if(clistart)
         printf("Translator won't. (%lx)\n",TranslatorError);
#endif
         /* flash this screen */
         DisplayBeep(ControlWindow->WScreen);
      }
      /* Hey! NumChars includes the terminating NULL. */
      /* This must be done. */
      PhonInfo.NumChars = voice_io.message.io_Length + 1;
      if (PhonInfo.DispPos > voice_io.message.io_Length)
          PhonInfo.DispPos = voice_io.message.io_Length;
      AddGadget(ControlWindow, &PhonStrGadget, i);
      RefreshGadgets(&PhonStrGadget, ControlWindow, NULL);
   }
   else if (address == (APTR)&SpeakGadget) {
/*    SpeakGadget.Flags ^= GADGDISABLED;
      FaceGadget.Flags ^= GADGDISABLED;
*/
      OffGadget(&SpeakGadget, ControlWindow, NULL);
      OffGadget(&FaceGadget,  ControlWindow, NULL);

      voice_io.message.io_Length = strlen(PhonBuffer);

      InitVoiceValues();	/* Rob Peck - before any new
				 * speech output, copy the
				 * current values into the
				 * I/O request block
				 */
      speakdone = FALSE;	/* once start to speak, dont blow it */
      SendIO(&voice_io);
      if (voice_io.mouths == 1) {
         mouth_io.voice.message.io_Error = 0;
         SendIO(&mouth_io);
      }
   }
   else if (address == (APTR)&EnglStrGadget);  /* do nothing */
   else if (address == (APTR)&PhonStrGadget);  /* do nothing */
   else if (address == (APTR)&Props[0]) {
      PropRange = RNGFREQ;
      DEFFREQ2 = (( (PInfos[0].HorizPot >> 1)
       * PropRange) >> 15) + MINFREQ;
#ifdef DEBUG
   if(clistart)
      printf("Freq. = %ld\n", DEFFREQ2);
#endif
   }
   else if (address == (APTR)&Props[1]) {
      PropRange = RNGRATE;
      DEFRATE2 = (((PInfos[1].HorizPot >> 1)
       * PropRange) >> 15) + MINRATE;
#ifdef DEBUG
   if(clistart)
      printf("Rate  = %ld\n", DEFRATE2);
#endif
   }
   else if (address == (APTR)&Props[2]) {
      PropRange = RNGPITCH;
      DEFPITCH2 = (((PInfos[2].HorizPot >> 1)
       * PropRange) >> 15) + MINPITCH;
#ifdef DEBUG
   if(clistart)
      printf("Pitch = %ld\n", DEFPITCH2);
#endif
   }
   else if (address == (APTR)&Props[3]) {
      PropRange = RNGVOL;
      DEFVOL2 = (((PInfos[3].HorizPot >> 1)
       * PropRange) >> 15) + MINVOL;
#ifdef DEBUG
   if(clistart)
      printf("Vol.  = %ld\n", DEFVOL2);
#endif
   }
#ifdef DEBUG
   else if (clistart) printf("Unhandled gadget up received!\n");
#endif
}

/* This calculates variables used to draw the mouth
 * and eyes, as well as redrawing the face.
 * Proportionality makes it very wierd, but it's
 * wierder if you don't use a GimmeZeroZero window
 * and GZZWidth/GZZHeight.
 */
void
DrawFace() {
   XMouthCenter =  FaceWindow->GZZWidth >> 1;
   /* set left edge of left eye */
   EyesLeft = FaceWindow->GZZWidth >> 2;
   /* multiplier for mouth width */
   MouthWMult = FaceWindow->GZZWidth >> 6;

   EyesTop = (FaceWindow->GZZHeight >> 2)
    - (FaceWindow->GZZHeight >> 4);
   EyesBottom = EyesTop + (FaceWindow->GZZHeight >> 3) + 1;
   yaw = FaceWindow->GZZHeight - EyesBottom;
   YMouthCenter = ((yaw >> 1) + EyesBottom);
   MouthHMult = yaw >> 5;

   /* Set pen to White */
   SetAPen(FaceWindow->RPort, WHTP);
   RectFill(FaceWindow->RPort, 0, 0,
    FaceWindow->GZZWidth, FaceWindow->GZZHeight);

   /* Set pen to Blue */
   SetAPen(FaceWindow->RPort, BLUP);
   RectFill(FaceWindow->RPort,
    EyesLeft, EyesTop,
    EyesLeft + (FaceWindow->GZZWidth >> 3),
    EyesTop + (FaceWindow->GZZHeight >> 3));
   RectFill(FaceWindow->RPort,
    (FaceWindow->GZZWidth >> 1)
    + (FaceWindow->GZZWidth >> 3),
    EyesTop,
    (FaceWindow->GZZWidth >> 1)
     + (FaceWindow->GZZWidth >> 3) /* two >> 3, not one >> 2 */
     + (FaceWindow->GZZWidth >> 3),/* so eyes are same width */
    EyesTop + (FaceWindow->GZZHeight >> 3));

    SetAPen(FaceWindow->RPort, REDP);  /* Set pen to Red */
    Move(FaceWindow->RPort,
     XMouthCenter - (FaceWindow->GZZWidth >> 3), YMouthCenter);
    Draw(FaceWindow->RPort,
     XMouthCenter + (FaceWindow->GZZWidth >> 3), YMouthCenter);

}

/* Deallocate any memory, and close all of the
 * windows/screens/devices/libraries in reverse order to
 * make things work smoothly. And be sure to check
 * that the open/allocation was successful before
 * closing/deallocating.
 */
MyCleanup(n)
SHORT n;
{
#ifdef DEBUG
   if(clistart)
    printf("Exit reason = %ld\n",n);
#endif

   if (clistart)
      if(iconlock) UnLock(iconlock);  /* Rob Peck */
   if (rexxport)
      DeletePort(rexxport);	/* RAP */
   if (diskobj != 0)
      FreeDiskObject(diskobj);
   if (read_port.mp_SigBit != -1)
      FreeSignal(read_port.mp_SigBit);
   if (talk_port.mp_SigBit != -1)
      FreeSignal(talk_port.mp_SigBit);
   if (FaceWindow != NULL)
      CloseWindow(FaceWindow);
   if (ControlWindow != NULL)
      CloseWindow(ControlWindow);
   /* freeimages makes sure image allocation was successful */
   freeimages();
   if (NarratorOpenError == 0)
      CloseDevice(&voice_io);
   if (TranslatorBase != 0)
      CloseLibrary(TranslatorBase);
   if (GfxBase != 0)
      CloseLibrary(GfxBase);
   if (IntuitionBase != 0)
      CloseLibrary(IntuitionBase);
   if (IconBase != 0)
      CloseLibrary(IconBase);
   return(0);
}

/* Allocate chip memory for gadget images, and set the
 * pointers in the corresponding image structures to point
 * to these images. This must be done because the program
 * could be loaded into expansion memory (off the side of
 * the box), which the custom chips cannot access.
 * And images must be in chip ram (that's memory that the
 * custom chips can access, the internal 512K).
 */
InitImages()
{
   /* the images were staticly initialized above main */
   extern USHORT *FemaleIData_chip;
   extern USHORT *MaleIData_chip;
   extern USHORT *HumanIData_chip;
   extern USHORT *RobotIData_chip;
   extern USHORT *FaceIData_chip;
   extern USHORT *StopIData_chip;
   int i;

   /* Allocate them all, stop and return false on failure */
   if ((FemaleIData_chip = (USHORT *)
    AllocMem(sizeof(FemaleIData),MEMF_CHIP)) == 0)
      return(FALSE);
   if ((MaleIData_chip = (USHORT *)
    AllocMem(sizeof(MaleIData),MEMF_CHIP)) == 0)
      return(FALSE);
   if ((HumanIData_chip = (USHORT *)
    AllocMem(sizeof(HumanIData),MEMF_CHIP)) == 0)
      return(FALSE);
   if ((RobotIData_chip = (USHORT *)
    AllocMem(sizeof(RobotIData),MEMF_CHIP)) == 0)
      return(FALSE);
   if ((FaceIData_chip = (USHORT *)
    AllocMem(sizeof(FaceIData),MEMF_CHIP)) == 0)
      return(FALSE);
   if ((StopIData_chip = (USHORT *)
    AllocMem(sizeof(StopIData),MEMF_CHIP)) == 0)
      return(FALSE);

   for (i=0; i<20; i++)
      FemaleIData_chip[i] = FemaleIData[i];
   for (i=0; i<20; i++)
      MaleIData_chip[i] = MaleIData[i];
   for (i=0; i<60; i++)
      HumanIData_chip[i] = HumanIData[i];
   for (i=0; i<60; i++)
      RobotIData_chip[i] = RobotIData[i];
   for (i=0; i<30; i++)
      FaceIData_chip[i] = FaceIData[i];
   for (i=0; i<30; i++)
      StopIData_chip[i] = StopIData[i];

   FemaleImage.ImageData = FemaleIData_chip;
   MaleImage.ImageData = MaleIData_chip;
   HumanImage.ImageData = HumanIData_chip;
   RobotImage.ImageData = RobotIData_chip;
   FaceImage.ImageData = FaceIData_chip;
   StopImage.ImageData = StopIData_chip;

   return(TRUE);
}

/* Deallocate the memory that was used for images,
 * See initimages for more details.
 */
void
freeimages()
{
   /* the images were staticly initialized above main */
   extern USHORT *FemaleIData_chip;
   extern USHORT *MaleIData_chip;
   extern USHORT *HumanIData_chip;
   extern USHORT *RobotIData_chip;
   extern USHORT *FaceIData_chip;
   extern USHORT *StopIData_chip;

   /* Deallocate only if the pointer is really there. */
   if (RobotIData_chip != 0)
      FreeMem(RobotIData_chip, sizeof(RobotIData));
   if (HumanIData_chip != 0)
      FreeMem(HumanIData_chip, sizeof(HumanIData));
   if (MaleIData_chip != 0)
      FreeMem(MaleIData_chip, sizeof(MaleIData));
   if (FemaleIData_chip != 0)
      FreeMem(FemaleIData_chip, sizeof(FemaleIData));
   if (FaceIData_chip != 0)
      FreeMem(FaceIData_chip, sizeof(FaceIData));
   if (StopIData_chip != 0)
      FreeMem(StopIData_chip, sizeof(StopIData));
}


/* ReadIcon:
 * input  = lock on the directory where the icon resides
 *          (program only gets this if called from WBench)
 * 	    and name of the .info file to be read.
 * returns a pointer to a DiskObject structure if success,
 *          or NULL if not.
 */

struct DiskObject *
ReadIcon(icondirlock, iconfilename)
struct FileLock *icondirlock;   /* lock on dir where icon is located */
char *iconfilename;	    /* name of this icon */
{

	struct FileLock *olddir;
	struct DiskObject *dobj;

	dobj   = NULL;				/* assume failure */
	olddir = CurrentDir(icondirlock);       /* Move where icon is */
	dobj   = GetDiskObject(iconfilename);   /* Only then can read it */

	CurrentDir(olddir);  /* Return to original dir */
	return(dobj);        /* If successful, returns a pointer
			      * to a memory area allocated by 
			      * the icon.library
			      */

/* ... REMEMBER to FreeDiskObject when done with it. */
}

/* WriteIcon */

/* input = lock on the directory where to write and name of 
 *         file to write, and a disk object pointer, 
 *         
 * output = nonzero if ok, but if value over 100, then error.
 */

long
WriteIcon(icondirlock, iconfilename, dobj)
struct FileLock *icondirlock;
char *iconfilename;
struct DiskObject *dobj;
{
	struct FileLock *olddir;
	long status;
	status = -1;	/* assume failure */

	olddir = CurrentDir(icondirlock);  /* move to target directory */

	status = PutDiskObject(iconfilename, dobj);
	if(status == 0)
	{
	   status = IoErr();   /* let AmigaDOS say why if possible */
	}
	CurrentDir(olddir);
	return(status);
}

void
SetValues()
{
   USHORT i;
   if(parseresult & 0x20)
   {
	if(
	   ((DEFSEX2 == 1) && ((FemaleGadget.Flags & SELECTED) == 0)) ||
	   ((DEFSEX2 == 0) && ((MaleGadget.Flags   & SELECTED) == 0))    
	  )
	  {
	      gadgetmessage(&FemaleGadget,ControlWindow);
	      gadgetmessage(&MaleGadget,ControlWindow);
	  }
   }

   if(parseresult & 0x10) ModGadget(&ModeGadget);
   if(parseresult & 0x40) ModGadget(&EnglStrGadget);
   if(parseresult & 0x80) IssueTranslate();
   if(parseresult & 0x100) IssueSpeak();
   if(parseresult & 0x200) ModGadget(&PhonStrGadget);
}
