If your script uses a GUI add-on, such as REXX GUI, then it's advisable that you use asynchronous operation with SpeechSpeak(). This may help avoid a situation where the user is operating controls in your window while the text is speaking, but your script does not appear to be servicing those actions.

With aynchronous operation, SpeechSpeak() will not actually start the speaking of the text. Rather, it will return immediately with the text queued (ie, waiting) to be spoken. The text will not actually start speaking until such time as you call your GUI's function to service messages. For example, REXX GUI's function to do that is GuiGetMsg(), so the text will not start until your next call to GuiGetMsg().

When you call your GUI's function to service messages, the text will start speaking in the background, and your script can go on to service other messages/events that happen with your GUI (such as the user clicking on controls in one of your windows). The text will finish speaking on its own.

If it is important for you to know when the text has actually started, and then finished, speaking, you can choose to have the speech engine send a message to one of your windows. You set this up by passing the handle of one of your windows to SpeechOpen(). You can also pass a message number that you would like to be used for the message sent to you (so that there is no conflict with any other message numbers you may be using with other add-on DLLs). By default, the message number used is WM_APP + 1000 (ie, 33768). When the text starts playing, a message is sent to your window with that message number. You can look for, and process, these messages in the procedure for your window. (For REXX GUI, you would look for message number 33768 in your WM_EXTRA subroutine). You can then pass ARG(1) and ARG(2) to SpeechEvent which will return the string "START" or "STOP", depending upon whether playback has started or stopped.


Stop/Pause/Resume asynchronous operation

If aynchronous operation is used with SpeechSpeak(), then the text will be playing in the background. During playback, you can call SpeechControl() to stop the speaking, or pause it, or resume speaking that was previously paused.

Here is an example of a REXX Gui script that presents a window with a box into which you can type some text to speak. When you click on the Speak button, the text is spoken. This demonstrates asynchronous speaking.

/*
GUIBEGIN
WINDOW , 54, 263, 257, 54, POPUP | CAPTION | SYSMENU | MINBOX | MAXBOX | THICK, , REXX Speech example
   FONT 8, 400, MS Shell Dlg
   TEXT 6, 7, 72, 8, GROUP, , , , Type phrase to speak:
   ENTRY 79, 3, 175, 16, H_AUTO | BORDER | TABSTOP, CLIENTEDGE, SpeakText
   TEXT 5, 45, 247, 8, GROUP, , SpeakDone
   PUSH 140, 24, 40, 14, DEFAULT | TABSTOP, , SpeakButton, ALT "S", &Speak
   PUSH 40, 24, 80, 14, TABSTOP, , SpeakNewVoice, ALT "P", &Pick Voice
DEND
GUIEND
*/

OPTIONS 'C_CALL'

/* Load and register functions in REXXGUI.DLL and REXXSPEECH.DLL. */
LIBRARY rexxgui, rexxspeech

GuiErr = 'SYNTAX'
GuiHeading = 1
GuiCreateWindow('NORMAL')

/* Let the user pick out an initial voice to use, and open that voice. */
voice = ChooseSpeakingVoice()
IF voice == 0 THEN RETURN

Again:
DO FOREVER
   GuiGetMsg()
   IF EXISTS('GuiObject') == 0 THEN DO
      IF EXISTS('GuiSignal') THEN DO

         /* Was it our SpeakButton's WM_CLICK that called GuiWake()? */
         IF GuiSignal == 'SpeakButton' THEN DO

            /* SpeakText has been set to the text to speak. Speak it now. NOTE:
             * We use asynchronous mode. SpeechSpeak() will return immediately
             * and we can continue on with our message loop while the text is
             * speaking in the background. When the text is done, our window's
             * WM_EXTRA subroutine will be called with a message number of
             * 33768. If there's an error starting the speech, then SpeechSpeak()
             * will return that now, and we'll display it in our window.
             */
            error = SpeechSpeak(voice, SpeakText, "A")
            IF error \== "" THEN GuiAddCtlText("SpeakDone", "Error speaking text:" error)
         END
      END
   END

   /* We have no Child Window Layout scripts, so we can skip any further checking
    * of GuiSignal and GuiObject.
    */

    CATCH SYNTAX
      IF GuiInfo() \== "" THEN DO /* End this script if all windows are closed */
         CONDITION('M')
         SIGNAL Again
      END

    FINALLY
      /* Close the speech voice. */
      SpeechClose(voice)

      GuiDestroyWindow()
END
RETURN

ChooseSpeakingVoice: PROCEDURE EXPOSE GuiWindow

   /* Let the user pick out a voice to use. NOTE: We pass our REXX GUI window
    * handle created above to let the speech dialog be tied to that window.
    */
   id = SpeechVoiceDlg(GuiWindow)
   IF id == "" THEN DO
      GuiSay("Error getting speech device ID, or user cancelled")
      RETURN 0
   END

   /* Open that speech engine, and save its voice parameter. NOTE: We pass
    * our REXX GUI window handle created above to have the speech engine
    * cause its events in our window. One such event happens when a phrase
    * is done speaking.
    */
   voice = SpeechOpen(id, GuiWindow)
   IF voice == "" THEN DO
      GuiSay("Error opening/initializing the speech voice!")
      RETURN 0
   END

   RETURN voice

/* Called by Reginald when the user clicks on the "Pick Voice" button. */
WM_CLICK_SpeakNewVoice:

   /* First close whatever voice we already have open. This is safe
    * to call even if the voice we pass was never opened.
    */
   SpeechClose(voice)

   /* Now let the user pick a new voice and open it. */
   voice = ChooseSpeakingVoice()

   RETURN

/* Called by Reginald when the user clicks on the "Speak" button. */
WM_CLICK_SpeakButton:

   /* Get the text typed by the user. */
   GuiGetCtlValue("SpeakText")

   /* Let our main message loop return from GuiGetMsg(). Set GuiSignal to
    * "SpeakButton" so it knows that the user clicked the "Speak" button.
    */
   GuiWake("SpeakButton")

   RETURN

/* Called by Reginald when our window receives an event that REXX GUI itself
 * doesn't know about. REXX GUI does not know about speech engine events,
 * so it calls our "EXTRA" event handler. It passes two args, which are the
 * data for the event. The third arg is the message number. For a speech
 * engine event, the default message number is 33768.
 */
WM_EXTRA:
   /* Is it a message from the Speech engine? */
   IF ARG(3) == 33768 THEN DO

      /* Call SpeechEvent to deduce what type of speech event happened */
      IF SpeechEvent(voice, ARG(1), ARG(2)) == "START" THEN

         /* Display a message that the text is speaking. */
         GuiAddCtlText("SpeakDone", "Speaking...")

      /* Otherwise first arg = 2, which means text is done speaking. */
      ELSE

         /* Display a message that the text is done speaking. */
         GuiAddCtlText("SpeakDone", "Done speaking text.")

   END

   /* Don't let Rexx Gui process this event. */
   RETURN ""