home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-10-12 | 42.5 KB | 1,215 lines |
- 5 NARRATOR DEVICE
-
- 5.1 INTRODUCTION
-
- The Amiga can not only produce crisp clear four channel stereo
- sound, it can even speak. The operating system was designed so
- this unique feature easily and efficiently can be used. With
- very little effort you can let the Amiga read information.
-
- Sadly very few programs use this extremely useful feature.
- Although artificial speech sounds a bit monotone and
- uninteresting it can be used in many situations to transfer
- information from the computer to the user.
-
- Imagine if the user has to check hundreds of different numbers
- on the screen when the original numbers are on papers. This
- very boring and slow task can be made much easier if the Amiga
- automatically reads the values and the user only has to look at
- the paper.
-
-
-
- 5.2 ARTIFICIAL SPEECH
-
- Imitating human speech is a very difficult process. Even the
- most sophisticated super computers can still not manage to
- produce all the different intonations and sounds as a real
- human voice can. The Amiga's synthesized speech mechanism is
- not at all as powerful as what these super computers have, but
- compared to other home computers, it is outstanding.
-
- Producing artificial speech on the Amiga can be divided into
- two steps:
-
- 1. First we have to convert the text we want to be read into
- phonetical text. Phonetical text is written as the words
- should sound and not as how the words are spelled.
-
- If you open a dictionary you will notice that there exist
- a specification on how every word should be pronounced.
- The special symbols used to tell you how the word should
- sound are usually called "the phonetic symbols", and are
- defined in the beginning or end of the dictionary. The
- pronunciation of all words can be constructed with help
- of these symbols.
-
- On the Amiga we use the same technique. However, since the
- real phonetical symbols are very strange and can not be
- typed with a normal keyboard, we use a special
- "computerised" version of these symbols See next section
- for a complete list of these phonetical symbols.
-
- A great news is that you do not have to write the text
- with the phonetical language. You can instead use a
- special function called Translate() which can be found in
- a the "Translator Library". This function can
- automatically convert english text into phonetics.
-
- 2. After you have created a string with the phonetical text
- you send it to the "Narrator Device" which will transform
- the phonetical symbols into sound, and the text is read.
-
-
-
- 5.2.1 PHONETIC SYMBOLS
-
- The phonetical symbols used by the dictionaries can sadly not
- be used on normal computers since they use very strange
- signs that can not be reproduced on normal keyboards. Instead
- we have to use a special computerized set phonetical symbols
- which is defined like this:
-
- Sound Phoeme Example Type
- ---------------------------------
- A AE Hat Vowel
- AO Talk Vowel
- EY Page Diphthong (A diphtong is a union
- --------------------------------- of two vowel sounds or
- B B Bad Consonant vowel letters.)
- ---------------------------------
- C CH Chin Consonant
- K Car Consonant
- ---------------------------------
- D D Did Consonant
- ---------------------------------
- E EH Ten Vowel
- IY Feet Vowel
- ---------------------------------
- F F Four Consonant
- ---------------------------------
- G G Got Consonant
- ---------------------------------
- H /H How Consonant
- ---------------------------------
- I AY Five Diphthong
- ER Fur Vowel
- IH Sit Vowel
- ---------------------------------
- J J Yes Consonant
- ---------------------------------
- K K Cat Consonant
- ---------------------------------
- L L Leg Consonant
- ---------------------------------
- M M Man Consonant
- ---------------------------------
- N N No Consonant
- NX Sing Consonant
- ---------------------------------
- O AA Got Vowel
- AW Now Diphthong
- AX About Vowel
- IX Solid Vowel
- OH Saw Vowel
- OW Home Diphthong
- OY Join Diphthong
- UH Put Vowel
- ---------------------------------
- P P Pen Consonant
- ---------------------------------
- Q K Cat Consonant
- ---------------------------------
- R R Red Consonant
- ---------------------------------
- S S Sit Consonant
- SH She Consonant
- ---------------------------------
- T DH Then Consonant
- T Tea Consonant
- TH Thin Consonant
- ---------------------------------
- U AH Cup Vowel
- UW Too Diphthong
- ---------------------------------
- V V Voice Consonant
- ---------------------------------
- W W Wet Consonant
- ---------------------------------
- X /C Loch Consonant
- ---------------------------------
- Y Y Yellow Consonant
- ---------------------------------
- Z Z Zoo Consonant
- ZH Vision Consonant
- ---------------------------------
-
- For example, the word "hello" can phonetically be written as
- "/HEHLOW", and the word "house" as "/HAWZ".
-
-
-
- 5.2.2 INTONATION
-
- When you are speeking you do not use the same voice all the
- time. Some parts of the words or sentences are stressed by
- using a higher voice, were other parts are prounounced more
- softly.
-
- For example, the letter "e" in the word "hello" is usually
- prounounced in a higher voice. By stressing some words the text
- will sound more interesting, and if used with care it can be a
- very effective way to get the user's attention.
-
- Futhermore, by stressing different parts of a sentence the
- whole meaning of the sentence can change quite dramatically.
- Take the sentence "I will go home" for example. By stressing
- different words the user will interpretate the meaning
- differently. With these four words we can actually get five
- rather different meanings:
-
- 1. If no stressin is used, the meaning is simply that the
- person is going home.
-
- 2. By stressing the first word "I" it now sounds like the
- person him/her self is going home but the other persons
- (if there are any) may do something differently.
-
- 3. By stressing the second word "will" it now sounds like
- the persons is really determined and that he/she really
- wants to go home.
-
- 4. If the person stresses the third word "go" he/she means
- that he/she will walk home, although the word "go" can
- mean travelling by car, train etc.
-
- 5. Finally, if the last word is stressed the person wants
- to go to his/her home, and not any other place.
-
-
- To stress parts of a word you can put a single digit directly
- after the phonem which should use a higher intonation. The
- digit 0 means no special intonation, while the higher digit
- the more stressed will the phonem be. The digit 9 should be
- used when you want maximum intonation.
-
- For example, the stress the letter "e" in the word "hello" the
- phonetically string should be written as "/HEH4LOW". The
- phonem "EH" will then be medium stressed.
-
-
-
- 5.2.3 PUNCTATION AND SPECIAL SYMBOLS
-
- There exist some symbols which are used to help the reader to
- use the righ intonations. The most commonly used symbole is the
- puncation ".", but others are the question mark "?", the comma
- ",", dash "-", etc... Most of these common punctations are
- understod by the Amiga, and will automatically alter the
- intonation and speed correctly.
-
- Here are the symbols you may use:
-
-
- Type Symbol How it affects the intonation
- ------------------------------------------------------------
- Punctation . This will make the intonation of the
- last word a bit softer, and there will
- be a long pause before any following
- words are read.
-
- Question mark ? This will cause the Amiga to increase
- the intonation of the last word so it
- sounds like a question, and it will be
- followed by long pause before any
- following words are read.
-
- Comma , The comma is used to divide a sentense
- into several parts, but putting a small
- pause before the following text.
-
- Dash - The dash can be used as the comma, and
- works much in the same way. There exist
- a lot of confusing rules on when the
- dash and when the comma should be used,
- but I do not intead to expalin it here.
- (Mainly because I do not know anything
- about these rules, sadly. I use what
- looks best, which I think is an
- excellent rule.)
-
- Parentheses ( ) Parantheses are used to explain
- something which is not so important that
- it has to be in the main sentense, but
- is can still be important for the
- reader. The text inside parantheses
- starts off by using less intonation than
- the rest of the sentense.
-
-
-
- 5.2.4 VOLUME
-
- Although the intonation is altering the volumne a little, most
- of the text will be read with the same volume. You can however
- tell the narrator device to use a completely different volume,
- and this can be very effective. If you normally use a soft
- voice, but suddenly increase it to the maximum the user will
- not miss that part.
-
- Producing the correct sound can take some time, but once you
- have mastered it, the text will sound rather smooth and,
- although a bit computerized, rather human.
-
-
-
- 5.3 CONVERT TEXT INTO PHONETIC SYMBOLS
-
- Converting text into phonetical symbols does not have to be a
- hard task. If you are going to read an English text (or
- American, Irish, Scottish etc...) you can use the Translate()
- function. It will automatically convert the text into (almost)
- correct phonetics which can be used.
-
- The Translate() function is located in the "Translator" library
- which must be opened before you can use the function. The
- Translate() function is actually the only function in this
- library.
-
-
-
- 5.3.1 OPEN THE TRANSLATOR LIBRARY
-
- To open the translator library you declare a global pointer
- to it which must be called "TranslatorBase", and you then call
- the function OpenLibrary() to open it.
-
- Synopsis: library_ptr = OpenLibrary( name, revision );
-
- library_ptr: (struct Library *) If the function could open
- the library it returns a pointer to it, else
- NULL is returned. The translator library is
- located on the system disk, and is therefore
- loaded when needed. If some other program
- already has opened it has already been loaded
- into memory and a pointer is immediately
- returned to it.
-
- Be careful to close the library before your
- program terminates. If you forget it, the
- library will remain in the memory until the
- computer is switched off.
-
- name: (char *) Pointer to a string containing the
- name of the library you want to open. When
- you open the translator library, the name
- should be "translator.library".
-
- revision: (long) This value tells the Amiga which oldest
- version may be used. If there exist a library
- with the same or higher value the function will
- successfully open the library, else NULL is
- returned. If you simply want any version, set
- this field to zero.
-
-
- Here is an example: (Remember that the pointer to the
- translator library must ALWAYS be called "TranslatorBase"!)
-
- /* Pointer to the translator library: */
- struct Library *TranslatorBase;
-
- /* Open the library: */
- TranslatorBase = OpenLibrary( "translator.library", 0 );
-
- /* Have we successfully opened it? */
- if( !TranslatorBase )
- clean_up( "Could not open the translator library!" );
-
-
-
- 5.3.2 TRANSLATE TEXT
-
- Once you have opened the translator device you may start to
- use the Translate() function. This function will work best
- with English sentences, but can be used for many other
- languages if you afterwards do some small changes of the
- phonetic text.
-
- Synopsis: char_left = Translate( in, in_len, out, out_len );
-
- char_left: (long) If not all phonetic text could fit in the
- out string the function will automatically only
- translate the words which will fit. (It will not
- stop in the middle of a word.) If all words could
- be translated zero is returned, else a negative
- value is returned. This value tells us how many
- letters of the "in" string have been translated.
- With this value we can then call the function
- again and translate the remaining text.
-
- Note that the number is negative.
-
- in: (char *) Pointer to the (English) text string
- which should be read.
-
- in_len: (long) The length of the (English) text string.
-
- out: (char *) Pointer to a string where the phonetic
- text string will be stored. (The "in" string is
- converted and copied to the "out" string.)
-
- out_len: (long) The length of the phonetic (out) string.
-
-
- Here is an example on how to translate a string. The nice thing
- is that we can translate strings of any size. If the translated
- string does not fit in the buffer, we divide it up into smaller
- parts.
-
- /* Translated buffer size: */
- #define PHONETIC_BUFFER_SIZE 50
-
- /* Number of characters that were translated, or */
- /* zero if all characters were translated: */
- int char_translated;
-
- /* This variable contains the current position */
- /* in the string which is translated: */
- int current_position;
-
- /* The original string: */
- char *original_string = "The Amiga C Encyclopedia!";
-
- /* The phonetic string: */
- char phonetic_string[ PHONETIC_BUFFER_SIZE ];
-
-
- /* Open the translator library: */
- TranslatorBase = (struct Library *)
- OpenLibrary( "translator.library", 0 );
-
- /* Have we successfully opened the library? */
- if( !TranslatorBase )
- clean_up( "Could not open the translator library!" );
-
-
- /* Start with the first character in the original string: */
- current_position = 0;
-
- /* Translate (parts of) our string into phonetics: */
- char_translated =
- Translate( original_string,
- strlen( original_string ),
- phonetic_string,
- PHONETIC_BUFFER_SIZE );
-
- /* Print the translated part: */
- printf( "%s\n", phonetic_string );
-
-
- /* As long as Translate() does not return zero we stay */
- /* in this while loop and continues to translate the */
- /* original string into phonetics: */
- while( char_translated )
- {
- /* Increase the current position in the original string: */
- /* (Remember that "char_translated" variable is negative, */
- /* and we must therefore use the "-=" operator and not */
- /* the "+=" to increase the current position.[-- = +]) */
- current_position -= char_translated;
-
- /* Translate the following part our string into phonetics: */
- /* (Note that when we put brackets after a string we we */
- /* get the character at the specified position, but since */
- /* we want the address of that position we also have to */
- /* put the pointer "&" sign in front of the string.) */
- char_translated =
- Translate( &original_string[ current_position],
- strlen( &original_string[ current_position] ),
- phonetic_string,
- PHONETIC_BUFFER_SIZE );
-
- /* Print the translated part: */
- printf( "%s\n", phonetic_string );
- }
-
- /* All words have now been translated! */
-
-
-
- 5.3.3 CLOSE THE TRANSLATOR LIBRARY
-
- Before your program terminates it must close the translator
- library. This is especially important with this library since
- it is loaded from the system disk when opened, and if you do
- not close it a lot of memory is wasted. Be careful about this.
-
- As I have said many times, make sure that your program does not
- only terminate nicely when it has reached the end. It must also
- manage to terminate nicely if it suddenly has to quit because of
- some error. If you look at the examples which accompanies this
- manual you will see that most of them use a function called
- "clean_up()". The idea with this function is that it examines
- all resources, and if it finds out that something has been
- opened or allocated it closes it before the program terminates.
- Because it examines all the resources before it attempts to
- close or deallocate them the function can always be called and
- it will clean up everything, even if you only have opened and
- allocated some resources. Try to use something similar in your
- own programs.
-
-
-
- 5.4 READ PHONETIC SYMBOLS
-
- The translator library and the function Translate() are only
- used to convert normal (English) text into phonetical strings.
- These phonetical strings can then be read by the Amiga, but to
- do this you have to use another resource on the Amiga which is
- the famous "Narrator Device". (Tataaa, trumpets and drums!)
-
- The narrator device is very straight forward and easy to use.
- As always you use request blocks to send your commands, and the
- device will reply by sending messages to a reply (message)
- port. The device should of course first be opened by calling
- the OpenDevice() function. When your program wants to terminate
- the device and message port are closed and the request block
- is deallocated.
-
-
-
- 5.4.1 NARRATOR REQUEST BLOCK
-
- When you are using the narrator device you have to use an
- extended request block called "narrator_rb" which is defined
- like this: (Defined in header file "devices/narrator.h".)
-
- struct narrator_rb
- {
- struct IOStdReq message;
- UWORD rate;
- UWORD pitch;
- UWORD mode;
- UWORD sex;
- UBYTE *ch_masks;
- UWORD nm_masks;
- UWORD volume;
- UWORD sampfreq;
- UBYTE mouths;
- UBYTE chanmask;
- UBYTE numchan;
- UBYTE pad;
- };
-
- message: The top part of this request block consists of the
- standard input output request block. This IOStdReq
- block is explained below.
-
- rate: The speaking rate in words per minute. The default
- rate (DEFRATE) has been defined as 150 words per
- minute. The maximum speed (MAXRATE) is 400 words and
- the minimum speed (MINRATE) is 40 words per minute.
-
- pitch: The voice's pitch. This is the medium pitch value.
- Stressed words are spoken with a higher pitch, while
- soft words are spoken with a lower pitch. By
- changing this value the whole voice is affected.
-
- The default pitch (DEFPITCH) is 110. The maximum
- pitch (MAXPITCH) is 320 and the minimum pitch is 65.
-
- mode: This value will affect the way the text is read. If
- you set the flag "NATURALF0" the voice will go up
- and down as the text is read. On the other hand, if
- you set the flag "ROBOTICF0" the voice will be very
- dull and does not change as the text is read.
-
- sex: If the flag "MALE" is set a dark male voice will be
- used. If you instead set the flag "FEMALE" a lighter
- and sharper voice will be used. (Do not ask me why
- they did not define it as "BITCH".)
-
- *ch_masks: This field should be given a pointer to an
- "allocation array" which specifies which audio
- channels that should be used.
-
- There exist four sound channels which can be used.
- The first and last sound channel is connected to the
- left audio port, while the second and third is
- connected to the right audio port.
-
- To specify which channels you want to use you use
- a value where the first bit represents the first
- sound channel, the second bit the second sound
- channel and so on...
-
- Bit: 3 2 1 0
- -------------------------------------
- Value: 8 4 2 1
- Channel: 3 2 1 0
- Audio port: Left Right Right Left
-
- To reserve the first and last channel (both using
- the left sound channel) you would use the value 9
- (1001[bin] = 9[dec]). To reserve the second and
- third channel (both using the right sound channel)
- you would use the value 6 (0110[bin] = 6[dec]).
- To reserve all four sound channels set the value to
- 15 (1111[bin] = 15[dec]).
-
- The allocation array should consist of all the
- channel combinations you like. The narrator device
- will first try to reserve the channels specified by
- the first value. If it could not get these channels,
- it tries the next values until if finds a
- combination it could reserve or it reaches the end
- of the allocation array and the operation fails.
-
- For example. If you want that the text should be
- read in stereo you have to use one audio channel
- from the right and one from the left port. There
- exist four possible combinations which satisfy this,
- and the "allocation array" should therefore look
- like this:
-
- UBYTE allocation_array = { 3, 5, 10,12 };
-
- Dec Bin Description
- ---------------------------------------------------
- 3 0011 First left and first right channel.
- 5 0101 First left and second right channel.
- 10 1010 Second left and first right channel.
- 12 1100 Second left and second right channel.
-
- nm_masks: This field should be given a number which tells the
- narrator device how many values there are in the
- allocation array.
-
- volume: The volume of the voice. The default volume (DEFVOL)
- is the same as the maximum volume (MAXVOL) which is
- defined as 64. The minimum volume (MINVOL) is 0
- which is the same thing as silent.
-
- I myself does not like that the maximum volume is
- often used. It would be much better if everyone were
- using the medium volume as the default value. The
- user will then turn up the volume on his/her stereo
- to compensate for the lower volume. The advantage is
- that if you now suddenly play a very loud sound or
- use a very loud voice it will also be very loud.
-
- Imagine a game which is normally using a sound level
- of 32. The user is sitting alone in his/her own room,
- and it is very late. Imagine what would happen if a
- monster would appear in the the game and at the same
- time the volume was increased to maximum. It will
- undoubtedly be a nice (hmmm) surprise for the user.
-
- sampfreq: This field specifies which frequency should be used.
- The default frequency (DEFFREQ) is 22200. The
- maximum value (MAXFREQ) is 28000 and the minimum
- value (MINFREQ) is 5000.
-
- mouths: Normally this field should be set to zero. However,
- of you are going to use the "mouth" request, as
- explained below, you should set a non zero value.
-
- chanmask: This field contains the value which was used of the
- allocation array to reserve the audio channel(s).
- This field is used by the system, but if you like
- you may examine it, but you may never change it.
-
- numchan: Number of channels used. This field is also used by
- the system, but if you like you may examine it, but
- you may never change it.
-
- pad: This field is not used, and should therefore never
- be used.
-
-
- The IOStdReq structure (which is a part of the narrator_rb
- structure) is defined in the "exec/io.h" header file like this:
- (Only some parts of this structure will be used, so you do not
- have bother too much about it.)
-
- struct IOStdReq
- {
- struct Message io_Message;
- struct Device *io_Device;
- struct Unit *io_Unit;
- UWORD io_Command;
- UBYTE io_Flags;
- BYTE io_Error;
- ULONG io_Actual;
- ULONG io_Length;
- APTR io_Data;
- ULONG io_Offset;
- };
-
- io_Message: The top part consists of a Message structure which
- will be sent to us when the request has been
- completed (successfully or not). This message
- structure will automatically be given a pointer to
- the reply port when the narrator_rb structure is
- created, and the priority will automatically be set
- when used, so we do not have to bother too much
- about it.
-
- io_Device: This field will automatically be initialized when
- you use this request block together with an
- OpenDevice() function call. It is simply a pointer
- to the device which this request block is made for.
-
- io_Unit: This field will automatically be initialized when
- you send the request block to the narrator device.
-
- io_Command: The following commands are accepted by the narrator
- device, and may be used: (Will be explained below.)
-
- CMD_WRITE Start to speak.
- CMD_STOP Stop all speech requests.
- CMD_START Start to speak again.
- CMD_FLUSH Remove all queued requests.
- CMD_RESET Reset the narrator device.
-
- io_Flags: No flags are used.
-
- io_Error: If the request can not successfully be executed by
- the narrator device, it will be returned with one
- of the following error flags:
-
- ND_NoMem Not enough memory for the request.
- ND_NoAudLib Can not open the audio device.
- ND_MakeBad Can not execute the MakeLibrary()
- function.
- ND_UnitErr Wrong unit number (not 0).
- ND_CantAlloc Can not find any free audio channels
- to reserve.
- ND_Unimpl Not a valid command.
- ND_NoWrite Nothing is being read, so you can
- not read any "mouth" values.
- ND_Expunged Wrong expunge value.
- ND_PhonErr Wrong phonetic code.
- ND_RateErr Too large or small rate value
- ND_PitchErr Too large or small pitch value
- ND_SexErr Wrong sex number used.
- ND_ModeErr Wrong mode value used.
- ND_FreqErr Too high or low frequency value.
- ND_VolErr Too high or low volume value.
-
- io_Actual: Used by the system.
-
- io_Length: The number of characters in the phonetic string
- that should be read.
-
- io_Data: Pointer to the phonetic sting.
-
- io_Offset: Used by the system.
-
-
-
- 5.4.2 OPEN THE NARRATOR DEVICE
-
- To open the narrator device does not differ from opening other
- devices. First you have to create a message port to which the
- device can send messages to you. Secondly you have to create
- one or more narrator_rb structures by using the CreateExtIO()
- function. Finally you can open the narrator device with help
- of one of the request blocks.
-
- 1. Open a message port. Since it is only our task and the
- narrator device that will use the message port, we do not
- need to make it "public", therefore no name. Priority
- should as usual be set to 0, normal priority.
-
- struct MsgPort *replymp;
-
- replymp = (struct MsgPort *)
- CreatePort( NULL, 0 );
-
- if( !replymp )
- clean_up( "Could not create the reply port!" );
-
-
- 2. Allocate a request block of type narrator_rb structure.
- The narrator_rb structure is an extended version of the
- normal request block, and should therefore be allocated
- with help of the CreateExtIO() function with the size
- set to sizeof( struct narrator_rb ).
-
- struct narrator_rb *narrator_req;
-
- narrator_req = (struct narrator_rb *)
- CreateExtIO( replymp, sizeof( struct narrator_rb ) );
-
- if( !narrator_req )
- clean_up( "Not enough memory!" );
-
-
- 3. Once the message port and the request block have
- successfully been created you can "open" (gain access to)
- the narrator device. The default values will now also be
- set in the request block.
-
- error = OpenDevice( "narrator.device", 0, narrator_req, 0 );
-
- if( error )
- clean_up( "Could not open the Narrator Device!" );
-
-
-
- 5.4.3 READ PHONETIC TEXT
-
- Once you have successfully opened the narrator device you may
- start to use the narrator device. The fields of the request
- block with which the narrator device was opened with have
- automatically been given the default values:
-
- 1. The rate is set to 150 words per minute.
- 2. The pitch is set to 110 Hz.
- 3. The mode is set to normal (living) voice.
- 4. The sex field is set to man.
- 5. The sampfreq is set to 22200.
- 6. The volume is set to 64.
-
- The following fields must be initialized by yourself:
-
- 1. You must give the "io_Data" field of the IOStdReq
- structure a pointer to the string of phonetic words that
- should be read.
-
- 2. Give the "io_Length" field of the IOStdReq structure the
- number of translated characters that should be read.
- You can use the function strlen() to find out how long
- the translated string is.
-
- 3. You have to give the "ch_masks" field a pointer to the
- allocation array that should be used.
-
- 4. Give the "nm_masks" field the number of values which exist
- in the allocation array.
-
- 5. Finally you should give the "io_Command" field of the
- IOStdReq structure the "CMD_WRITE" flag. (We are going to
- send [write] text to the narrator device.)
-
-
- Here is an example:
-
-
- /* The text should be read in stereo: */
- UBYTE allocation_array[]=
- {
- LEFT0F|RIGHT0F, /* First left and first right channel. */
- LEFT0F|RIGHT1F, /* First left and second right channel. */
- LEFT1F|RIGHT0F, /* Second left and first right channel. */
- LEFT1F|RIGHT1F /* Second left and second right channel. */
- };
-
- /* The phonetic string: */
- char *phonetic_string = "/HEH4LOW";
-
-
- /* Message port opened... */
- /* "narrator_rb" created... */
- /* Narrator device opened... */
-
-
- /* Set our requirements: */
-
- /* 1. Give it a pointer to the phonetic string: */
- narrator_req->message.io_Data = (APTR) phonetic_string;
-
- /* 2. Set the length of the phonetic string: */
- narrator_req->message.io_Length = strlen( phonetic_string );
-
- /* 3. Desired channel combinations: */
- narrator_req->ch_masks = allocation_array;
-
- /* 4. Size of the allocation array: */
- narrator_req->nm_masks = sizeof( allocation_array );
-
- /* 5. Send (write) the text to the device: */
- narrator_req->message.io_Command = CMD_WRITE;
-
-
- You may of course also change the other values if you do not
- want to use the default ones.
-
- After the request has been initialized can it be sent to the
- narrator device which will read the phonetic string. To send
- request you can either use the synchronous command DoIO() or
- the asynchronous command SendIO().
-
- The DoIO() function will send the request to the device and
- put your program to sleep. When the device has finished your
- request it is returned and your program wakes up. If something
- failed will DoIO() return an error number and a copy of their
- value will also be stored in the "io_Error" field of the
- request block.
-
- The SendIO() function should be used when you want to continue
- to do something while the narrator device is reading the text.
- After you have called SendIO() you can put your task to sleep
- by either using the WaitIO() or Wait() function. If you want
- to check if the request has been completed you can use the
- CheckIO() function.
-
- All these functions have been described in chapter "Devices",
- and will therefore not be repeated here.
-
- An example on a synchronous call: (Your program is put to sleep
- while the narrator device completes your request.)
-
- BYTE error;
-
- /* ... */
-
- /* Read the text: */
- error = DoIO( narrator_req );
-
- /* Were there any errors? */
- if( error )
- clean_up( "Problems with reading the text!" );
-
-
- An example on an asynchronous call: (Your program continues to
- run while the text is read.)
-
-
- /* Start to read: */
- SendIO( narrator_req );
-
- while( !CheckIO( narrator_req ) )
- {
- /* Do something... */
- }
-
- /* Collect message at the reply port... */
-
- /* Were there any errors? */
- if( narrator_req->message.io_Error )
- clean_up( "Problems with reading the text!" );
-
-
-
- 5.4.4 USING SEVERAL REQUEST BLOCKS
-
- If you have created several request blocks will only the one
- which you opened the narrator device with be correctly
- preinitialized. These structures which have not been initialized
- must be prepared before they may be used. The simplest solution
- is actually to copy the whole structure, because you will then
- be sure that no values are forgotten.
-
- Here is an example:
-
- /* Source and destination pointers: */
- BYTE *first_ptr;
- BYTE *second_ptr;
-
-
- /* Allocate request blocks, open narrator device, etc... */
- /* (The two request blocks are called "first_narrator_req" */
- /* and "second_narrator_req".) */
-
- /* Copy the first request block to the second one: */
-
- /* Get the start addresses of both request blocks: */
- first_ptr = (BYTE *) first_narrator_req;
- second_ptr = (BYTE *) second_narrator_req;
-
- /* Copy byte by byte: */
- for( loop = 0; loop < sizeof( struct narrator_rb ); loop++ )
- {
- /* Copy: */
- *second_ptr = *first_ptr;
-
- /* Next byte: */
- first_ptr++;
- second_ptr++;
- }
-
-
-
- 5.4.5 CLEAN UP
-
- When your program terminates you have to clean up and free all
- allocated resources. This is what has to be done:
-
- 1. Remove any messages still left at the message (reply) port.
-
- 2. Close the message (reply) port(s).
-
- 3. Close the narrator device.
-
- 4. Deallocate the request blocks.
-
-
-
- 5.4.5.1 REMOVE ALL MESSAGES
-
- Before you close anything you should first remove all messages
- from the message (reply) port. A short while loop that removes
- the messages until it can not find any more is a simple but
- effective solution.
-
- while( GetMsg( replymp ) )
- printf( "Collected a message at the reply port.\n" );
-
-
-
- 5.4.5.2 CLOSE MESSAGE PORT
-
- After all messages have been removed from the port you may close
- it by calling the DeletePort() function. Note that you should
- never try to close a message port you have not opened.
-
- DeletePort( replymp);
-
-
-
- 5.4.5.3 CLOSE THE NARRATOR DEVICE
-
- The narrator device itself must of course also be closed if you
- have opened it. Note that you should never try to close a
- device you have not opened.
-
- CloseDevice( narrator_req );
-
-
-
- 5.4.4 DEALLOCATE THE REQUEST BLOCKS
-
- Finally you should deallocate all request blocks you have
- created. Use the DeleteExtIO() function. The size should be set
- to the size of a narrator_rb structure. Just remember to close
- the narrator device before you deallocate the request block(s).
-
- DeleteExtIO( narrator_req, sizeof( struct narrator_rb ) );
-
-
-
- 5.5 THE NARRATOR'S MOUTH
-
- The narrator device offers a rather unique but sometimes useful
- feature. The narrator device can be used to draw a mouth as the
- text is read. It may sound a bit strange, but if you are using
- animations this can be very helpful.
-
- The narrator device will tell us how wide and high the mouth
- should be, and with this we can easily construct a simple
- mouth.
-
-
-
- 5.5.1 MOUTH REQUEST BLOCK
-
- To use this "mouth" feature you have to use a special request
- block which is called "mouth_rb", and looks like this: (Defined
- in header file "devices/narrator.h".)
-
- struct mouth_rb
- {
- struct narrator_rb voice;
- UBYTE width;
- UBYTE height;
- UBYTE shape;
- UBYTE pad;
- };
-
- voice: The top part of the mouth block consists of a
- "narrator_rb" structure. When you create this structure
- the message port will automatically be initialized.
- The following fields must however be initialized by
- yourself:
-
- 1. You must copy the "io_Device" pointer from the
- request which is reading the text.
-
- 2. The "io_Unit" number must also be copied.
-
- 3. The "io_Error" field should be set to zero.
-
- 4. Finally you should set the command "CMD_READ"
- in the "io_Command" field.
-
- After you have sent this request block and it is
- returned you can examine the "io_Error" field to
- check if there were any problems. See above for a
- complete list of error flags.
-
- When you are using this request, it will be returned to
- you if one of the following thing has happened:
-
- 1. The size of the mouth has changed.
-
- 2. Something went wrong, and the "io_Error" field will
- contain an error flag. If you have received the
- "ND_NoWrite" the Amiga has stopped talking, and
- you should not try to draw any mouth any more.
-
- width: After you have successfully executed this request you
- can examine this field to check how wide the mouth
- should be. The request block will only be returned when
- the size of the mouth has changed, or the Amiga has
- stopped talking.
-
- height: This field will contain the height of the mouth.
-
- shape: This field may only be used by the system, and should
- never be changed.
-
- pad: No used, and should not be used.
-
-
-
- 5.5.2 CREATE A MOUTH REQUEST BLOCK
-
- Since the size of the mouth_rb is larger than the standard
- sized request block you have to use the CreateExtIO() function
- to allocate and preinitialize the request block. Normally it
- is easiest to connect this structure to the same message port
- as the other request blocks are connected to. You can of course
- use a separate message port if you want.
-
- struct mouth_rb *mouth_req;
-
- mouth_req = (struct mouth_rb *)
- CreateExtIO( replymp, sizeof( struct mouth_rb ) );
-
- if( !mouth_req )
- clean_up( "Not enough memory!" );
-
-
-
- 5.5.3 PREPARE THE MOUTH REQUEST BLOCK
-
- After you have created the mouth_rb structure you have to copy
- some values, as explained above, from the request block which
- will read the text. You must also set some values yourself.
-
- Here is an example on how to initilaize the mouth request:
-
- /* Set the mouth width and height to zero: */
- mouth_req->width = 0;
- mouth_req->height = 0;
-
- /* Give the mouth request a pointer to the narrator device: */
- mouth_req->voice.message.io_Device = narrator_req->message.io_Device;
-
- /* Give the mouth request the current unit number: */
- mouth_req->voice.message.io_Unit = narrator_req->message.io_Unit;
-
- /* No error number (so far): */
- mouth_req->voice.message.io_Error = 0;
-
- /* The mouth request should look at (read) the request */
- /* which is currently talking: */
- mouth_req->voice.message.io_Command = CMD_READ;
-
- The mouth_rb request block can now be used.
-
-
-
- 5.5.4 GET THE SIZE OF THE MOUTH
-
- The mouth request block should be sent to the narrator device
- after you have sent a normal read request. If the device is
- currently not reading any text the mouth request will
- immediately be returned with the error flag "ND_NoWrite" set.
-
- If the device is reading some text the mouth request will first
- be returned when the mouth has changed size, or if the narrator
- device has reached the end of the text string. If the device
- reached the end of the string and stops talking the mouth
- request will be returned with the error flag "ND_NoWrite" set.
- However, if the request is returned and no error flag is set,
- the mouth has changed size and should be redrawn.
-
- Since your program must be able to work while the narrator
- device is reading the text you have to use the asynchronous
- command SendIO() to start the read request. Here is an example
- on how to use the narrator's mouth:
-
-
- /* TRUE as long the Amiga is reading text: */
- BOOL still_talking;
-
- /* ... */
-
- /* Start to read: (The read request must be sent by */
- /* the asynchronous command SendIO().) */
- SendIO( narrator_req );
-
- /* The Amiga is reading the text: */
- still_talking = TRUE;
-
- /* As long as the Amiga is reading the text */
- /* we stay in the while loop: */
- while( still_talking )
- {
- /* Send the mouth request to the narrator device. The request */
- /* will be returned when the mouth width and/or height have */
- /* changed, or the device has stopped reading the text: */
- DoIO( mouth_req );
-
- /* Has the device stopped reading the text: */
- if( mouth_req->voice.message.io_Error == ND_NoWrite )
- still_talking = FALSE;
- else
- {
- /* No, the device is still reading. The mouth must have */
- /* changed, so we better redraw it: */
-
- /* Draw the new mouth... */
- }
- }
-
- /* We know that the Amiga has stopped speaking, */
- /* and hence we do not have to wait for the */
- /* narrator to finish reading. We should however */
- /* check if we have successfully read the text: */
- if( narrator_req->message.io_Error )
- clean_up( "Error while reading!" );
-
-
-
- 5.6 EXAMPLES
-
- Example 1
- This very simple example demonstrates how to open the
- translator library, translate a string, and finally close
- the library before the program terminates.
-
- Example 2
- This example demonstrates how you can use a while loop to
- translate parts of a string until the whole string has been
- translated.
-
- Example 3
- This example demonstrates how to translate a string into a
- phonetical string which is then read by the narrator device.
-
- Example 4
- This example very similar to the previous one, but this time
- are we using a different voice. By altering the rate, pitch,
- mode, sex and volume, you can produce very different sounds.
-
- Example 5
- This example demonstrates how you can let the Amiga read
- small stories. By altering the rate, pitch, mode, sex and
- volume parameters it can sound like several persons are
- talking. It can also be used to express emotions and stress
- important parts of the text.
-
- This example is using some home made functions which makes
- life a little bit easier. If you have to read a lot of text I
- recommend you to use special functions like these. It will
- then be much easier to write (and read) the program code.
-
- Example6
- This example demonstrates how to use the mouth request block
- to draw a talking mouth.
-