// DEMOINDX.C - This program prompts user to enter 4 digits up to three // times or the '#' sign to terminate. The 4 digits user // entered will be playbacked from indexed mode. If # sign // is in the digits user entered, that channel will be // terminated. Four voice files, INTR.VOX, YOU.VOX, // THANKS.VOX & DIGITS.VOX must be in the same directory // with this program. // // Enter "demoindx " at DOS prompt where n is optional // user-assigned hardware interrupt level. The standard // BICOM 4LS hardware interrupt is 3. // can be used to terminate this program at any time. // // BICOM (R) Multi-line Indexed Playing Demo Program // Copyright (c) BICOM 1990, 1991 All rights reserved. // INCLUDES #include #include #include #include #include #include "sys\types.h" #include "sys\stat.h" #include "bcmlib.h" // BICOM standard header file // DEFINES // system definitions #define FOREVER 1 // while loop index in main function #define RINGS 1 // # of rings before pickup #define INTLVL 3 // standard H/W interrupt level #define NUMCHAN 4 // number of channels #define FOURDIG 4 // 4-digit code #define TENDIGITS 10 // 10 entries in voice file "DIGITS" #define ESC 0x1B // escape key #define EOT 0xFFFFFFFF // end of table marker // channel state definitions #define ST_ONHOOK 0 // going on hook #define ST_WTCALL 1 // waiting for call #define ST_OFHOOK 2 // going off hook #define ST_PLINTR 3 // playing introduction #define ST_GET3DG 4 // getting 3 DTMF digits #define ST_GET4DG 5 // getting 4 DTMF gidits #define ST_PLMSG 6 // playing message #define ST_PLINDX 7 // playing voice file #define ST_PLTHNX 8 // playing thanks message #define ST_EXIT 9 // exiting #define NUM_STATE 10 // number of states // event definitions #define RING 0 // rings received #define OFHK 1 // off hook complete #define LNDS 2 // line disconnect #define ENDF 3 // end of file on playback #define TOUT 4 // time out #define XEVT 5 // 3 timeouts or # entered #define TERM 6 // terminating DTMF digits received #define MAXD 7 // max DTMF digits received #define EXIT 8 // exiting #define ONHK 9 // on hook complete #define NUM_EVENT 10 // number of events // user defined events #define T_THREETIMES 98 // event sent to queue after asking // user to enter digits three times #define T_POUNDSIGN 99 // event sent to queue after user // entered pound sign // STRUCTURES static struct ChanData // Channel data structure { int nChanState; // current state int fhVoxFile; // file handle of voice file char cDtmf[5]; // DTMF buffer } Channel[NUMCHAN+1]; static struct ChanEvent // Event data sturcture { int nChanNum; // channel number int nEventType; // event type occurred } Event; struct OffLen // index format data structure { unsigned long dwOffset; // offset unsigned long dwLength; // length }; // VARIABLES static TCB Tcb = {0}; // task control block static CSB Csb = {0}; // channel status block static int nIntLvl; // HW interrupt level in use static int nTimes[NUMCHAN+1] = {0}; // keep track of times of timeout // for each channel static struct OffLen Header[TENDIGITS];// header of indexed file unsigned char *pDtmf; // pointer to the terminating DTMF static int ThreeDigitsFlag = 0; // set if DTMF string is from // Get3Digits_Entry() // PROTOTYPES void main(int, char **); void Init(void); void EventProcess(struct ChanEvent *); void StateTransit(struct ChanEvent *); void Play(int, char *, int); int InitHeader(char *, struct OffLen *, int); void GoOnhook_Entry(struct ChanEvent *); void GoOnhook_Exit(struct ChanEvent *); void WaitCall_Entry(struct ChanEvent *); void WaitCall_Exit(struct ChanEvent *); void GoOffhook_Entry(struct ChanEvent *); void GoOffhook_Exit(struct ChanEvent *); void PlayIntr_Entry(struct ChanEvent *); void PlayIntr_Exit(struct ChanEvent *); void Get3Digits_Entry(struct ChanEvent *); void Get3Digits_Exit(struct ChanEvent *); void Get4Digits_Entry(struct ChanEvent *); void Get4Digits_Exit(struct ChanEvent *); void PlayMsg_Entry(struct ChanEvent *); void PlayMsg_Exit(struct ChanEvent *); void PlayIndex_Entry(struct ChanEvent *); void PlayIndex_Exit(struct ChanEvent *); void PlayThanx_Entry(struct ChanEvent *); void PlayThanx_Exit(struct ChanEvent *); void Exit_Entry(void); // STATE TRANSITION TABLE // Current state is on the vertical axis. // Event is on the horizontal axis. // Each cell is the next state for a given state and an event occurred. int NextState[NUM_STATE][NUM_EVENT] = { /* RING OFHK LNDS ENDF TOUT XEVT TERM MAXD EXIT ONHK */ /* ST_ONHOOK */ {ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_EXIT, ST_WTCALL}, /* ST_WTCALL */ {ST_OFHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_EXIT, ST_ONHOOK}, /* ST_OFHOOK */ {ST_ONHOOK, ST_PLINTR, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_EXIT, ST_ONHOOK}, /* ST_PLINTR */ {ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_GET4DG, ST_ONHOOK, ST_PLTHNX, ST_GET3DG, ST_ONHOOK, ST_EXIT, ST_ONHOOK}, /* ST_GET3DG */ {ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_PLINTR, ST_PLTHNX, ST_ONHOOK, ST_PLMSG, ST_EXIT, ST_ONHOOK}, /* ST_GET4DG */ {ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_PLINTR, ST_ONHOOK, ST_ONHOOK, ST_PLMSG, ST_EXIT, ST_ONHOOK}, /* ST_PLMSG */ {ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_PLINDX, ST_ONHOOK, ST_PLTHNX, ST_PLINDX, ST_ONHOOK, ST_EXIT, ST_ONHOOK}, /* ST_PLINDX */ {ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_PLTHNX, ST_ONHOOK, ST_ONHOOK, ST_PLTHNX, ST_ONHOOK, ST_EXIT, ST_ONHOOK}, /* ST_PLTHNX */ {ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_EXIT, ST_ONHOOK}, }; // LOOKUP TABLE // for entry and exit functions for each state struct LookUp { void (*pfnEntryFunction)(); void (*pfnExitFunction)(); } LookUpTbl[NUM_STATE] = { GoOnhook_Entry, GoOnhook_Exit, // ST_ONHOOK WaitCall_Entry, WaitCall_Exit, // ST_WTCALL GoOffhook_Entry, GoOffhook_Exit, // ST_OFHOOK PlayIntr_Entry, PlayIntr_Exit, // ST_PLINTR Get3Digits_Entry, Get3Digits_Exit, // ST_GET3DG Get4Digits_Entry, Get4Digits_Exit, // ST_GET4DG PlayMsg_Entry, PlayMsg_Exit, // ST_PLMSG PlayIndex_Entry, PlayIndex_Exit, // ST_PLINDX PlayThanx_Entry, PlayThanx_Exit, // ST_PLTHNX Exit_Entry // ST_EXIT }; //*************************************************************************** // go onhook entry function //*************************************************************************** void GoOnhook_Entry(struct ChanEvent *Event) { nTimes[Event->nChanNum] = 0; // initialize timeout counter Bcm6_SetHook(Event->nChanNum, H_ONH); printf(" Enter: GoOnhook_Entry\n"); } //*************************************************************************** // go onhook exit function //*************************************************************************** void GoOnhook_Exit(struct ChanEvent *Event) { Event; // compress compiler printf(" Exit : GoOnhook_Exit\n"); } //*************************************************************************** // idle entry function //*************************************************************************** void WaitCall_Entry(struct ChanEvent *Event) { int i; Bcm8_ClearDTMFQ(Event->nChanNum); // clear the DTMF buffer // clear the application's channel DTMF buffer for (i=0; i<5; i++) Channel[Event->nChanNum].cDtmf[i] = 0; printf(" Enter: WaitCall_Entry\n"); } //*************************************************************************** // idle exit function //*************************************************************************** void WaitCall_Exit(struct ChanEvent *Event) { Event; // compress compiler printf(" Exit : WaitCall_Exit\n"); } //*************************************************************************** // go offhook entry function //*************************************************************************** void GoOffhook_Entry(struct ChanEvent *Event) { Bcm6_SetHook(Event->nChanNum, H_OFFH); printf(" Enter: GoOffhook_Entry\n"); } //*************************************************************************** // go offhook exit function //*************************************************************************** void GoOffhook_Exit(struct ChanEvent *Event) { Event; // compress compiler printf(" Exit : GoOffhook_Exit\n"); } //*************************************************************************** // play "intr.vox" entry function //*************************************************************************** void PlayIntr_Entry(struct ChanEvent *Event) { int nReturnCode, i; // Check if # sign is in the digits (less than 4) user entered. // If user entered 4 digits, the check routine is done in PlayMsg_Entry(). for (i=0; i<5; i++) if (Channel[Event->nChanNum].cDtmf[i] == 35) // # sign entered { nReturnCode = Bcm70_PutEventInQ(Event->nChanNum, T_POUNDSIGN, 0); printf(" User entered # sign.\n"); printf(" Put event \"T_POUNDSIGN\" into Q.\n"); printf(" Enter: PlayIntr_Entry\n"); return; } if (nTimes[Event->nChanNum] < 3) // ask user three times { Play(Event->nChanNum, "intr1.vox", 1); nTimes[Event->nChanNum]++; printf(" Enter: PlayIntr_Entry %d\n", nTimes[Event->nChanNum]); } else { nTimes[Event->nChanNum] = 0; // send user event to event queue for changing state nReturnCode = Bcm70_PutEventInQ(Event->nChanNum, T_THREETIMES, 0); printf(" Played intro message 3 times.\n"); printf(" Put event \"T_THREETIMES\" into Q.\n"); printf(" Enter: PlayIntr_Entry\n"); } } //*************************************************************************** // play "intr.vox" exit function //*************************************************************************** void PlayIntr_Exit(struct ChanEvent *Event) { close(Channel[Event->nChanNum].fhVoxFile); printf(" Exit : PlayIntr_Exit\n"); } //*************************************************************************** // get 3 digits entry function //*************************************************************************** void Get3Digits_Entry(struct ChanEvent *Event) { int nReturnCode; // There must be a DTMF digit in the buffer, since the event which // makes ST_PLINTR transit to ST_GET3DG is "Terminating DTMF"(TERM). // We can use Bcm9_GetDTMFDigit(). nReturnCode = Bcm9_GetDTMFDigit(Event->nChanNum, pDtmf); if (*pDtmf == 35) // user entered # sign { // send user event to event queue for changing state nReturnCode = Bcm70_PutEventInQ(Event->nChanNum, T_POUNDSIGN, 0); printf(" User entered # sign.\n"); printf(" Put event \"T_POUNDSIGN\" into Q.\n"); } else // 1st digit isn't '#', go get other 3 digits { // set the flag, tell PlayIndex_Entry() that it's from Get3Digits_Entry ThreeDigitsFlag = 1; // set TCB Bcm_ClearTCB(&Tcb); // reserve the 1st byte for the digit we got above Tcb.BufferOff = FindOffset(Channel[Event->nChanNum].cDtmf+1); Tcb.BufferSeg = FindSegment(Channel[Event->nChanNum].cDtmf+1); Tcb.DTMF_Max = 3; // 3 digits required Tcb.DTMF_Term = 0; // no termination digit Tcb.TimeOut = 5; // max 5 seconds Tcb.LoopDrop = 1; // terminate on drop in loop current Tcb.Flag = 0; // disable beep Tcb.SetExtended = 1; // extended rwb used Tcb.InterDigTime = 20; // max interdigit delay 2 seconds if ((nReturnCode = Bcm15_GetDTMFString(Event->nChanNum, &Tcb))) printf("Error in getting DTMF, Return Code = %d\n", nReturnCode); } printf(" Enter: Get3Digits_Entry\n"); } //*************************************************************************** // get 3 digits exit function //*************************************************************************** void Get3Digits_Exit(struct ChanEvent *Event) { Event; // compress compiler printf(" Exit : Get3Digits_Exit\n"); } //*************************************************************************** // get 4 digits entry function //*************************************************************************** void Get4Digits_Entry(struct ChanEvent *Event) { int nReturnCode; // set TCB Bcm_ClearTCB(&Tcb); Tcb.BufferOff = FindOffset(Channel[Event->nChanNum].cDtmf); Tcb.BufferSeg = FindSegment(Channel[Event->nChanNum].cDtmf); Tcb.DTMF_Max = 4; // 4 digits required Tcb.DTMF_Term = 0; // no termination digit Tcb.TimeOut = 6; // max 6 seconds Tcb.LoopDrop = 1; // terminate on drop in loop current Tcb.Flag = 0; // disable beep Tcb.SetExtended = 1; // extended rwb used Tcb.InterDigTime = 2; // max interdigit delay 2 seconds if ((nReturnCode = Bcm15_GetDTMFString(Event->nChanNum, &Tcb))) printf("Error in getting DTMF, Return Code = %d\n", nReturnCode); printf(" Enter: Get4Digits_Entry\n"); } //*************************************************************************** // get 4 digits exit function //*************************************************************************** void Get4Digits_Exit(struct ChanEvent *Event) { Event; // compress compiler printf(" Exit : Get4Digits_Exit\n"); } //*************************************************************************** // play "you.vox" entry function //*************************************************************************** void PlayMsg_Entry(struct ChanEvent *Event) { int nReturnCode, i; // Check if # sign is in the 4 digits user entered. If user entered // less than 4 digits, the check routine is done in PlayIntr_Entry. for (i=0; i<5; i++) if (Channel[Event->nChanNum].cDtmf[i] == 35) { nReturnCode = Bcm70_PutEventInQ(Event->nChanNum, T_POUNDSIGN, 0); printf(" User entered # sign.\n"); printf(" Put event \"T_POUNDSIGN\" into Q.\n"); printf(" Enter: PlayMsg_Entry (# entered)\n"); return; } Play(Event->nChanNum, "you.vox", 1); printf(" Enter: PlayMsg_Entry\n"); } //*************************************************************************** // play "you.vox" exit function //*************************************************************************** void PlayMsg_Exit(struct ChanEvent *Event) { close(Channel[Event->nChanNum].fhVoxFile); printf(" Exit : PlayMsg_Exit\n"); } //*************************************************************************** // play indexed file entry function //*************************************************************************** void PlayIndex_Entry(struct ChanEvent *Event) { int fhIndexFile, i, nDigit, nChan; char *pDigit; static struct OffLen Index[NUMCHAN+1][FOURDIG+1]; // open indexed voice file fhIndexFile = InitHeader("digits.vox", Header, TENDIGITS); nChan = Event->nChanNum; // if it's from Get3Digits_Entry(), concatenate the 1st dtmf(*pDtmf) and // the other three(Channel[nChan].cDtmf[1], [2], [3]), ie., put *Dtmf into // Channel[nChan].cDtmf[0]. if (ThreeDigitsFlag) { Channel[nChan].cDtmf[0] = *pDtmf; ThreeDigitsFlag = 0; } pDigit = Channel[nChan].cDtmf; for (i=0; inChanNum].fhVoxFile); printf(" Exit : PlayIndex_Exit\n"); } //*************************************************************************** // play "thanks.vox" entry function //*************************************************************************** void PlayThanx_Entry(struct ChanEvent *Event) { Play(Event->nChanNum, "thanks.vox", 1); printf(" Enter: PlayThanx_Entry\n"); } //*************************************************************************** // play "thanks.vox" exit function //*************************************************************************** void PlayThanx_Exit(struct ChanEvent *Event) { nTimes[Event->nChanNum] = 0; close(Channel[Event->nChanNum].fhVoxFile); printf(" Exit : PlayThanx_Exit\n"); } //*************************************************************************** // exit function //*************************************************************************** void Exit_Entry() { int nLineNum; for (nLineNum=1; nLineNum <= NUMCHAN; nLineNum++) Bcm6_SetHook(nLineNum, H_ONH); Bcm3_StopSystem(); printf(" Enter: Exit_Entry\n"); printf("\n\n PROGRAM TERMINATED.\n\n"); exit(0); } //*************************************************************************** // Play voice file //*************************************************************************** void Play(int nChan, char *szFileName, int nTermFlag) { int nReturnCode, fhFile; // open VOX file if (!(fhFile = open(szFileName, O_BINARY | O_RDONLY))) { printf("Error in opening %s\n", szFileName); Exit_Entry(); } // save file handle for closing it Channel[nChan].fhVoxFile = fhFile; // set Tcb Bcm_ClearTCB(&Tcb); Tcb.FileHandle = fhFile; Tcb.DTMF_Term = nTermFlag ? (BYTE) '@' : 0; Tcb.LoopDrop = 1; // start playing if (nReturnCode = Bcm13_PlayFile(nChan, &Tcb)) { printf("Error in playing %s, Return Code = %d\n", szFileName, nReturnCode); Exit_Entry(); } } //*************************************************************************** // Read in header of the indexed file //*************************************************************************** int InitHeader(char *szFileName, struct OffLen Header[], int nTenEntries) { int fhIndexFile, nCount, nTotalEntries, i; unsigned long dwTempBuffer[6]; // open indexed voice file if (!(fhIndexFile = open(szFileName, O_BINARY | O_RDONLY))) { printf("Error in opening %s\n", szFileName); Exit_Entry(); } // There are 6 long words in the beginning of the voice file. // And the third one contains the number of entries in this file. nCount = read(fhIndexFile, (char *)dwTempBuffer, 6*sizeof(long int)); if (nCount < 6*sizeof(long int)) { printf("Error in reading %s\n", szFileName); Exit_Entry(); } // third word nTotalEntries = (int)dwTempBuffer[2]; if (nTotalEntries > nTenEntries) nTotalEntries = nTenEntries; // Each entry has 3 long words of data. // 1st : offset // 2nd : length // 3rd : annotation (not used here) for (i=0; inChanNum].nChanState; // leave current state pfnExit = LookUpTbl[nCurrState].pfnExitFunction; (*pfnExit)(Event); // execute function //find the new state of this channel */ nNewState = NextState[nCurrState][Event->nEventType]; Channel[Event->nChanNum].nChanState = nNewState; // get into new state pfnEntry = LookUpTbl[nNewState].pfnEntryFunction; (*pfnEntry)(Event); // execute function } //*************************************************************************** // process events //*************************************************************************** void EventProcess(struct ChanEvent *Event) { int nKeyStroke = 0 ; int nReturnCode; int nEvtChan, nEvtCode, nEvtData; do { // check if any key struck, get the key if yes if (kbhit()) nKeyStroke = getch() ; // check if any event occurred nReturnCode = Bcm16_GetEvent(&nEvtChan, &nEvtCode, &nEvtData); } while ( (nReturnCode == 0) && (nKeyStroke != ESC) ); // as long as no event AND no ESC key struck, keep looping // // at this point, an event occurred OR user hit the ESC key // // find the channel first Event->nChanNum = nEvtChan; printf("------------------------------------------\n"); printf(" Channel <%d>: Event is ", Event->nChanNum); if (nReturnCode != 0) // event occurred { switch (nEvtCode) // find out the event type { case T_RING: Event->nEventType = RING; // rings received printf("\"RING\"\n"); break; case T_OFFH: Event->nEventType = OFHK; // off hook complete printf("\"OFHK\"\n"); break; case T_LCTERM: case T_LC: Event->nEventType = LNDS; // line disconnect printf("\"LNDS\"\n"); break; case T_EOF: Event->nEventType = ENDF; // end of file on playback printf("\"ENDF\"\n"); break; case T_IDTIME: Event->nEventType = TOUT; // time out printf("\"IDTIME\"\n"); break; case T_TIME: Event->nEventType = TOUT; // time out printf("\"TOUT\"\n"); break; case T_TERMDT: Event->nEventType = TERM; // terminating DTMF received printf("\"TERM\"\n"); break; case T_MAXDT: Event->nEventType = MAXD; // max DTMF digits received printf("\"MAXD\"\n"); break; case T_ONH: Event->nEventType = ONHK; // on hook complete printf("\"ONHK\"\n"); break; case T_THREETIMES: // three times in timeout case T_POUNDSIGN: // user hit # sign Event->nEventType = XEVT; // extra event printf("\"XEVT\"\n"); break; default: printf("\n\nUnexpected event type %d \n", nEvtCode); Event->nEventType = LNDS; break; } } else // ESC key struck { Event->nEventType = EXIT; // exiting printf("\"EXIT\"\n"); } } //*************************************************************************** // initialize system //*************************************************************************** void Init() { int nReturnCode, nNumOfLine, nLineNum; // check if driver installed if (Bcm_GetSWIntVector()) { // stop system first Bcm3_StopSystem(); // start system if (nReturnCode = Bcm1_StartSystem(nIntLvl, SM_EVENT, 0, 0, &nNumOfLine)) { // cannot start system printf("Unable to start system, Return Code %d\n", nReturnCode); exit(1); } else { // start system OK printf("%d phone lines installed.\n\n", nNumOfLine); } } else { // driver not installed printf("Error: BICOM driver not installed!\n"); exit(1); } // set up every channel for (nLineNum=1; nLineNum <= NUMCHAN; nLineNum++) { // set every channel on hook Channel[nLineNum].nChanState = ST_ONHOOK; // set Event Generation Mask Bcm7_SetEGMask(nLineNum, ( C_LC // enable loop signal event queued +C_RING // enable automatic answering +C_OFFH // enable off hook msg +C_ONH ), // enable on hook msg RINGS ); // # of rings before pick up // set line on hook Bcm6_SetHook(nLineNum, H_ONH); } } //*************************************************************************** // main function //*************************************************************************** void main(int argc, char *argv[]) { // check if user entered interrupt level if (argc > 1) { // get the interrupt user entered sscanf(argv[1], "%d", &nIntLvl); // validate interrupt level if (nIntLvl < 2 || nIntLvl > 7) { printf("Invalid Bicom interrupt level %d\n", nIntLvl); exit(1); } } else { // use default hardware interrupt level 3 nIntLvl = INTLVL; } // print BICOM msg printf("\nBICOM (R) Multi-line Indexed Playing Demo Program\n"); printf("Copyright (c) BICOM 1990, 1991 All rights reserved.\n\n"); // initialize system Init(); // loop forever: get event from queue, process it and go to next state while ( FOREVER ) { EventProcess(&Event); StateTransit(&Event); } }