After application initialization, the application is ready to perform whatever IRAPI functions necessary to interact with the caller in the way defined by the application. Usually, the best way to implement this interaction is through an event switch inside a continuous loop on the irWCheck(3IRAPI) function. Each case in the switch should handle a unique event, perform the next event generating function, and return to the wait loop [irWCheck()]. Each time an application executes an event generating function, it should not execute another IRAPI function until it receives the event that terminates that function.
For example, an application might perform the following functions after application initialization:
In the chantest.c application, after the IRE_EXEC event is received and chantest initializes the application, it answers the incoming call with irAnswer(3IRAPI). It then returns to the while loop to wait for the IRE_ANSWER_DONE event. The IRE_ANSWER_DONE event contains the result of the answer attempt.
while ( irWCheck(&ev) != IRR_FAIL ) {
switch(ev.event_id) {
.
.
.
case IRE_EXEC:
.
.
.
if (irAnswer(cid) == IRR_FAIL) {
cleanup ("Error onirAnswer",cid);
}
break;
case IRE_ANSWER_DONE:
if ( ev.event_mod1 != IREM_COMPLETE ) {
cleanup("Error in IRE_ANSWER_DONE", cid);
break;
}
startChanTst(cid);
break;
Chantest uses its startChanTst() subroutine when the IRE_ANSWER_DONE event is received to perform this step of the application. This subroutine plays the introductory announcement, ``Begin testing.''
void startChanTst(channel_id cid)
{
int chan = irCid2Chan(cid);
if ( chan == IRR_FAIL ) {
cleanup("Can't get channel from cid", cid);
return;
}
Chl[chan].PlayDone = IGNORE_WHEN_DONE;
Chl[chan].RetryCount = 0;
if (irFPlay(cid, 0, "/speech/begn.tst") < 0) {
cleanup ("Error on irFPlay", cid);
return;
}
playInstr(cid);
}
The startChanTst routine converts the cid to a channel number with irCid2Chan(3IRAPI). It uses the channel number to set application specific data for the channel. The PlayDone flag indicates what is to be done when the next IRE_PLAY_DONE event arrives. The RetryCount indicates the number of times the application has prompted the caller after getting no input. irFPlay(3IRAPI) is used to queue up the "begin testing'' introductory phrase stored in the file /speech/begn.tst for playing. The playInstr() subroutine then is called to play the instructions that are part of the initial chantest prompt.
void playInstr(channel_id cid)
{
int chan = irCid2Chan(cid);
if ( chan == IRR_FAIL ) {
cleanup("Can't get channel from cid", cid);
return;
}
Chl[chan].PlayDone = START_TIMER_WHEN_DONE;
if(irFlushInput(cid) == IRR_FAIL ||
irSetEvent(cid, IRE_INPUT, IRF_PLAYINTR | IRF_NOTIFY)
== IRR_FAIL ||
irSetEvent(cid, IRE_INPUT_DONE, IRF_NOTIFY) == IRR_FAIL ||
irFPlay(cid, 0, "/speech/ent.4.tt") == IRR_FAIL ||
irFPlay(cid, 0, "/speech/all.rptd") == IRR_FAIL ||
irFPlay(cid, 0, "/speech/term.4.0") == IRR_FAIL ||
irEnd(cid, 0, 0) < 0) {
cleanup ("Error in playInstr", cid);
}
}
Before the prompt is played, the PlayDone flag is set to indicate that the touch-tone input timer should be started when the next IRE_PLAY_DONE event is received. The irFlushInput(3IRAPI) function clears any caller input that has been given before the prompt. This synchronizes caller input with prompts. The irSetEvent(3IRAPI) function is used to set the IRE_INPUT event to interrupt speech play when the event is generated. This enables the talkoff feature, allowing the caller to interrupt a prompt with touch-tone input. Three separate phrases are queued up with irFPlay(3IRAPI) that make up instructions to the caller: "Enter 4 touch tone digits,'' "All digits except star and pound will be repeated,'' "Terminate your input with a pound sign.'' Speech play is started with the call to irEnd(3IRAPI) and the subroutine returns back to the main while loop to wait for the next event.
Touch-tone input is always being collected and placed on the input queue. The IRE_INPUT event indicates that input has been placed on the input queue. The IRE_INPUT_DONE event indicates that the input on the input queue matches conditions specified by the input queue parameters.
Chantest sets these input parameters during application initialization with its setTTParams() subroutine. After the playing of prompt is started, chantest returns to the main while loop to wait for the next event. If the caller waits for play to complete before entering any input, the IRE_PLAY_DONE event arrives when chantest's PlayDone flag is set to START_TIMER_WHEN_DONE. Chantest must start the touch-tone timer with irStartTTTimer(3IRAPI). Chantest then returns to the main while loop to wait for the IRE_INPUT_DONE event:
case IRE_PLAY_DONE:
switch(Chl[chan].PlayDone) {
.
.
.
case START_TIMER_WHEN_DONE:
if (irStartTTTimer(cid) == IRR_FAIL)
cleanup("irStartTimer Failed", cid);
break;
case INPUT_DONE_WHEN_DONE:
input_done(cid, &Chl[chan].InputDoneEvent);
break;
IRE_INPUT and IRE_INPUT_DONE events are also handled in the main while loop. The IRE_INPUT event is simply ignored since nothing needs to be done until the IRE_INPUT_DONE event arrives. The touch-tone timer does not have to be restarted before the IRE_INPUT_DONE event. The IRAPI automatically restarts the timer using the IRP_TT_INTERTIME parameter value between IRE_INPUT events.
If the IRE_INPUT_DONE arrives when the IRAPI library is still in the IRS_PLAYING state, processing of the IRE_INPUT_DONE event is delayed until the IRE_PLAY_DONE event arrives. This is done by setting the channel's PlayDone flag to INPUT_DONE_WHEN_DONE and saving the IRE_INPUT_DONE event structure. The IRE_INPUT event always precedes the IRE_PLAY_DONE event when speech is talked off. The library is not in the IRS_IDLE state until the IRE_PLAY_DONE event is received. (See above where the INPUT_DONE_WHEN_DONE value is used with the IRE_PLAY_DONE event.) This ensures that the IRAPI is in the IRS_IDLE state before the application continues. The IRAPI must be idle before more speech can be queued to play.
case IRE_INPUT_DONE:
if ( irLibState(cid) == IRS_PLAYING ) {
Chl[chan].PlayDone = INPUT_DONE_WHEN_DONE;
Chl[chan].InputDoneEvent = ev;
} else {
input_done(cid, &ev);
}
break;
case IRE_INPUT: /* Event is ignored */
break;
The input_done() subroutine evaluates the modifiers from IRE_INPUT_DONE:
void input_done(channel_id cid, ir_event_t *evPtr)
{
int chan = irCid2Chan(cid);
if ( chan == IRR_FAIL) {
cleanup("Can't get channel from cid", cid);
return;
}
switch(evPtr->event_mod1) {
case IREM_INPUT_LENGTH:
case IREM_INPUT_DELIM:
Chl[chan].RetryCount = 0;
/* play back touch tones to caller */
play_tt(cid);
break;
case IREM_TT_PRE:
case IREM_TT_INTER:
if(Chl[chan].RetryCount++ >= 3) {
/* 3 tries with no input. Abort transaction. */
if ( irSetEvent(cid, IRE_INPUT, IRF_NOTIFY) == IRR_FAIL ||
irFPlay(cid, 0, "/speech/aborted") == IRR_FAIL ||
irFPlay(cid, 0, "/speech/bye") == IRR_FAIL ||
irEnd(cid, 0, 0) == IRR_FAIL ) {
cleanup ("Error processing IRE_INPUT_DONE", cid);
return;
}
Chl[chan].PlayDone = DISCONNECT_WHEN_DONE;
} else {
/* play instructions again */
playInstr(cid);
}
break;
default:
cleanup("Unexpected IRE_INPUT_DONE event modifier", cid);
break;
}
}
Input is removed from the input queue with irGetInput(3IRAPI) and played back to the caller in chantest's play_tt() subroutine:
void play_tt(channel_id cid)
{
int len;
char buf[INPUT_LEN + 1];
char *bufPtr;
if ( (len = irGetInput(cid,buf,INPUT_LEN)) == IRR_FAIL ) {
cleanup("irGetInput Failed in play_tt", cid);
return;
}
buf[len] = 0;
if (irSetEvent(cid, IRE_INPUT, IRF_NOTIFY) == IRR_FAIL ||
irSetEvent(cid, IRE_INPUT_DONE, IRF_IGNORE) == IRR_FAIL ){
cleanup("irSetEvent failed in play_tt", cid);
return;
}
for(bufPtr = &buf[0]; *bufPtr != 0; bufPtr++) {
switch(*bufPtr) {
case '0':
(void) irFPlay(cid, 0, "/speech/n.0");
break;
case '1':
(void) irFPlay(cid, 0, "/speech/n.1");
break;
.
.
.
case '9':
(void) irFPlay(cid, 0, "/speech/n.9");
break;
}
}
if(strcmp(buf, "0000") == 0) {
if (irFPlay(cid, 0, "/speech/bye") == IRR_FAIL) {
cleanup ("Error on irFPlay", cid);
return;
}
Chl[chan].PlayDone = DISCONNECT_WHEN_DONE;
} else {
Chl[chan].PlayDone = REPROMPT_WHEN_DONE;
}
if ( irLibState(cid) != IRS_PLAY_QUEUED ) {
reprompt(cid);
return;
}
if (irEnd(cid, 0, 0) < 0) {
cleanup ("Error on irEnd", cid);
}
}
Notice that the play_tt() subroutine shown above plays back caller input after using irSetEvent(3IRAPI) to set the IRE_INPUT event to IRF_NOTIFY only, thereby clearing the IRF_ITR flag. This disables talkoff so that the caller may not interrupt this play. A check is done to see if the caller entered 4 zeros. If so, a good-bye message is played and the channel's PlayDone flag is set to disconnect when the IRE_PLAY_DONE event for that message arrives.