Search code examples
javavoicexmlrivr

"Wait a second" message in Rivr VoiceXML for Java


I need to play a “Wait a second…” message right after an interaction. I need this as I have to perform some time consuming tasks before going back to the user. The app flow is:

1) Play Welcome message (TTS) 2) Collect voice from user (Rivr voice record interaction) 3) Play "wait a second" TTS message as processing of voice and related business process is time consuming (takes several seconds) 4) [Time consuming task] 5) Play (TTS) the result of the process and say good bye.

Everything is working OK but the "Wait a second" message is played right after the time consuming tasks together with the synthesised result of the process (I mean, user speaks and has to wait), even though in my Dialog code it is placed before the "Time Consuming task". For some reason either Rivr or VoiceXML engine is buffering both messages (3 and 5) playing them together.

How can I make Rivr "flush" step 3 and play the "Wait a second" message right after the Recording interaction so user knows he should wait a moment?


Solution

  • Prompt queueing in VoiceXML

    In VoiceXML, whenever a prompt is executed, it is in fact placed in a queue. The VoiceXML interpreter will flush the prompt queue (i.e. play it) only when waiting for a user input or when exiting. This behaviour is described in details in the Prompt Queueing and Input Collection section of the VoiceXML W3C documentation.

    In your case, your prompt is queue before doing the long operation and it is played only during the next interaction. This is a typical situation and there are classical VoiceXML solutions having their equivalent in Rivr.

    Solution 1: Force the Prompt Queue to be Flushed Using fetchaudio

    The VoiceXML specifications states that the prompt queue will be flushed...

    when the interpreter begins fetching a resource (such as a document) for which fetchaudio was specified. In this case the prompts queued before the fetchaudio are played to completion, and then, if the resource actually needs to be fetched (i.e. it is not unexpired in the cache), the fetchaudio is played until the fetch completes.

    So to make sure the prompts will be played, one can simply set a fetchaudio on the <submit> element used to obtain the result of the long operation. The effect is that the "Please wait a moment." message will be played and then, the file specified in the fetchaudio will be played in a loop until the server returns the result. Typically, you would use a sound suggesting that something is being processed.

    If you don't want anything to be heard while the VoiceXML is waiting for the operation to finish, you can provide an audio file with silence in it. Another hack would be to specify a file that doesn't exist. This works under some VoiceXML plaform. YMMV.

    The VoiceXML could look like that:

    <?xml version="1.0" encoding="UTF-8"?>
    <vxml version="2.1" xmlns="http://www.w3.org/2001/vxml">
      <form id="form">
        <block>
          <prompt>Please wait a moment.</prompt>
          <submit fetchaudio="/audio/fetch.wav" method="post" next="/long-operation" />
        </block>
      </form>
    </vxml>
    

    With Rivr, it's:

    context.getFetchConfiguration().getDocumentFetchConfiguration()
      .setFetchAudio("/audio/fetch.wav");
    
    Message message = new Message("wait-message", 
      new SpeechSynthesis("Please wait a moment."));
    
    DialogueUtils.doTurn(message, context);
    performLongOperation();
    

    Solution 2: Insert an artificial waiting state

    Another trick to force prompt queue to be played is to create a dummy interaction having timeout of 0. This is force the interpreter to play the messages. We must be careful to make the disable barge-in on the prompt otherwise a DTMF input could interrupt the message. Here's an example of a dummy input:

    <?xml version="1.0" encoding="UTF-8"?>
    <vxml version="2.1" xmlns="http://www.w3.org/2001/vxml">
      <form id="form">
    
        <field name="dummy">
          <property name="timeout" value="0ms" />
          <grammar src="builtin:dtmf/digits" />
          <prompt>Please wait a moment.</prompt>
          <filled>
            <goto nextitem="submit" />
          </filled>
          <noinput>
            <goto nextitem="submit" />
          </noinput>
          <nomatch>
            <goto nextitem="submit" />
          </nomatch>
        </field>
    
        <block name="submit">
          <submit fetchaudio="audio/fetch.wav" method="post" next="/long-operation" />
        </block>
    
      </form>
    </vxml>
    

    And here's the Rivr equivalent:

    DtmfRecognition dummyRecognition = new DtmfRecognition(new GrammarReference("builtin:dtmf/digits"));
    
    SpeechSynthesis message = new SpeechSynthesis("Please wait a moment.");
    
    Interaction interaction = OutputTurns.interaction("wait-message")
                    .addPrompt(dummyRecognition, message).build();
    
    DialogueUtils.doTurn(interaction, context);
    performLongOperation();
    

    And if you want to reuse this pattern in your application, you can make a function:

    private void forcePlayMessage(VoiceXmlDialogueContext context,
                                  String messageName,
                                  AudioItem... audioItems)
                    throws Timeout, InterruptedException {
        DtmfRecognition dummyRecognition = new DtmfRecognition(new GrammarReference("builtin:dtmf/digits"));
        Interaction interaction = OutputTurns.interaction(messageName)
                .addPrompt(dummyRecognition, audioItems).build();
        DialogueUtils.doTurn(interaction, context);
    }
    

    If the operation is very long, it might be wise to use Java FutureTask class to process you request in the background, allowing you dialog to send messages to you caller every X seconds instead of blocking on performLongOperation()