// DEMOANSR.C - This program plays a voice file and waits for recording // message from user. The voice file DEMO.VOX, INVALID.VOX // and THANKS.VOX must be in the same directory with this // program. // // Enter "demoansr " 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 DTMF Detection 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 ESC 0x1B // escape key // 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_HELLO 3 // playing hello #define ST_PLINTR 4 // playing introduction #define ST_GETDGT 5 // getting DTMF digit #define ST_TASKNG 6 // jump to dtmf lookup table #define ST_PLTHNX 7 // playing thanks message #define ST_EXIT 8 // exiting #define NUM_STATE 9 // 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 DTMF 5 // required DTMF digit received #define EXIT 6 // exiting #define ONHK 7 // on hook complete #define NUM_EVENT 8 // number of events // STRUCTURES static struct ChanData // Channel data structure { int nChanState; // current state int fhVoxFile; // file handle of voice file char cDtmf; // DTMF digit } Channel[NUMCHAN+1]; static struct ChanEvent // Event data sturcture { int nChanNum; // channel number int nEventType; // event type occurred } Event; // VARIABLES static TCB Tcb = {0}; // Task control block static int nIntLvl; // HW interrupt level in use char *MsgFile[NUMCHAN+1] = // names of message file { "\0", "Chan1Msg.vox", "Chan2Msg.vox", "Chan3Msg.vox", "Chan4Msg.vox" }; // PROTOTYPES void main(int, char **); void Init(void); void EventProcess(struct ChanEvent *); void StateTransit(struct ChanEvent *); void Play(int, char *, 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 Playhello_Entry(struct ChanEvent *); void Playhello_Exit(struct ChanEvent *); void PlayIntr_Entry(struct ChanEvent *); void PlayIntr_Exit(struct ChanEvent *); void GetDigit_Entry(struct ChanEvent *); void GetDigit_Exit(struct ChanEvent *); void Task_Entry(struct ChanEvent *); void Task_Exit(struct ChanEvent *); void PlayThanx_Entry(struct ChanEvent *); void PlayThanx_Exit(struct ChanEvent *); void Exit_Entry(void); void PlayMsg(struct ChanEvent *); void RecordMsg(struct ChanEvent *); void PutYourFuncHere(struct ChanEvent *); // 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 DTMF EXIT ONHK */ /* 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_EXIT, ST_ONHOOK}, /* ST_OFHOOK */ {ST_ONHOOK, ST_HELLO, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_EXIT, ST_ONHOOK}, /* ST_HELLO */ {ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_PLINTR, ST_ONHOOK, ST_GETDGT, ST_EXIT, ST_ONHOOK}, /* ST_PLINTR */ {ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_GETDGT, ST_ONHOOK, ST_GETDGT, ST_EXIT, ST_ONHOOK}, /* ST_GETDGT */ {ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_PLTHNX, ST_TASKNG, ST_EXIT, ST_ONHOOK}, /* ST_TASKNG */ {ST_ONHOOK, ST_ONHOOK, ST_ONHOOK, ST_PLTHNX, ST_PLTHNX, ST_PLTHNX, ST_EXIT, ST_ONHOOK}, /* ST_PLTHNX */ {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 Playhello_Entry, Playhello_Exit, // ST_HELLO PlayIntr_Entry, PlayIntr_Exit, // ST_PLINTR GetDigit_Entry, GetDigit_Exit, // ST_GETDGT Task_Entry, Task_Exit, // ST_TASKNG PlayThanx_Entry, PlayThanx_Exit, // ST_PLTHNX Exit_Entry // ST_EXIT }; // DTMF LOOKUP TABLE struct DtmfLookUp { void (*pfnDtmfFunction)(); } DtmfLookUpTbl[] = { RecordMsg, // DTMF 0 PlayMsg, // DTMF 1 PutYourFuncHere, // DTMF 2 PutYourFuncHere, // DTMF 3 PutYourFuncHere, // DTMF 4 PutYourFuncHere, // DTMF 5 PutYourFuncHere, // DTMF 6 PutYourFuncHere, // DTMF 7 PutYourFuncHere, // DTMF 8 PutYourFuncHere, // DTMF 9 PutYourFuncHere, // DTMF 10 PutYourFuncHere // DTMF 11 }; //*************************************************************************** // go onhook entry function //*************************************************************************** void GoOnhook_Entry(struct ChanEvent *Event) { 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) { // clear the DTMF buffer Bcm8_ClearDTMFQ(Event->nChanNum); Channel[Event->nChanNum].cDtmf = 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 "hello.vox" entry function //*************************************************************************** void Playhello_Entry(struct ChanEvent *Event) { Play(Event->nChanNum, "hello.vox", 1); printf(" Enter: Playhello_Entry \n"); } //*************************************************************************** // play "hello.vox" exit function //*************************************************************************** void Playhello_Exit(struct ChanEvent *Event) { close(Channel[Event->nChanNum].fhVoxFile); printf(" Exit : Playhello_Exit\n"); } //*************************************************************************** // play "intr.vox" entry function //*************************************************************************** void PlayIntr_Entry(struct ChanEvent *Event) { Play(Event->nChanNum, "intr.vox", 1); 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 digit entry function //*************************************************************************** void GetDigit_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 = 1; // 1 digit required Tcb.DTMF_Term = 0; // no termination digit Tcb.TimeOut = 5; // max 6 seconds Tcb.LoopDrop = 1; // terminate on drop in loop current Tcb.Flag = 0; // disable beep if ((nReturnCode = Bcm15_GetDTMFString(Event->nChanNum, &Tcb))) printf("Error in getting DTMF, Return Code = %d\n", nReturnCode); printf(" Enter: GetDigit_Entry\n"); } //*************************************************************************** // get digit exit function //*************************************************************************** void GetDigit_Exit(struct ChanEvent *Event) { Event; // compress compiler printf(" Exit : GetDigit_Exit\n"); } //*************************************************************************** // tasking function entry function //*************************************************************************** void Task_Entry(struct ChanEvent *Event) { void (*pfnDtmf)(struct ChanEvent *); printf(" Enter: Task_Entry, "); switch (Channel[Event->nChanNum].cDtmf) { case '0': // record pfnDtmf = DtmfLookUpTbl[0].pfnDtmfFunction; break; case '1': // play pfnDtmf = DtmfLookUpTbl[1].pfnDtmfFunction; break; default: pfnDtmf = DtmfLookUpTbl[2].pfnDtmfFunction; break; } (*pfnDtmf)(Event); } //*************************************************************************** // tasking function exit function //*************************************************************************** void Task_Exit(struct ChanEvent *Event) { switch (Channel[Event->nChanNum].cDtmf) { case '0': close(Channel[Event->nChanNum].fhVoxFile); break; case '1': // truncate the voice file chsize(Channel[Event->nChanNum].fhVoxFile, 0L); close(Channel[Event->nChanNum].fhVoxFile); break; default: close(Channel[Event->nChanNum].fhVoxFile); break; } printf(" Exit : Task_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) { 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 message file //*************************************************************************** void PlayMsg(struct ChanEvent *Event) { Play(Event->nChanNum, MsgFile[Event->nChanNum], 0); printf("Playing Message ...\n"); } //*************************************************************************** // record message file //*************************************************************************** void RecordMsg(struct ChanEvent *Event) { int nReturnCode, fhFile; // open VOX file if (!(fhFile = open(MsgFile[Event->nChanNum], O_BINARY | O_RDWR | O_APPEND | O_CREAT, S_IREAD | S_IWRITE))) { printf("Error in opening %s\n", MsgFile[Event->nChanNum]); Exit_Entry(); } // save file handle for closing it Channel[Event->nChanNum].fhVoxFile = fhFile; // set Tcb Bcm_ClearTCB(&Tcb); Tcb.FileHandle = fhFile; Tcb.TimeOut = 60; // max 30 seconds Tcb.DTMF_Term = '@'; // terminate on any DTMF Tcb.SilenceCnt = 5; // max 5 seconds silence Tcb.LoopDrop = 1; // terminate on drop in loop current Tcb.Flag = 2; // enable beep Tcb.ToneData = 2; // 0.4 (0.2 * 2) second beep // move to end of file lseek(fhFile, 0L, SEEK_END); // start recording if (nReturnCode = Bcm12_RecordFile(Event->nChanNum, &Tcb, RD_SILCOMP)) { printf("Error in recording %s, Return Code = %d\n", MsgFile[Event->nChanNum], nReturnCode); Exit_Entry(); } printf("Recording Message ...\n"); } //*************************************************************************** // put your TASKING function here //*************************************************************************** void PutYourFuncHere(struct ChanEvent *Event) { Play(Event->nChanNum, "invalid.vox", 1); printf("Playing Error Message ...\n"); } //*************************************************************************** // Play voice file //*************************************************************************** void Play(int Ch, char *szFileName, int nTermFlag) { int nReturnCode, fhFile; int nStaReturn = 1; static CSB Csb = {1}; // Channel status block // open VOX file if (!(fhFile = open(szFileName, O_BINARY | O_RDWR))) { printf("Error in opening %s\n", szFileName); Exit_Entry(); } // save file handle for closing it Channel[Ch].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(Ch, &Tcb)) { //find DOS error information if ((nStaReturn=Bcm5_GetChanStatus(Ch, &Csb)) == 0) { printf("channel %d has following status\n", Ch); printf("DOS error %d \n", Csb.DOS_Error); printf("channel mode %d \n", Csb.ChanMode); printf("Channel error %d \n", Csb.Error); printf("Termination condition %d \n", Csb.DOS_Error); } else printf("channel %d has channel error %d \n", Ch, nStaReturn); getch(); printf("Error in playing %s, Return Code = %d\n", szFileName, nReturnCode); Exit_Entry(); } } //*************************************************************************** // transit states //*************************************************************************** void StateTransit(struct ChanEvent *Event) { int nCurrState, nNewState; void (*pfnEntry)(struct ChanEvent *); void (*pfnExit)(struct ChanEvent *); // find the current state of this channel nCurrState = Channel[Event->nChanNum].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_TIME: case T_SIL: case T_IDTIME: Event->nEventType = TOUT; // time out printf("\"TOUT\"\n"); break; case T_TERMDT: case T_MAXDT: Event->nEventType = DTMF; // max DTMF digits received printf("\"DTMF\"\n"); break; case T_ONH: Event->nEventType = ONHK; // on hook complete printf("\"ONHK\"\n"); break; default: printf("Unexpected 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 Call Status Transition 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) { // use 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 Answering Demo Program\n"); printf("Copyright (c) BICOM 1990, 1991 All rights reserved.\n\n"); // initialize system Init(); while ( FOREVER ) { EventProcess(&Event); StateTransit(&Event); } }