/* 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 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();

say_string(Txt,done)
  UBYTE *Txt;
  int done;
{
  static FirstTime = 1;

  if (FirstTime) {
    FirstTime = 0;
	TranslatorBase = OpenLibrary("translator.library",REVISION);
	if(TranslatorBase == NULL) exit (CANT_OPEN_TRANSLATOR);
	rtnCode = Translate(Txt,strlen(Txt)+1,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;

	/* select rate of 165 words/min. */

	writeNarrator->rate = (UWORD)165;

/* 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
		 */
	}

	Delay(30);
     return;
   }
   if ( !done ) {
	rtnCode = Translate(Txt,strlen(Txt)+1,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);
    return;
  }

    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 */	

/***********************************************************************
*
*	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);
}
