All read requests to the narrator device must be matched to an associated write request. This is done by copying the narrator_rb structure used in the OpenDevice() call into the voice field of the mouth_rb I/O request structure. You must do this after the call to OpenDevice(). Matching the read and write requests allows the narrator device to coordinate I/O requests across multiple uses of the device. In pre-V37 versions of the narrator device, only mouth shape changes can be queried from the device. This is done by setting the mouths field of the narrator_rb I/O request structure (the write request) to a non-zero value. The write request is then sent asynchronously to the device and while it is in progress, synchronous read requests are sent to the device using the mouth_rb I/O request structure. When the mouth shape has changed, the device will return the read request to the user with bit 0 set in the sync field of the mouth_rb. The fields width and height of the mouth_rb structure will contain byte values which are proportional to the actual width and height of the mouth for the phoneme currently being spoken. Read requests sent to the narrator device are not returned to the user until one of two things happen: either the mouth shape has changed (this prevents the user from having to constantly redraw the same mouth shape), or the speech has completed. The user can check io_Error to determine if the mouth shape has changed (a return code of 0) or if the speech has completed (return code of ND_NoWrite). In addition to returning mouth shapes, reads to the V37 narrator device can also perform two new functions: word and syllable sync. To generate word and/or syllable sync events, the user must specify several bits in the flags field of the write request (narrator_rb structure). The bits are NDB_WORDSYNC and NDB_SYLSYNC, for start of word and start of syllable synchronization events, respectively, and, of course, NDB_NEWIORB, to indicate that the V37 I/O request is required. NDB_WORDSYNC and NDB_SYLSYNC tell the device to expect read requests and to generate the appropriate event(s). As with mouth shape change events, the write request is sent asynchronously to the device and, while it is in progress, synchronous read requests are sent to the device. The sync field of the mouth_rb structure will contain flags indicating which events (mouth shape changes, word sync, and/or syllable sync) have occurred. The returned sync field flags are: bit 0 (0x01) -> mouth shape change event bit 1 (0x02) -> start-of-word synchronization event bit 2 (0x04) -> start-of-syllable synchronization event and 1 or more flags may be set for any particular read. As with mouth shape changes, read requests will not return until the requested event(s) have occurred, and the user must test the io_Error field of the mouth_rb structure to tell when the speech has completed (an error return of ND_NoWrite). Several read events can be compressed into a single event. This can occur in two ways: first when two dissimilar events occur between two successive read requests. For example, a single read may return both a mouth change and a syllable sync event. This should not present a problem if the user checks for all events. The second is when multiple events of the same type occur between successive read requests. This is of no great concern in dealing with mouth shape changes because, presumably, mouth events are used to drive animation, and the animation procedure will simply draw the current mouth shape. Watch Those Sync Events. ------------------------ When word or syllable sync is desired, the narrator device may compress multiple sync events into a single sync event. Missing a word or syllable sync may cause word highlighting (for example) to lose sync with the speech output. A future version of the device will include an extension to the mouth_rb I/O request structure which will contain word and syllable counts and, possibly, other synchronization methods. The following code fragment shows the basics of how to perform reads from the narrator device. For a more complete example, see the sample program at the end of this chapter. For this fragment, take the code of the previous write example as a starting point. Then the following code would need to be added: struct mouth_rb *MouthIO; /* Pointer to read IORequest block */ struct MsgPort *MouthMP; /* Pointer to read message port */ /* * (1) Create a message port for the read request. */ if (!(MouthMP = CreatePort("narrator_read", 0L))) BellyUp("Read CreatePort failed"); /* * (2) Create an extended IORequest of type mouth_rb. */ if (!(MouthIO = (struct mouth_rb *) CreateExtIO(MouthMP, sizeof(struct mouth_rb)))) BellyUp("Read CreateExtIO failed"); /* * (3) Set up the read IORequest. Do this after the call to OpenDevice(). * We assume that the write IORequest and the OpenDevice have been done */ MouthIO->voice = *SpeakIO; MouthIO->voice.message.io_Message.mn_ReplyPort = ReadMsgPort; MouthIO->voice.message.io_Command = CMD_READ; /* * (4) Set the flags field of the narrator_rb write request to return the * desired sync events. If mouth shape changes are required, then the * mouths field of the IORequest should be set to a non-zero value. */ SpeakIO->mouths = 1; /* Generate mouth shape changes */ SpeakIO->flags = NDF_NEWIORB | /* Indicates V37 style IORequest */ NDF_WORDSYNC | /* Request start-of-word sync events */ NDF_SYLSYNC; /* Request start-of-syll sync events */ /* * (5) Issue asynchronous write request. The driver initiates the write * request and returns immediately. */ SendIO(SpeakIO); /* * (6) Issue synchronous read requests. For each request we check the sync * field to see which events have occurred. Since any combination of * events can be returned in a single read, we must check all * possibilities. We continue looping until the read request returns * an error of ND_NoWrite, which indicates that the write request has * completed. */ for (DoIO(MouthIO);MouthIO->voice.message.io_Error != ND_NoWrite;DoIO(MouthIO)) { if (MouthIO->sync & 0x01) DoMouthShape(); if (MouthIO->sync & 0x02) DoWordSync(); if (MouthIO->sync & 0x04) DoSyllableSync(); } /* * (7) Finally, we must perform a WaitIO() on the original write request. */ WaitIO(SpeakIO);