home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Audio Version 4.94
/
audioversion4.94knowledgemediaresourcelibraryoctober1994.iso
/
amiga
/
speech
/
typndtll
/
typndtll.c
< prev
Wrap
C/C++ Source or Header
|
1991-08-16
|
20KB
|
626 lines
/* TYPE and TELL
By: Giorgio Galeotti
Anakin Research Inc.
Rexdale, Ontatio, Canada.
Tel. 416-744-4246
This program will install an input device handler before the Intuition
one, tap all keys typed by the user and spell them out in real time.
Just run this program as a background task. To quit the program press
"CONTROL LEFT-SHIFT LEFT-ALT RIGHT-AMIGA" at the same time.
This was the result of a couple of days work and it could be improved
by finding better sounding voices and translations for some of the
keys.
You are free to modify or use this program in any way you want, as long
as this notice is left here and as long as you do not use the program
for any commercial purposes. As a matter of fact, you are encouraged to
improve TYPE & TELL since it could be of some benefit to blind
Amiga users.
Finally, I and Anakin Research are not responsible for any losses or
damages caused by this program. After all, it has hardly been tested
and therefore you should use it at your own risk.
P.S. Writen for Lattice 'C'.
*/
#include <exec/types.h>
#include <exec/tasks.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/execbase.h>
#include <exec/ports.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <exec/interrupts.h>
#include <exec/devices.h>
#include <exec/libraries.h>
#include <devices/input.h>
#include <devices/inputevent.h>
#include <devices/narrator.h>
#include <libraries/translator.h>
#include <stdio.h>
#include <libraries/dos.h>
#define INTUITION_REV 0
#define INPUT_RING_SIZE 50 /* Size of Input Event ring buffer */
#define QUIT_CODE_1 0x8099 /* Control-shift-alt-alt's event qualifier */
#define QUIT_CODE_2 0x809d /* As above but with Caps-Lock light on */
/* Globals */
struct MsgPort *InputDevPort = 0;
struct IOStdReq *InputRequestBlock = 0;
struct Interrupt handlerStuff;
int InputDeviceOpen = 0; /* Just to keep track of things */
struct MemEntry me[10]; /* Dummy data used to keep the handler */
/* happy */
struct InputEvent EventRing[INPUT_RING_SIZE]; /* Raw key events buffer */
int Filler = 0; /* Pointer into the EventRing Buffer */
int Emptier = 0; /* " " " */
int Counter = 0; /* # of entries now in buffer */
ULONG lastsecond = 0; /* See myhandler() routine */
ULONG lastmicro = 0;
int MainSignal; /* Used by handler to wakeup main task */
int Waiting = 0; /* Set by main before going to sleep */
struct Task *MainTask;
extern struct GfxBase *GfxBase;
struct Library *TranslatorBase = 0;
struct Device *ConsoleDevice = 0;
struct narrator_rb *WriteNarrator = 0;
extern VOID HandlerInterface();
int NarratorDeviceOpen = 0; /* To keep track of things */
struct MsgPort *WritePort = 0; /* Output Port for Narrator device */
UBYTE AudioChannels[4] = {3, 5, 10, 12}; /* Audio channels to recieve output */
UBYTE OutputString[100]; /* Translated output string */
/******************************************************************************/
main()
/* The initialization of all resources and a bit more. */
{
/* For cleanup purposes */
GfxBase = 0;
/* Open the console device. This will return the InputRequestBlock
whose field io_Device is initialized to the library pointer.
See page B-54 in the RKM V2 */
if ( OpenDevice("console.device",-1,InputRequestBlock,0) != 0 )
exit(100);
ConsoleDevice = InputRequestBlock->io_Device;
CloseDevice(InputRequestBlock);
InputRequestBlock = 0; /* For clean up purposes */
/* Open the Gfx library, since it's needed to do the WaitTOF()'s in the
input handler */
if ( (GfxBase = (struct GfxBase *)
OpenLibrary("graphics.library",INTUITION_REV)) == 0 )
CleanUp_Exit(110);
/* Get a signal bit so that our input handler can wake us up when there is
something in the buffer */
MainSignal = AllocSignal(-1);
MainTask = (struct Task *)FindTask(0);
/* Get a port for the input device ... */
InputDevPort = (struct MsgPort *)CreatePort(0,0);
if ( InputDevPort == NULL )
CleanUp_Exit(120);
/* ... and an InputRequestBlock */
InputRequestBlock = (struct IOStdReq *)CreateStdIO(InputDevPort);
if ( InputRequestBlock == 0 )
CleanUp_Exit(130);
/* Open the input device */
if ( OpenDevice("input.device",0,InputRequestBlock,0) == 0 )
InputDeviceOpen = 1;
else CleanUp_Exit(140);
/* Get ready to install our own device handler ... */
handlerStuff.is_Data = (APTR)&me[0];
handlerStuff.is_Code = HandlerInterface;
handlerStuff.is_Node.ln_Pri = 51;
InputRequestBlock->io_Command = IND_ADDHANDLER;
InputRequestBlock->io_Data = (APTR)&handlerStuff;
/* ... and install the handler */
DoIO(InputRequestBlock);
/* Open the translator library */
if ( (TranslatorBase = (struct Library *)
OpenLibrary("translator.library",0)) == NULL )
CleanUp_Exit(500);
/* Open the Narrator device for writes */
if ( (WritePort = (struct MsgPort *)CreatePort(0,0)) == NULL )
CleanUp_Exit(510);
WriteNarrator = (struct narrator_rb *)
CreateExtIO(WritePort,sizeof(struct narrator_rb));
if ( WriteNarrator == NULL )
CleanUp_Exit(520);
if ( OpenDevice("narrator.device",0,WriteNarrator,0) == 0 )
NarratorDeviceOpen = 1;
else CleanUp_Exit(140);
/* Set up part of the narrator's WritePort */
WriteNarrator->ch_masks = (AudioChannels);
WriteNarrator->nm_masks = sizeof(AudioChannels);
WriteNarrator->message.io_Command = CMD_WRITE;
/* Call the main control routine and sit there until the user requests to
quit. */
TheBigLoop();
}
/****************************************************************************/
TheBigLoop()
/* This routine contains the loop that keeps checking everything the user does
at the keyboard so that appropiate actions can be taken. */
{ BYTE buffer[30]; /* Buffer used by RawKeyConvert() to put results in. */
int actual; /* # of bytes returned by RawKeyConvert(). */
UWORD Qualifier;
while ( 1 )
{
/* Anything in the buffer ? */
if ( Counter == 0 )
{
/* No ... Go to sleep, waiting for the input handler to wake
us up */
Waiting = 1;
Wait(1<<MainSignal);
}
Qualifier = EventRing[Emptier].ie_Qualifier;
/* User requested to quit Type&Tell ? */
if ( Qualifier == QUIT_CODE_1 || Qualifier == QUIT_CODE_2 )
CleanUp_Exit(1000);
/* Convert raw-key event to ANSI standard. Assume default keymap. */
actual = RawKeyConvert(&EventRing[Emptier],buffer,30,0);
if ( actual == 1 )
SpeakASCII(buffer,actual);
else SpeakQualifier(&EventRing[Emptier]);
/* Buffer wrap ? */
if ( Emptier == INPUT_RING_SIZE-1 )
Emptier = 0;
else ++Emptier;
/* One less event in the buffer ... */
--Counter;
}
}
/****************************************************************************/
SpeakASCII(buffer,actual)
/* Speak the ASCII character contained in buffer and whose lenght is in
actual. */
char *buffer;
int actual;
{
if ( actual == 1 )
if ( (buffer[0] >= 'A' && buffer[0] <= 'Z') ||
(buffer[0] >= '0' && buffer[0] <= '9') )
{
WriteNarrator->rate = DEFRATE+100;
WriteNarrator->pitch = DEFPITCH;
WriteNarrator->mode = NATURALF0;
WriteNarrator->sex = DEFSEX;
WriteNarrator->volume = DEFVOL;
WriteNarrator->sampfreq = DEFFREQ;
/* Translate the character */
Translate(buffer,1,OutputString,100);
WriteNarrator->message.io_Data = (APTR)OutputString;
WriteNarrator->message.io_Length = strlen(OutputString);
/* And speak it */
DoIO(WriteNarrator);
return;
}
else if ( buffer[0] >= 'a' && buffer[0] <= 'z' )
{
WriteNarrator->rate = DEFRATE+100;
WriteNarrator->pitch = DEFPITCH+50;
WriteNarrator->mode = NATURALF0;
WriteNarrator->sex = FEMALE;
WriteNarrator->volume = DEFVOL;
WriteNarrator->sampfreq = DEFFREQ;
/* Translate the character */
Translate(buffer,1,OutputString,100);
WriteNarrator->message.io_Data = (APTR)OutputString;
WriteNarrator->message.io_Length = strlen(OutputString);
/* And speak it */
DoIO(WriteNarrator);
return;
}
else {
WriteNarrator->rate = DEFRATE+100;
WriteNarrator->pitch = DEFPITCH;
WriteNarrator->mode = NATURALF0;
WriteNarrator->sex = DEFSEX;
WriteNarrator->volume = DEFVOL;
WriteNarrator->sampfreq = DEFFREQ;
/* See if it is a control character */
if ( buffer[0] >= 0x01 && buffer[0] <= 0x1a )
{
buffer[8] = buffer[0] + 0x40;
strcpy(buffer,"CONTROL");
buffer[7] = ' ';
Translate(buffer,9,OutputString,100);
}
else switch ( buffer[0] )
{
case 0x1b:Translate("ESCAPE",6,OutputString,100);
break;
case ' ': Translate("SPACE",5,OutputString,100);
break;
case '!': Translate("EXCLAMATION",11,OutputString,100);
break;
case '"': Translate("DOUBLE QUOTE",12,OutputString,100);
break;
case '#': Translate("SHARP",5,OutputString,100);
break;
case '$': Translate("DOLLAR",6,OutputString,100);
break;
case '%': Translate("PERCENT",7,OutputString,100);
break;
case '&': Translate("AND",3,OutputString,100);
break;
case '\'':Translate("RIGHT QUOTE",11,OutputString,100);
break;
case '(': Translate("LEFT PARENTHESIS",16,OutputString,100);
break;
case ')': Translate("RIGHT PARENTHESIS",17,OutputString,100);
break;
case '*': Translate("STAR",4,OutputString,100);
break;
case '+': Translate("PLUS",4,OutputString,100);
break;
case ',': Translate("COMMA",5,OutputString,100);
break;
case '-': Translate("MYNUS",5,OutputString,100);
break;
case '.': Translate("DOT",3,OutputString,100);
break;
case '/': Translate("SLASH",5,OutputString,100);
break;
case ':': Translate("COLON",5,OutputString,100);
break;
case ';': Translate("SEMI COLON",10,OutputString,100);
break;
case '<': Translate("LESS THAN",9,OutputString,100);
break;
case '>': Translate("GREATER THAN",12,OutputString,100);
break;
case '=': Translate("EQUAL",5,OutputString,100);
break;
case '?': Translate("QUESTION",8,OutputString,100);
break;
case '@': Translate("AT",2,OutputString,100);
break;
case '[': Translate("LEFT BRACKET",12,OutputString,100);
break;
case '\\':Translate("BACKSLASH",9,OutputString,100);
break;
case ']': Translate("RIGHT BRACKET",13,OutputString,100);
break;
case '^': Translate("CARROT",6,OutputString,100);
break;
case '_': Translate("UNDER SCORE",11,OutputString,100);
break;
case '`': Translate("LEFT QUOTE",10,OutputString,100);
break;
case '{': Translate("LEFT BRACE",10,OutputString,100);
break;
case '|': Translate("VERTICAL BAR",12,OutputString,100);
break;
case '}': Translate("RIGHT BRACE",11,OutputString,100);
break;
case '~': Translate("TILDA",5,OutputString,100);
break;
case 0x7f:Translate("DELETE",6,OutputString,100);
break;
default: return;
}
WriteNarrator->message.io_Data = (APTR)OutputString;
WriteNarrator->message.io_Length = strlen(OutputString);
/* And speak it */
DoIO(WriteNarrator);
return;
}
}
/****************************************************************************/
SpeakQualifier(Event)
/* The keystroke did not translate to any ASCII character, but nevertheless
it must be called out */
struct InputEvent *Event;
{
WriteNarrator->rate = DEFRATE+100;
WriteNarrator->pitch = DEFPITCH+100;
WriteNarrator->mode = NATURALF0;
WriteNarrator->sex = FEMALE;
WriteNarrator->volume = DEFVOL;
WriteNarrator->sampfreq = DEFFREQ;
switch ( Event->ie_Code )
{
case 0x50: Translate("FUNCTION 1",10,OutputString,100);
break;
case 0x51: Translate("FUNCTION 2",10,OutputString,100);
break;
case 0x52: Translate("FUNCTION 3",10,OutputString,100);
break;
case 0x53: Translate("FUNCTION 4",10,OutputString,100);
break;
case 0x54: Translate("FUNCTION 5",10,OutputString,100);
break;
case 0x55: Translate("FUNCTION 6",10,OutputString,100);
break;
case 0x56: Translate("FUNCTION 7",10,OutputString,100);
break;
case 0x57: Translate("FUNCTION 8",10,OutputString,100);
break;
case 0x58: Translate("FUNCTION 9",10,OutputString,100);
break;
case 0x59: Translate("FUNCTION TEN",12,OutputString,100);
break;
case 0x63: Translate("CONTROL",7,OutputString,100);
break;
case 0x60:
case 0x61: Translate("SHIFT",5,OutputString,100);
break;
case 0x64:
case 0x65: Translate("ALTERNATE",9,OutputString,100);
break;
case 0x66: Translate("LEFT AMIGA",10,OutputString,100);
break;
case 0x67: Translate("RIGHT AMIGA",11,OutputString,100);
break;
case 0x5f: Translate("HELP",4,OutputString,100);
break;
case 0x4c: Translate("GO UP",5,OutputString,100);
break;
case 0x4f: Translate("GO LEFT",7,OutputString,100);
break;
case 0x4e: Translate("GO RIGHT",8,OutputString,100);
break;
case 0x4d: Translate("GO DOWN",7,OutputString,100);
break;
case 0x62: Translate("CAPS LOCK ON",12,OutputString,100);
break;
case 0xe2: Translate("CAPS LOCK OFF",13,OutputString,100);
break;
default: return;
}
WriteNarrator->message.io_Data = (APTR)OutputString;
WriteNarrator->message.io_Length = strlen(OutputString);
/* And speak it */
DoIO(WriteNarrator);
}
/****************************************************************************/
CleanUp_Exit(exitlevel)
/* Clean up all allocated resources and exit */
int exitlevel;
{
/* Remove our input handler and device from the chain */
if ( InputRequestBlock )
{
InputRequestBlock->io_Command = IND_REMHANDLER;
InputRequestBlock->io_Data = (APTR)&handlerStuff;
DoIO(InputRequestBlock);
if ( InputDeviceOpen )
CloseDevice(InputRequestBlock);
DeleteStdIO(InputRequestBlock);
}
if ( InputDevPort )
DeletePort(InputDevPort);
if ( NarratorDeviceOpen )
CloseDevice(WriteNarrator);
if ( WriteNarrator )
DeleteExtIO(WriteNarrator,sizeof(struct narrator_rb));
if ( TranslatorBase )
CloseLibrary(TranslatorBase);
if ( WritePort )
DeletePort(WritePort);
if ( GfxBase )
CloseLibrary(GfxBase);
exit(exitlevel);
}
/****************************************************************************/
struct InputEvent *myhandler(ev,mydata)
/* This input handler inserts itself at a higher priority than the intuition
handler. It will intercept all raw-key events and put them into the ring-
buffer so that other tasks in this program can use the data to keep track
of what keys have been pressed by the user ... */
struct InputEvent *ev;
struct MemEntry *mydata[];
{ register struct InputEvent *NextEvent;
/* look at all events in the event-list (or whatever it's called) and
put in the buffer all raw-key events whose time stamp indicates
we haven't looked at them before. Hopefully the system links new events
at the end of the chain, so that all time stamps are in order. */
NextEvent = ev; /* First entry in event-list */
do {
if ( NextEvent->ie_Class == IECLASS_RAWKEY )
if ( ( NextEvent->ie_TimeStamp.tv_micro > lastmicro &&
NextEvent->ie_TimeStamp.tv_secs >= lastsecond ) ||
NextEvent->ie_TimeStamp.tv_secs > lastsecond )
{
lastmicro = NextEvent->ie_TimeStamp.tv_micro;
lastsecond = NextEvent->ie_TimeStamp.tv_secs;
/* Get our own copy of the event */
movmem(NextEvent,&EventRing[Filler],sizeof(struct InputEvent));
++Counter; /* One more item in the buffer */
/* Buffer Wrap ? */
if ( Filler == INPUT_RING_SIZE-1 )
Filler = 0;
else ++Filler;
/* Is the user of the buffer waiting ? */
if ( Waiting )
{
Signal(MainTask,1<<MainSignal);
Waiting = 0;
}
/* The buffer may fill in final program. In case that
it happens, do some waits to give the other tasks the time
to empty the buffer a bit. A completely busy loop would tie up
the system forever. */
while ( Counter >= INPUT_RING_SIZE - 2 )
{
WaitTOF();
WaitTOF();
WaitTOF();
}
}
NextEvent = NextEvent->ie_NextEvent;
}
while ( NextEvent ); /* Done ? */
return ( ev );
}