東進的語音卡編程:最簡單的電話外呼程式
cheungmine
2008-6-23
整個工程是Console程式,它對D081A進行編程,實現一個最簡單的電話外呼程式:CallTest.exe。工程包含3個檔案:
CallTest.cpp和LRTimer.h、LRTimer.cpp。
後2個檔案是一個C++的定時器類。這裡沒有使用Windows內建的定時器。CallTest.cpp是一個根據東進的例子程式Dial改編的,但是我去掉了Windows視窗部分,下面我把全部檔案貼出來,通過這個最簡單的程式,算是對東進語音卡狀態機器編程的入門吧:
///////////////////////////////////////////////////////////////////////////////// CallTest.cpp// // 說明:使用東進的D081A語音卡的一個自動呼叫電話的例子。// 完成打一個電話的整個動作。//////// 2008-6-23 建立/////////////////////////////////////////////////////////////////////////////////#include <stdio.h>#include <stdlib.h>#include <assert.h>#include "LRTimer.h"#include "C:/DJDBDK/Inc/tc08a32.h"#include "C:/DJDBDK/Inc/newsig.h"#pragma comment(lib, "C:/DJDBDK/Lib/Tc08a32.lib")#pragma comment(lib, "C:/DJDBDK/Lib/NewSig.lib")#define ACD_SUCCESS 0#define ACD_ERROR 1#define ACD_NUMLEN 32static char VoicePath[]="C:/DJDBDK/Voc/";typedef enum{CH_FREE = 0,CH_DIAL,CH_CHECKSIG,CH_PLAY,CH_ONHOOK,CH_CONNECT,CH_OFFHOOK,CH_BUSY,CH_NOBODY,CH_NOSIGNAL,CH_NODIALTONE,CH_NORESULT}ACD_CHANNEL_STATE;typedef struct{intnType;intState;charTelNum[ACD_NUMLEN];}ACD_LINESTRUCT;// 重設通道void acdResetChannel(ACD_LINESTRUCT* pChannels, int iChNo){if(pChannels[iChNo].nType == CHTYPE_TRUNK){HangUp(iChNo);Sig_ResetCheck(iChNo);StartSigCheck(iChNo);}pChannels[iChNo].TelNum[0]=0;pChannels[iChNo].State = CH_FREE;}// 外線呼出typedef struct{ACD_LINESTRUCT* pChannels;intiChNo;}ACD_DIALOUT;// 實現呼叫工作, 這個是最主要的回呼函數, 在定時事件中回調//static void acdCallJob(void* pJobParam){ACD_LINESTRUCT* pChannels = ((ACD_DIALOUT*)pJobParam)->pChannels;int iChNo = ((ACD_DIALOUT*)pJobParam)->iChNo;// 維持放音操作PUSH_PLAY();// 維持斷續響鈴及訊號音的函數FeedSigFunc();switch(pChannels[iChNo].State){case CH_FREE:break;case CH_DIAL:if(CheckSendEnd(iChNo) == 1){StartSigCheck(iChNo);pChannels[iChNo].State = CH_CHECKSIG;}break;case CH_CHECKSIG:{int res = Sig_CheckDial(iChNo);switch(res){case S_BUSY:pChannels[iChNo].State = CH_BUSY;break;case S_CONNECT:pChannels[iChNo].State = CH_CONNECT;break;case S_NOSIGNAL:pChannels[iChNo].State= CH_NOSIGNAL;break;case S_NOBODY:pChannels[iChNo].State= CH_NOBODY;break;case S_NODIALTONE:pChannels[iChNo].State= CH_NODIALTONE;break;}}break;case CH_BUSY:case CH_NOSIGNAL:case CH_NOBODY:case CH_NODIALTONE:acdResetChannel(pChannels, iChNo);break;case CH_CONNECT:char FileName[MAX_PATH];RsetIndexPlayFile(iChNo);strcpy_s(FileName, MAX_PATH, VoicePath);strcat_s(FileName, MAX_PATH, "dial.001");AddIndexPlayFile(iChNo, FileName);strcpy_s(FileName, MAX_PATH,VoicePath);strcat_s(FileName, MAX_PATH,"dial.002");AddIndexPlayFile(iChNo, FileName);strcpy_s(FileName, MAX_PATH,VoicePath);strcat_s(FileName, MAX_PATH,"d1");AddIndexPlayFile(iChNo, FileName);strcpy_s(FileName, MAX_PATH,VoicePath);strcat_s(FileName, MAX_PATH,"d2");AddIndexPlayFile(iChNo, FileName);strcpy_s(FileName, MAX_PATH,VoicePath);strcat_s(FileName, MAX_PATH,"d8");AddIndexPlayFile(iChNo, FileName);strcpy_s(FileName, MAX_PATH,VoicePath);strcat_s(FileName, MAX_PATH,"d15");AddIndexPlayFile(iChNo, FileName);strcpy_s(FileName, MAX_PATH,VoicePath);strcat_s(FileName, MAX_PATH,"d9");AddIndexPlayFile(iChNo,FileName);strcpy_s(FileName, MAX_PATH,VoicePath);strcat_s(FileName, MAX_PATH,"d7");AddIndexPlayFile(iChNo, FileName);strcpy_s(FileName, MAX_PATH,VoicePath);strcat_s(FileName, MAX_PATH,"dial.003");AddIndexPlayFile(iChNo, FileName);strcpy_s(FileName, MAX_PATH,VoicePath);strcat_s(FileName, MAX_PATH,"dial.004");AddIndexPlayFile(iChNo, FileName);pChannels[iChNo].State = CH_OFFHOOK;break;case CH_OFFHOOK:StartIndexPlayFile(iChNo);pChannels[iChNo].State=CH_PLAY;break;case CH_PLAY:if(CheckIndexPlayFile(iChNo) == 1){StopIndexPlayFile(iChNo);pChannels[iChNo].State = CH_ONHOOK;}break;case CH_ONHOOK:acdResetChannel(pChannels, iChNo);break;}//end switch// 處理異常if(pChannels[iChNo].nType==CHTYPE_TRUNK && pChannels[iChNo].State != CH_FREE){if (Sig_CheckBusy(iChNo)){if(CH_PLAY == pChannels[iChNo].State){StopIndexPlayFile(iChNo);}acdResetChannel(pChannels, iChNo);}}}// 外線呼出void acdDialOut(ACD_LINESTRUCT* pChannels, int iChNo, const char* TelNum){if( CHTYPE_TRUNK != pChannels[iChNo].nType ){MessageBox(0, "請選擇一個外線呼出!", "系統提示", MB_OK);return;}OffHook(iChNo);char tel[ACD_NUMLEN];strcpy_s(tel, ACD_NUMLEN, TelNum);Sig_StartDial(iChNo, tel, "", 0);strcpy_s(pChannels[iChNo].TelNum, ACD_NUMLEN, tel);pChannels[iChNo].State = CH_DIAL;// 啟動定時器//ACD_DIALOUT dialOut;dialOut.pChannels = pChannels;dialOut.iChNo = iChNo;LRTimer timer(100);timer.setCallbackProc(acdCallJob, (void*)&dialOut);timer.start();getchar();timer.stop();}///////////////////////////////////////////////////////////////////////////////// // calltest 主程式/////////////////////////////////////////////////////////////////////////////////#define USES_LINENO 1#define USES_TELNUM "8566"int main(int argc, char** argv){printf("CallTest 東進語音卡 D081A 測試程式 1.0/n");// 載入語音卡驅動//long result = LoadDRV();if ( result != ACD_SUCCESS ) {MessageBox( 0, "LoadDRV 失敗", "系統提示", MB_OK );return ACD_ERROR;}// 檢查通道, 並給每個通道分配緩衝區//WORD wUsedCh = CheckValidCh();printf("匯流排路: %d/n/n", wUsedCh);result = EnableCard(wUsedCh, 8196);if ( result != ACD_SUCCESS ) {FreeDRV();MessageBox( 0, "EnableCard 失敗", "系統提示", MB_OK );return ACD_ERROR;}// 分配通道並設定每通道狀態//ACD_LINESTRUCT * pLines = (ACD_LINESTRUCT*) calloc(wUsedCh, sizeof(ACD_LINESTRUCT));assert(pLines);// 設定撥號參數SetDialPara(1000, 4000, 350, 7);for (int i=0; i<wUsedCh; i++){ pLines[i].nType = CheckChTypeNew(i);pLines[i].State = CH_FREE;}// 在初始化D161A 卡之後調用訊號音檢測初始化函數Sig_InitSig_Init(0);// 檢查每通道狀態//printf(" 線號 狀態 /n-------------------/n");for(int i=0; i<wUsedCh; i++){pLines[i].TelNum[0] = 0;switch (pLines[i].nType){case CHTYPE_TRUNK:printf(" %d/t內線/n", i);break;case CHTYPE_USER:printf(" %d/t外線/n", i);break;case CHTYPE_RECORD:printf(" %d/t錄音/n", i);break;case CHTYPE_EMPTY:printf(" %d/t懸空/n", i);break;}}// 撥號: USES_TELNUM 是要撥的號碼, 使用外線 USES_LINENO//printf("/n#%d 線路正在呼出: %s ...... (按Enter鍵退出)/n", USES_LINENO, USES_TELNUM);acdDialOut(pLines, USES_LINENO, USES_TELNUM);// 釋放每個通道//for(int i=0; i<wUsedCh; i++){if(pLines[i].nType == CHTYPE_TRUNK){// 如果是內線則掛掉此線路HangUp(i);Sig_ResetCheck(i);}}DisableCard();// 釋放驅動FreeDRV();free(pLines);return 0;}
附加的類LRTimer:
/******************************************************************************** LRTimer.h ** ** Written by Max Gurdziel 2005 under GNU General Public License ** contact me: max[at]remoteSOS[dot]com ** ** LRTimer is a low resolution timer class with own timing thread. It allows ** an external callback function to be supplied that will be called in ** pre-defined time intervals. ** The smallest timer interval you can use is 1ms. ** ** Tested with gcc mingw & Visual C++ 6.0 under WindowsXP Home and Pro ** ** ** LRTimer timer; // define LRTimer object ** timer.setInterval(100); // set interval of 100ms ** timer.setCallbackProc(&myCallbackFunction, 0); // set callback function ** // it's prototype is: ** // void myCallbackFunction(void* pParam); ** ** timer.start(); // start the timer ** .... ** timer.stop(); // stops the timer ** .... ** timer.start(200); // starts timer with new interval ** ** ** Example code: ** Copy and paste below sample code to test LRTimer ** *________________________________________________________________________________#include <stdlib.h>#include "LRTimer.h"// define callback function//static void myCallback(void* data) {static DWORD cnt = 0;char c;cnt++;switch (cnt % 4) {case 0: c = '|'; break;case 1: c = '/'; break;case 2: c = '-'; break;case 3: c = '//';}printf("/b%c",c);}int main(int argc, char *argv[]) {LRTimer lrt;lrt.setCallbackProc(&myCallback, NULL);// set the callback function by referencelrt.setInterval(50);// set delay interval in milisecondslrt.start();// start the timergetchar();// let it run for a while - press Enterlrt.stop();// stop the timergetchar();// wait to show it's stopped - Enterlrt.start(200);// start with different delaygetchar();lrt.stop();system("PAUSE");return 0;}________________________________________________________________________________* ** Permission to use, copy, modify, and distribute this software and its ** documentation under the terms of the GNU General Public License is hereby ** granted. No representations are made about the suitability of this software ** for any purpose. It is provided "as is" without express or implied warranty. ** See http://www.gnu.org/copyleft/gpl.html for more details. ** ** All I ask is that if you use LRTimer in your project retain the ** copyright notice. If you have any comments and suggestions please email me ** max[at]remoteSOS[dot]com ** ********************************************************************************/#ifndef LRTIMER_H__#define LRTIMER_H__#define _WIN32_WINNT 0x0500// compile with: /MT /D "_X86_" /c// processor: x86#include <windows.h>#include <process.h> /* _beginthread, _endthread */#include <stdio.h>#include <assert.h>// define a second in terms of 100ns - used with waitable timer API#define _SECOND 10000typedef VOID (*LRTCallbackEventProc)(VOID*);class LRTimer {public:// default constructor with 1 second intervalLRTimer(DWORD dwInterval=1000);// default destructor~LRTimer();// starts timer by creating new thread. interval must be set earlierVOID start();// starts timer with given interval in milisecondsVOID start(DWORD _interval_ms);// stops the timerVOID stop();// sets time interval in milisecondsVOID setInterval(DWORD _interval_ms);// returns time interval in msDWORD getInterval();// sets function that will be called on time expirationVOID setCallbackProc( LRTCallbackEventProc pcbEventProc, VOID* pcbParam );// returns true if LRtimer is currently runningBOOL isRunning();// It should be used if the worker class will use CRT functionsstatic HANDLE CrtCreateThread(LPSECURITY_ATTRIBUTES lpsa, DWORD dwStackSize, LPTHREAD_START_ROUTINE pfnThreadProc, void *pvParam, DWORD dwCreationFlags, DWORD *pdwThreadId) throw(){// sanity check for pdwThreadIdassert(sizeof(DWORD) == sizeof(unsigned int)); // _beginthreadex calls CreateThread which will set the last error value before it returnsreturn (HANDLE) _beginthreadex(lpsa, dwStackSize, (unsigned int (__stdcall *)(void *)) pfnThreadProc, pvParam, dwCreationFlags, (unsigned int *) pdwThreadId);}private:DWORDm_dwInterval;// interval between alarmsLRTCallbackEventProc m_pCallback; // pointer to user callback functionVOID*m_pcbParam;// pointer to user callback parameterBOOLm_bRunning;// timer running stateHANDLEm_hTimerThread;// handle to timer threadDWORDm_iID;// timer thread id - added for compatibility with Win95/98// timer clocking tread runtinevirtual DWORD WINAPI timerThread();// wrapper to thread runtine so it can be used within a classstatic DWORD WINAPI timerThreadAdapter(PVOID _this) {return ((LRTimer*) _this)->timerThread();}// timer callback APC procedure called when timer is signaledvirtual VOID CALLBACK TimerAPCProc(LPVOID, DWORD, DWORD);// wrapper to callback APC procedure so it can be used within a classstatic VOID CALLBACK TimerAPCProcAdapter(PVOID _this, DWORD a1=0, DWORD a2=0) {((LRTimer*) _this)->TimerAPCProc( NULL, a1, a2 );}};#endif
實現檔案:
/******************************************************************************** LRTimer.cpp ** ** Written by Max Gurdziel 2005 under GNU General Public License ** contact me: max[at]remoteSOS[dot]com ** ** LRTimer is a low resolution timer class with own timing thread. It allows ** an external callback function to be supplied that will be called in ** pre-defined time intervals. The smallest timer interval you can use is 1ms. ** ** See header file for more info, usage information and example ** ** ** ** Permission to use, copy, modify, and distribute this software and its ** documentation under the terms of the GNU General Public License is hereby ** granted. No representations are made about the suitability of this software ** for any purpose. It is provided "as is" without express or implied warranty. ** See http://www.gnu.org/copyleft/gpl.html for more details. ** ** All I ask is that if you use LRTimer in your project you retain the ** copyright notice. If you have any comments and suggestions please email me ** max[at]remoteSOS[dot]com ** ** 2008-6-23 Modified by ZhangLiang ** ********************************************************************************/#include "LRTimer.h"#define _WIN32_WINNT 0x0500LRTimer::LRTimer(DWORD dwInterval):m_dwInterval(dwInterval),m_bRunning(FALSE), m_pCallback(NULL),m_pcbParam(NULL), m_hTimerThread(0){}LRTimer::~LRTimer(){}VOID CALLBACK LRTimer::TimerAPCProc(LPVOID, DWORD, DWORD) {// call custom callback functionif (NULL != m_pCallback)(*m_pCallback)(m_pcbParam);#ifdef _DEBUGelseprintf("No callback function set/n");#endif}DWORD WINAPI LRTimer::timerThread() {HANDLE hTimer;BOOL bSuccess;LARGE_INTEGER liDueTime;CHAR szError[255];if ( hTimer = CreateWaitableTimerA( NULL, FALSE, "LRTimer" ) )liDueTime.QuadPart=-(LONGLONG)m_dwInterval * _SECOND;bSuccess = SetWaitableTimer( hTimer, // Handle to the timer object &liDueTime, // When timer will become signaled first time m_dwInterval, // Periodic timer interval TimerAPCProcAdapter, // Completion routine this, // Argument to the completion routine FALSE ); // Do not restore a suspended systemif ( bSuccess ) {while (m_bRunning)SleepEx(1, TRUE);// SleepEx(0, TRUE) consumes 100% CPU usageCancelWaitableTimer(hTimer);} else {wsprintfA( szError, "SetWaitableTimer failed with Error %d.", GetLastError() );#ifdef _DEBUGMessageBoxA( NULL, szError, "Error", MB_ICONEXCLAMATION );#endifreturn 1;}CloseHandle(hTimer);return 0;}VOID LRTimer::start() {m_bRunning = TRUE;if (m_hTimerThread != 0) LPTHREAD_START_ROUTINEstop();#ifndef _INC_CRTDEFSm_hTimerThread = CreateThread(NULL, 0, timerThreadAdapter, this, 0, &m_iID);#elsem_hTimerThread = CrtCreateThread(NULL, 0, timerThreadAdapter, this, 0, &m_iID);#endifif (m_hTimerThread == NULL) {#ifdef _DEBUGprintf( "CreateThread failed (%d)/n", GetLastError() );#endifreturn;}}VOID LRTimer::start(DWORD _interval_ms) {setInterval(_interval_ms);start();}VOID LRTimer::stop(){m_bRunning = FALSE;CloseHandle(m_hTimerThread);m_hTimerThread = 0;}VOID LRTimer::setInterval(DWORD _interval_ms){m_dwInterval = _interval_ms;}DWORD LRTimer::getInterval(){return m_dwInterval;}VOID LRTimer::setCallbackProc( LRTCallbackEventProc pcbEventProc, VOID* pcbParam) {m_pCallback = pcbEventProc;m_pcbParam = pcbParam;}BOOL LRTimer::isRunning() {return m_bRunning;}