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()