/* Here is a sample speech demo program that compiles on
 * Amiga (Lattice) C.  It can be thought of as a stripped
 * down version of the speechtoy (lucas).  I haven't 
 * provided the graphics for the drawing of the mouth,
 * but the access to the mouth variables is shown here.
 * 
 * It is the sample program from the rev 1.1 ROM KERNEL
 * manual, now at the printers.
 * 
 * Rob Peck.   
 *
 * This code may be freely utilized to create programs for the Amiga.
 */
 


#include "exec/types.h"
#include "exec/exec.h"

#include "exec/nodes.h"
#include "exec/lists.h"
#include "exec/memory.h"
#include "exec/interrupts.h"
#include "exec/ports.h"
#include "exec/libraries.h"
#include "exec/io.h"
#include "exec/tasks.h"
#include "exec/execbase.h"

#include "devices/narrator.h"
#include "libraries/translator.h"

struct MsgPort *readport=0;
struct MsgPort *writeport=0;

extern struct MsgPort *CreatePort();
extern struct IORequest *CreateExtIO();   

struct narrator_rb *writeNarrator=0;
struct mouth_rb *readNarrator=0;
struct Library *TranslatorBase=0;
UBYTE *sampleinput;      /* pointer to sample input string */
UBYTE outputstring[500];   /* place to put the translation */
SHORT rtnCode;         /* return code from function */
SHORT readError;
SHORT writeError;
SHORT error;
BYTE  audChanMasks[4] = { 3,5,10,12 }; /* which channels to use */

#define CANT_OPEN_TRANSLATOR -100
#define CANT_OPEN_NARRATOR -200
#define CREATE_PORT_PROBLEMS -300
#define CREATE_IO_PROBLEMS -400
#define CANT_PERFORM_WRITE -500
#define REVISION 1

extern struct Library *OpenLibrary();

main()
{
   TranslatorBase = OpenLibrary("translator.library",REVISION);
   if(TranslatorBase == NULL) exit (CANT_OPEN_TRANSLATOR);
   sampleinput = "this is a test"; /* a test string of 14 characters */
   rtnCode = Translate(sampleinput,14,outputstring,500);
   error = rtnCode + 100;
   if(rtnCode != 0) goto cleanup0;
   
   writeport = CreatePort(0,0);
   if(writeport == NULL) { error=CREATE_PORT_PROBLEMS; goto cleanup1; }
   readport = CreatePort(0,0);
   if(readport == NULL) { error=CREATE_PORT_PROBLEMS; goto cleanup2; }
   writeNarrator = (struct narrator_rb *)CreateExtIO(writeport,
               sizeof(struct narrator_rb));
   if(writeNarrator == NULL) { error=CREATE_IO_PROBLEMS; goto cleanup3; }
   readNarrator = (struct mouth_rb *)CreateExtIO(readport,
               sizeof(struct mouth_rb));

   if(readNarrator == NULL) { error=CREATE_IO_PROBLEMS; goto cleanup4; }
/* SET UP THE PARAMETERS FOR THE WRITE-MESSAGE TO THE NARRATOR DEVICE */

   /* show where to find the channel masks */
   writeNarrator->ch_masks = (audChanMasks);

   /* and tell it how many of them there are */
   writeNarrator->nm_masks = sizeof(audChanMasks);   

   /* tell it where to find the string to speak */   
   writeNarrator->message.io_Data = (APTR)outputstring;

   /* tell it how many characters the translate function returned */
   writeNarrator->message.io_Length = strlen(outputstring);

   /* if nonzero, asks that mouths be calculated during speech */
   writeNarrator->mouths = 1;

   /* tell it this is a write-command */
   writeNarrator->message.io_Command = CMD_WRITE;

/* Open the device  */

   error = OpenDevice("narrator.device", 0, writeNarrator, 0);
   if(error != 0) goto cleanup4;

/* SET UP THE PARAMETERS FOR THE READ-MESSAGE TO THE NARRATOR DEVICE */

   /* tell narrator for whose speech a mouth is to be generated */
   readNarrator->voice.message.io_Device = 
      writeNarrator->message.io_Device;
   readNarrator->voice.message.io_Unit = 
      writeNarrator->message.io_Unit;

   readNarrator->width = 0;
   readNarrator->height = 0;   /* initial mouth parameters */

   readNarrator->voice.message.io_Command = CMD_READ;
      /* initial error value */
   readNarrator->voice.message.io_Error = 0;   

/* Send an asynchronous write request to the device */

   writeError = SendIO(writeNarrator);
   if(writeError != NULL) { error=CANT_PERFORM_WRITE; goto cleanup5; }
   /* return immediately, run tasks concurrently */

/* keep sending reads until it comes back saying "no write in progress" */

   while((readError = readNarrator->voice.message.io_Error) != 
      ND_NoWrite)
   {
      DoIO(readNarrator);
      /* put task to sleep waiting for a different
       * mouth shape or return of the message block
       * with the error field showing no write in
       * process
       */
      DrawMouth(readNarrator->width,readNarrator->height);
      /* user's own unique routine, not provided here */
   }

   Delay(30);

   rtnCode = Translate("No it is not",13,outputstring,500);
   writeNarrator->sex = FEMALE;
   writeNarrator->pitch = MAXPITCH;  /* raise pitch from default value */
   writeNarrator->message.io_Data = (APTR)outputstring;
   writeNarrator->message.io_Length = strlen(outputstring);
   DoIO(writeNarrator);
   
   Delay(30);

   rtnCode = Translate("Please! I am speaking now!",26,outputstring,500);
   writeNarrator->sex = MALE;
   writeNarrator->pitch = DEFPITCH;
   writeNarrator->message.io_Data = (APTR)outputstring;
   writeNarrator->message.io_Length = strlen(outputstring);
   DoIO(writeNarrator);

   Delay(30);

   rtnCode = Translate(
      "Well, you are not very interesting, so I am going home!", 
      55,outputstring,500);
   writeNarrator->sex = FEMALE;
   writeNarrator->pitch = MAXPITCH;
   writeNarrator->message.io_Data = (APTR)outputstring;
   writeNarrator->message.io_Length = strlen(outputstring);
   DoIO(writeNarrator);

   Delay(30);

   rtnCode = Translate("Bye Bye",7,outputstring,500);
   writeNarrator->sex = MALE;
   writeNarrator->pitch = DEFPITCH;
   writeNarrator->rate = 7;   /* slow him down */
   writeNarrator->message.io_Data = (APTR)outputstring;
   writeNarrator->message.io_Length = strlen(outputstring);
   DoIO(writeNarrator);

    cleanup5:
   if(writeNarrator != 0)
      CloseDevice(writeNarrator);
            /* terminate access to the device */

   /* now return system memory to the memory allocator */ 

    cleanup4:
   if(readNarrator != 0)
      DeleteExtIO(readNarrator,sizeof(struct mouth_rb));
    cleanup3:
   if(writeNarrator != 0)
      DeleteExtIO(writeNarrator,sizeof(struct narrator_rb));
    cleanup2:
   if(readport != 0)
      DeletePort(readport);
    cleanup1:
   if(writeport != 0)
      DeletePort(writeport);
    cleanup0:
   if(TranslatorBase != 0)
         CloseLibrary(TranslatorBase);
            /* terminate access to the library */
   
   if(error != 0) exit(error);
} /* end of test */   

DrawMouth(w,h)
SHORT w,h;
{   return(0);   /* dummy routine */   }

int strlen(string)
char *string;
{
   int i,length;
   length = -1;
   for(i=0; i<256; i++)   /* 256 characters max length at this time */
      {
      if(*string++ == '\0') { length = i+1; break; };
      }
   return(length);
}


/***********************************************************************
*
*   Exec Support Function -- Extended IO Request
*
***********************************************************************/

extern APTR AllocMem();

/****** exec_support/CreateExtIO **************************************
*
*   NAME   
*   CreateExtIO() -- create an Extended IO request
*
*   SYNOPSIS
*   ioReq = CreateExtIO(ioReplyPort,size);   
*
*   FUNCTION
*   Allocates memory for and initializes a new IO request block
*   of a user-specified number of bytes.
*
*   INPUTS
*   ioReplyPort - a pointer to an already initialized
*      message port to be used for this IO request's reply port.
*
*   RESULT
*   Returns a pointer to the new block.  Pointer is of the type
*   struct IORequest.
*
*   0 indicates inability to allocate enough memory for the request block
*   or not enough signals available.
*
*   EXAMPLE
*   struct IORequest *myBlock;
*   if( (myBlock = CreateExtIO(myPort,sizeof(struct IOExtTD)) == NULL)
*      exit(NO_MEM_OR_SIGNALS);
*
*   example used to allocate space for IOExtTD (trackdisk driver
*   IO Request block for extended IO operations).
*
*   SEE ALSO
*   DeleteExtIO
*
***********************************************************************/

struct IORequest *CreateExtIO(ioReplyPort,size)
    struct MsgPort *ioReplyPort;
    LONG size;
{
    struct IORequest *ioReq;

    if (ioReplyPort == 0)
   return ((struct IORequest   *) 0);

    ioReq = (struct IORequest *)AllocMem (size, MEMF_CLEAR | MEMF_PUBLIC);

    if (ioReq == 0)
   return ((struct IORequest   *) 0);

    ioReq -> io_Message.mn_Node.ln_Type = NT_MESSAGE;
    ioReq -> io_Message.mn_Node.ln_Pri = 0;

    ioReq -> io_Message.mn_ReplyPort = ioReplyPort;
    ioReq -> io_Message.mn_Length = (size - sizeof(struct Message));
                  /* new (rap) */
    return (ioReq);
}

/****** exec_support/DeleteExtIO **************************************
*
*   NAME
*   DeleteExtIO() - return memory allocated for extended IO request
*
*   SYNOPSIS
*   DeleteExtIO(ioReq,size);
*
*   FUNCTION
*   See summary line at NAME.  Also frees the signal bit which
*   had been allocated by the call to CreateExtIO.
*
*   INPUTS
*   A pointer to the IORequest block whose resources are to be freed.
*
*   RESULT
*   Frees the memory.  Returns (no error conditions shown)
*
*   EXAMPLE
*   struct IORequest *myBlock;
*   DeleteExtIO(myBlock,(sizeof(struct IOExtTD)));
*      
*   example shows that CreateExtIO had been used to create a trackdisk
*   (extended) IO Request block.
*
*   SEE ALSO
*   CreateExtIO
*
**************************************************************************/

DeleteExtIO(ioExt,size)
    struct IORequest *ioExt;
    LONG size;
{
    ioExt -> io_Message.mn_Node.ln_Type = 0xff;
    ioExt -> io_Device = (struct Device *) -1;
    ioExt -> io_Unit = (struct Unit *) -1;

    FreeMem (ioExt, size);
}


