/*
* JBoss, Home of Professional Open Source
* Copyright 2011, Red Hat, Inc. and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.restcomm.media.control.mgcp.pkg.au;
import java.net.MalformedURLException;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.log4j.Logger;
import org.restcomm.media.ComponentType;
import org.restcomm.media.control.mgcp.controller.signal.Event;
import org.restcomm.media.control.mgcp.controller.signal.NotifyImmediately;
import org.restcomm.media.control.mgcp.controller.signal.Signal;
import org.restcomm.media.scheduler.PriorityQueueScheduler;
import org.restcomm.media.scheduler.Task;
import org.restcomm.media.spi.MediaType;
import org.restcomm.media.spi.ResourceUnavailableException;
import org.restcomm.media.spi.dtmf.DtmfDetector;
import org.restcomm.media.spi.listener.TooManyListenersException;
import org.restcomm.media.spi.player.Player;
import org.restcomm.media.spi.player.PlayerEvent;
import org.restcomm.media.spi.player.PlayerListener;
import org.restcomm.media.spi.utils.Text;
/**
* Implements play announcement signal.
*
* Plays a prompt and collects DTMF digits entered by a user. If no digits are entered or an invalid digit pattern is entered,
* the user may be reprompted and given another chance to enter a correct pattern of digits. The following digits are supported:
* 0-9, *, #, A, B, C, D. By default PlayCollect does not play an initial prompt, makes only one attempt to collect digits, and
* therefore functions as a simple Collect operation. Various special purpose keys, key sequences, and key sets can be defined
* for use during the PlayCollect operation.
*
*
* @author oifa yulian
* @author Henrique Rosa (henrique.rosa@telestax.com)
*/
public class PlayCollect extends Signal {
private final static Logger logger = Logger.getLogger(PlayCollect.class);
// MGCP Responses
private static final Text RC_300 = new Text("rc=300");
private static final Text RC_301 = new Text("rc=301");
private final Event oc;
private final Event of;
// Core Components
private PriorityQueueScheduler scheduler;
// Media Components
private Player player;
private DtmfDetector dtmfDetector;
private Options options;
private final EventBuffer buffer;
private final PromptHandler promptHandler;
private final DtmfHandler dtmfHandler;
// PlayCollect Status
private volatile boolean isPromptActive;
private Text[] prompt;
private int promptLength;
private int promptIndex;
private long firstDigitTimer;
private long nextDigitTimer;
private int maxDuration;
private int numberOfAttempts;
private int segCount = 0;
private PlayerMode playerMode;
private Text eventContent;
private Heartbeat heartbeat;
// Listeners
private boolean dtmfListenerAdded = false;
private boolean playerListenerAdded = false;
// Concurrency
private final AtomicBoolean terminated;
private final Object LOCK;
public PlayCollect(String name) {
super(name);
// MGCP Responses
this.oc = new Event(new Text("oc"));
this.of = new Event(new Text("of"));
this.oc.add(new NotifyImmediately("N"));
this.oc.add(new InteruptPrompt("S", player));
this.of.add(new NotifyImmediately("N"));
// Media Components
this.dtmfHandler = new DtmfHandler(this);
this.promptHandler = new PromptHandler(this);
this.buffer = new EventBuffer();
// PlayCollect Status
this.isPromptActive = false;
this.prompt = new Text[10];
this.promptLength = 0;
this.promptIndex = 0;
this.firstDigitTimer = 0L;
this.nextDigitTimer = 0L;
this.maxDuration = 0;
this.numberOfAttempts = 1;
this.segCount = 0;
this.playerMode = PlayerMode.PROMPT;
// Listeners
this.dtmfListenerAdded = false;
this.playerListenerAdded = false;
// Concurrency
this.terminated = new AtomicBoolean(false);
this.LOCK = new Object();
}
@Override
public void execute() {
if (getEndpoint().getActiveConnectionsCount() == 0) {
oc.fire(this, new Text("rc=326"));
this.complete();
return;
}
playerMode = PlayerMode.PROMPT;
promptLength = 0;
promptIndex = 0;
segCount = 0;
this.scheduler = getEndpoint().getScheduler();
heartbeat = new Heartbeat(this);
// get options of the request
options = Options.allocate(getTrigger().getParams());
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Prepare digit collect phase", getEndpoint().getLocalName()));
}
// Initializes resources for DTMF detection
// at this stage DTMF detector started but local buffer is not assigned
// yet as listener
prepareCollectPhase(options);
if (options.getFirstDigitTimer() > 0) {
this.firstDigitTimer = options.getFirstDigitTimer();
} else {
this.firstDigitTimer = 0;
}
if (options.getInterDigitTimer() > 0) {
this.nextDigitTimer = options.getInterDigitTimer();
} else {
this.nextDigitTimer = 0;
}
if (options.getMaxDuration() > 0) {
this.maxDuration = options.getMaxDuration();
} else {
this.maxDuration = 0;
}
if (options.getNumberOfAttempts() > 1) {
this.numberOfAttempts = options.getNumberOfAttempts();
} else {
this.numberOfAttempts = 1;
}
// Need to manually set terminated to false at this point
// Because object is recycled and reset() is always called before this method.
this.terminated.set(false);
// if initial prompt has been specified then start with prompt phase
if (options.hasPrompt()) {
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Start prompt phase", getEndpoint().getLocalName()));
}
this.isPromptActive = true;
startPromptPhase(options.getPrompt());
return;
}
// flush DTMF detector buffer and start collect phase
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Start collect phase", getEndpoint().getLocalName()));
}
flushBuffer();
// now all buffered digits must be inside local buffer
startCollectPhase();
}
/**
* Starts the prompt phase.
*
* @param options requested options.
*/
private void startPromptPhase(Collection<Text> promptList) {
synchronized (this.LOCK) {
// Hotfix for concurrency issues
// https://github.com/RestComm/mediaserver/issues/164
if (this.terminated.get()) {
if (logger.isInfoEnabled()) {
logger.info("Skipping prompt phase because PlayCollect has been terminated.");
}
return;
}
player = getPlayer();
try {
// assign listener
if (!playerListenerAdded) {
player.addListener(promptHandler);
playerListenerAdded = true;
}
promptLength = promptList.size();
prompt = promptList.toArray(prompt);
player.setURL(prompt[0].toString());
// specify URL to play
// player.setURL(options.getPrompt().toString());
// start playback
player.activate();
} catch (TooManyListenersException e) {
of.fire(this, RC_300);
logger.error("Too many listeners, firing of");
} catch (MalformedURLException e) {
of.fire(this, RC_301);
logger.error("Received URL in invalid format, firing of");
} catch (ResourceUnavailableException e) {
of.fire(this, RC_301);
logger.error("Received URL can not be found, firing of");
}
}
}
/**
* Terminates prompt phase if it was started or do nothing otherwise.
*/
private void terminatePrompt() {
synchronized (this.LOCK) {
// jump to end of segments
if (promptLength > 0) {
promptIndex = promptLength - 1;
}
if (player != null) {
player.deactivate();
player.removeListener(promptHandler);
playerListenerAdded = false;
player = null;
}
}
}
/**
* Prepares resources for DTMF collection phase.
*
* @param options
*/
private void prepareCollectPhase(Options options) {
// obtain detector instance
dtmfDetector = this.getDetector();
// DTMF detector was buffering digits and now it can contain
// digits in the buffer
// Clean detector's buffer if requested
if (options.isClearDigits()) {
dtmfDetector.clearDigits();
}
// clear local buffer
buffer.reset();
buffer.setListener(dtmfHandler);
// assign requested parameters
buffer.setPatterns(options.getDigitPattern());
if (options.getMaxDigitsNumber() > 0) {
buffer.setCount(options.getMaxDigitsNumber());
} else {
buffer.setCount(options.getDigitsNumber());
}
// Activate DTMF detector (so prompt can be interrupted, if wished)
dtmfDetector.activate();
}
/**
* Flushes DTMF buffer content to local buffer
*/
private void flushBuffer() {
try {
// attach local buffer to DTMF detector
// but do not flush
if (!dtmfListenerAdded) {
dtmfDetector.addListener(buffer);
dtmfListenerAdded = true;
}
dtmfDetector.flushBuffer();
} catch (TooManyListenersException e) {
of.fire(this, RC_300);
logger.error("Too many listeners for DTMF detector, firing of");
}
}
private void startCollectPhase() {
synchronized (this.LOCK) {
// Hotfix for concurrency issues
// https://github.com/RestComm/mediaserver/issues/164
if (this.terminated.get()) {
if (logger.isInfoEnabled()) {
logger.info("Skipping collect phase because PlayCollect has been terminated.");
}
return;
}
if (this.firstDigitTimer > 0 || this.maxDuration > 0) {
if (this.firstDigitTimer > 0) {
heartbeat.setTtl((int) (this.firstDigitTimer));
} else {
heartbeat.setTtl(-1);
}
if (this.maxDuration > 0) {
heartbeat.setOverallTtl(this.maxDuration);
} else {
heartbeat.setOverallTtl(-1);
}
heartbeat.activate();
getEndpoint().getScheduler().submitHeatbeat(heartbeat);
}
buffer.activate();
buffer.flush();
}
}
/**
* Terminates digit collect phase.
*/
private void terminateCollectPhase() {
if (dtmfDetector != null) {
dtmfDetector.removeListener(buffer);
dtmfDetector.deactivate();
dtmfListenerAdded = false;
// dtmfDetector.clearDigits();
buffer.passivate();
buffer.clear();
dtmfDetector = null;
}
}
/**
* Terminates any activity.
*/
private void terminate() {
synchronized (this.LOCK) {
if (!this.terminated.get()) {
this.terminated.set(true);
this.isPromptActive = false;
this.terminatePrompt();
this.terminateCollectPhase();
if (this.heartbeat != null) {
this.heartbeat.disable();
this.heartbeat = null;
}
if (options != null) {
Options.recycle(options);
options = null;
}
}
}
}
@Override
public boolean doAccept(Text event) {
if (!oc.isActive() && oc.matches(event)) {
return true;
}
if (!of.isActive() && of.matches(event)) {
return true;
}
return false;
}
@Override
public void cancel() {
// disable signal activity and terminate
if (this.heartbeat != null) {
this.heartbeat.disable();
}
this.isPromptActive = false;
this.terminate();
}
private Player getPlayer() {
return (Player) getEndpoint().getResource(MediaType.AUDIO, ComponentType.PLAYER);
}
private DtmfDetector getDetector() {
return (DtmfDetector) getEndpoint().getResource(MediaType.AUDIO, ComponentType.DTMF_DETECTOR);
}
@Override
public void reset() {
super.reset();
// disable signal activity and terminate
this.isPromptActive = false;
this.terminate();
oc.reset();
of.reset();
}
private void next(long delay) {
synchronized (this.LOCK) {
// Hotfix for concurrency issues
// https://github.com/RestComm/mediaserver/issues/164
if (this.terminated.get() || !this.isPromptActive) {
if (logger.isInfoEnabled()) {
logger.info("Skipping prompt phase because PlayCollect has been terminated.");
}
return;
}
segCount++;
promptIndex++;
try {
String url = prompt[promptIndex].toString();
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Processing player next with url - %s", getEndpoint().getLocalName(), url));
}
player.setURL(url);
player.setInitialDelay(delay);
// start playback
player.start();
} catch (MalformedURLException e) {
of.fire(this, RC_301);
logger.error("Received URL in invalid format , firing of");
} catch (ResourceUnavailableException e) {
of.fire(this, RC_301);
logger.error("Received URL can not be found , firing of");
}
}
}
private void prev(long delay) {
synchronized (this.LOCK) {
// Hotfix for concurrency issues
// https://github.com/RestComm/mediaserver/issues/164
if (this.terminated.get() || !this.isPromptActive) {
if (logger.isInfoEnabled()) {
logger.info("Skipping prompt phase because PlayCollect has been terminated.");
}
return;
}
segCount++;
promptIndex--;
try {
String url = prompt[promptIndex].toString();
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Processing player prev with url - %s", getEndpoint().getLocalName(), url));
}
player.setURL(url);
player.setInitialDelay(delay);
// start playback
player.start();
} catch (MalformedURLException e) {
of.fire(this, RC_301);
logger.error("Received URL in invalid format, firing of");
return;
} catch (ResourceUnavailableException e) {
of.fire(this, RC_301);
logger.error("Received URL can not be found, firing of");
return;
}
}
}
private void curr(long delay) {
synchronized (this.LOCK) {
// Hotfix for concurrency issues
// https://github.com/RestComm/mediaserver/issues/164
if (this.terminated.get() || !this.isPromptActive) {
if (logger.isInfoEnabled()) {
logger.info("Skipping prompt phase because PlayCollect has been terminated.");
}
return;
}
segCount++;
try {
String url = prompt[promptIndex].toString();
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Processing player curr with url - %s", getEndpoint().getLocalName(), url));
}
player.setURL(url);
player.setInitialDelay(delay);
// start playback
player.start();
} catch (MalformedURLException e) {
of.fire(this, RC_301);
logger.error("Received URL in invalid format, firing of");
} catch (ResourceUnavailableException e) {
of.fire(this, RC_301);
logger.error("Received URL can not be found, firing of");
}
}
}
private void first(long delay) {
synchronized (this.LOCK) {
// Hotfix for concurrency issues
// https://github.com/RestComm/mediaserver/issues/164
if (this.terminated.get() || !this.isPromptActive) {
if (logger.isInfoEnabled()) {
logger.info("Skipping prompt phase because PlayCollect has been terminated.");
}
return;
}
segCount++;
promptIndex = 0;
try {
String url = prompt[promptIndex].toString();
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Processing player first with url - %s", getEndpoint().getLocalName(), url));
}
player.setURL(url);
player.setInitialDelay(delay);
// start playback
player.start();
} catch (MalformedURLException e) {
of.fire(this, RC_301);
logger.error("Received URL in invalid format , firing of");
} catch (ResourceUnavailableException e) {
of.fire(this, RC_301);
logger.error("Received URL can not be found , firing of");
}
}
}
private void last(long delay) {
synchronized (this.LOCK) {
// Hotfix for concurrency issues
// https://github.com/RestComm/mediaserver/issues/164
if (this.terminated.get() || !this.isPromptActive) {
if (logger.isInfoEnabled()) {
logger.info("Skipping prompt phase because PlayCollect has been terminated.");
}
return;
}
segCount++;
promptIndex = promptLength - 1;
try {
String url = prompt[promptIndex].toString();
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Processing player last with url - %s", getEndpoint().getLocalName(), url));
}
player.setURL(url);
player.setInitialDelay(delay);
// start playback
player.start();
} catch (MalformedURLException e) {
of.fire(this, RC_301);
logger.error("Received URL in invalid format , firing of");
} catch (ResourceUnavailableException e) {
of.fire(this, RC_301);
logger.error("Received URL can not be found , firing of");
}
}
}
private void decreaseNa() {
numberOfAttempts--;
if (options.hasReprompt()) {
buffer.passivate();
isPromptActive = true;
startPromptPhase(options.getReprompt());
heartbeat.disable();
} else if (options.hasPrompt()) {
buffer.passivate();
isPromptActive = true;
startPromptPhase(options.getPrompt());
heartbeat.disable();
} else {
startCollectPhase();
}
}
/**
* Handler for prompt phase.
*/
private class PromptHandler implements PlayerListener {
private PlayCollect signal;
/**
* Creates new handler instance.
*
* @param signal the play record signal instance
*/
protected PromptHandler(PlayCollect signal) {
this.signal = signal;
}
@Override
public void process(PlayerEvent event) {
switch (event.getID()) {
case PlayerEvent.START:
if (segCount == 0) {
flushBuffer();
}
break;
case PlayerEvent.STOP:
if (promptIndex < promptLength - 1) {
next(options.getInterval());
return;
}
switch (playerMode) {
case PROMPT:
// start collect phase when prompted has finished
if (isPromptActive) {
isPromptActive = false;
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Prompt phase terminated, start collect phase",
getEndpoint().getLocalName()));
}
startCollectPhase();
}
break;
case SUCCESS:
oc.fire(signal, eventContent);
reset();
complete();
break;
case FAILURE:
if (numberOfAttempts == 1) {
oc.fire(signal, eventContent);
reset();
complete();
} else {
decreaseNa();
}
break;
}
break;
case PlayerEvent.FAILED:
of.fire(signal, RC_300);
complete();
break;
}
}
}
/**
* Handler for digit collect phase.
*
*/
private class DtmfHandler implements BufferListener {
private PlayCollect signal;
/**
* Constructor for this handler.
*
* @param signal
*/
public DtmfHandler(PlayCollect signal) {
this.signal = signal;
}
@Override
public void patternMatches(int index, String s) {
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Collect phase: pattern has been detected", getEndpoint().getLocalName()));
}
String naContent = "";
if (options.getNumberOfAttempts() > 1) {
naContent = " na=" + (options.getNumberOfAttempts() - numberOfAttempts + 1);
}
if (options.hasSuccessAnnouncement()) {
eventContent = new Text("rc=100 dc=" + s + " pi=" + index + naContent);
playerMode = PlayerMode.SUCCESS;
startPromptPhase(options.getSuccessAnnouncement());
} else {
oc.fire(signal, new Text("rc=100 dc=" + s + " pi=" + index + naContent));
reset();
complete();
}
}
@Override
public void countMatches(String s) {
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Collect phase: max number of digits detected", getEndpoint().getLocalName()));
}
String naContent = "";
if (options.getNumberOfAttempts() > 1) {
naContent = " na=" + (options.getNumberOfAttempts() - numberOfAttempts + 1);
}
if (options.hasSuccessAnnouncement()) {
eventContent = new Text("rc=100 dc=" + s + naContent);
playerMode = PlayerMode.SUCCESS;
startPromptPhase(options.getSuccessAnnouncement());
} else {
oc.fire(signal, new Text("rc=100 dc=" + s + naContent));
reset();
complete();
}
}
@Override
public boolean tone(String s) {
if (options.getMaxDigitsNumber() > 0 && s.charAt(0) == options.getEndInputKey()
&& buffer.length() >= options.getDigitsNumber()) {
String naContent = "";
if (options.getNumberOfAttempts() > 1) {
naContent = " na=" + (options.getNumberOfAttempts() - numberOfAttempts + 1);
}
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) End Input Tone '%s' has been detected", getEndpoint().getLocalName(), s));
}
// end input key still not included in sequence
if (options.hasSuccessAnnouncement()) {
if (options.isIncludeEndInputKey()) {
eventContent = new Text("rc=100 dc=" + buffer.getSequence() + s + naContent);
} else {
eventContent = new Text("rc=100 dc=" + buffer.getSequence() + naContent);
}
playerMode = PlayerMode.SUCCESS;
startPromptPhase(options.getSuccessAnnouncement());
} else {
if (options.isIncludeEndInputKey()) {
oc.fire(signal, new Text("rc=100 dc=" + buffer.getSequence() + s + naContent));
} else {
oc.fire(signal, new Text("rc=100 dc=" + buffer.getSequence() + naContent));
}
heartbeat.disable();
reset();
complete();
}
return true;
}
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Tone '%s' has been detected", getEndpoint().getLocalName(), s));
}
if (isPromptActive) {
if (options.prevKeyValid() && options.getPrevKey() == s.charAt(0)) {
prev(options.getInterval());
return false;
} else if (options.firstKeyValid() && options.getFirstKey() == s.charAt(0)) {
first(options.getInterval());
return false;
} else if (options.currKeyValid() && options.getCurrKey() == s.charAt(0)) {
curr(options.getInterval());
return false;
} else if (options.nextKeyValid() && options.getNextKey() == s.charAt(0)) {
next(options.getInterval());
return false;
} else if (options.lastKeyValid() && options.getLastKey() == s.charAt(0)) {
last(options.getInterval());
return false;
}
}
if (!options.isNonInterruptable()) {
if (isPromptActive) {
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Tone '%s' detected: prompt phase interrupted",
getEndpoint().getLocalName(), s));
}
terminatePrompt();
} else {
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Tone '%s' detected: collected", getEndpoint().getLocalName(), s));
}
}
} else {
if (isPromptActive) {
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Tone '%s' detected, waiting for prompt phase termination",
getEndpoint().getLocalName(), s));
}
if (options.isClearDigits()) {
return false;
}
} else {
if (logger.isInfoEnabled()) {
logger.info(
String.format("(%s) Tone '%s' has been detected: collected", getEndpoint().getLocalName(), s));
}
}
}
if (nextDigitTimer > 0) {
heartbeat.setTtl((int) (nextDigitTimer));
if (!heartbeat.isActive()) {
heartbeat.activate();
getEndpoint().getScheduler().submitHeatbeat(heartbeat);
}
} else if (maxDuration == 0) {
heartbeat.disable();
}
return true;
}
}
private class Heartbeat extends Task {
private final AtomicInteger ttl;
private final AtomicInteger overallTtl;
private final AtomicBoolean active;
private Signal signal;
public Heartbeat(Signal signal) {
super();
ttl = new AtomicInteger(-1);
overallTtl = new AtomicInteger(-1);
active = new AtomicBoolean(false);
this.signal = signal;
}
@Override
public int getQueueNumber() {
return PriorityQueueScheduler.HEARTBEAT_QUEUE;
}
public void setTtl(int value) {
ttl.set(value);
}
public void setOverallTtl(int value) {
overallTtl.set(value);
}
public void disable() {
this.active.set(false);
}
public void activate() {
this.active.set(true);
}
public boolean isActive() {
return this.active.get();
}
@Override
public long perform() {
if (!active.get()) {
return 0;
}
int ttlValue = ttl.get();
int overallTtlValue = overallTtl.get();
if (ttlValue != 0 && overallTtlValue != 0) {
if (ttlValue > 0) {
ttl.set(ttlValue - 1);
}
if (overallTtlValue > 0) {
overallTtl.set(overallTtlValue - 1);
}
scheduler.submitHeatbeat(this);
return 0;
}
if (logger.isInfoEnabled()) {
logger.info(String.format("(%s) Timeout expired waiting for dtmf", getEndpoint().getLocalName()));
}
if (numberOfAttempts == 1) {
String naContent = "";
if (options.getNumberOfAttempts() > 1) {
naContent = " na=" + options.getNumberOfAttempts();
}
if (ttlValue == 0) {
int length = buffer.getSequence().length();
if (options.getDigitsNumber() > 0 && length >= options.getDigitsNumber()) {
if (options.hasSuccessAnnouncement()) {
eventContent = new Text("rc=100 dc=" + buffer.getSequence() + naContent);
playerMode = PlayerMode.SUCCESS;
startPromptPhase(options.getSuccessAnnouncement());
} else {
oc.fire(signal, new Text("rc=100 dc=" + buffer.getSequence() + naContent));
reset();
complete();
}
} else if (length > 0) {
if (options.hasNoDigitsReprompt()) {
eventContent = new Text("rc=326 dc=" + buffer.getSequence() + naContent);
playerMode = PlayerMode.FAILURE;
startPromptPhase(options.getNoDigitsReprompt());
} else if (options.hasFailureAnnouncement()) {
eventContent = new Text("rc=326 dc=" + buffer.getSequence() + naContent);
playerMode = PlayerMode.FAILURE;
startPromptPhase(options.getFailureAnnouncement());
} else {
oc.fire(signal, new Text("rc=326 dc=" + buffer.getSequence() + naContent));
reset();
complete();
}
} else {
if (options.hasNoDigitsReprompt()) {
eventContent = new Text("rc=326" + naContent);
playerMode = PlayerMode.FAILURE;
startPromptPhase(options.getNoDigitsReprompt());
} else if (options.hasFailureAnnouncement()) {
eventContent = new Text("rc=326" + naContent);
playerMode = PlayerMode.FAILURE;
startPromptPhase(options.getFailureAnnouncement());
} else {
oc.fire(signal, new Text("rc=326" + naContent));
reset();
complete();
}
}
} else {
if (options.hasNoDigitsReprompt()) {
eventContent = new Text("rc=330" + naContent);
playerMode = PlayerMode.FAILURE;
startPromptPhase(options.getNoDigitsReprompt());
} else if (options.hasFailureAnnouncement()) {
eventContent = new Text("rc=330" + naContent);
playerMode = PlayerMode.FAILURE;
startPromptPhase(options.getFailureAnnouncement());
} else {
oc.fire(signal, new Text("rc=330" + naContent));
reset();
complete();
}
}
} else {
buffer.reset();
decreaseNa();
}
return 0;
}
}
}