东进的语音卡编程:最简单的电话外呼程序
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 32
static 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
{
int nType;
int State;
char TelNum[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;
int iChNo;
}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_Init
Sig_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 reference lrt.setInterval(50); // set delay interval in miliseconds lrt.start(); // start the timer getchar(); // let it run for a while - press Enter lrt.stop(); // stop the timer getchar(); // wait to show it's stopped - Enter lrt.start(200); // start with different delay getchar(); 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 10000 typedef VOID (*LRTCallbackEventProc)(VOID*); class LRTimer { public: // default constructor with 1 second interval LRTimer(DWORD dwInterval=1000); // default destructor ~LRTimer(); // starts timer by creating new thread. interval must be set earlier VOID start(); // starts timer with given interval in miliseconds VOID start(DWORD _interval_ms); // stops the timer VOID stop(); // sets time interval in miliseconds VOID setInterval(DWORD _interval_ms); // returns time interval in ms DWORD getInterval(); // sets function that will be called on time expiration VOID setCallbackProc( LRTCallbackEventProc pcbEventProc, VOID* pcbParam ); // returns true if LRtimer is currently running BOOL isRunning(); // It should be used if the worker class will use CRT functions static HANDLE CrtCreateThread(LPSECURITY_ATTRIBUTES lpsa, DWORD dwStackSize, LPTHREAD_START_ROUTINE pfnThreadProc, void *pvParam, DWORD dwCreationFlags, DWORD *pdwThreadId) throw() { // sanity check for pdwThreadId assert(sizeof(DWORD) == sizeof(unsigned int)); // _beginthreadex calls CreateThread which will set the last error value before it returns return (HANDLE) _beginthreadex(lpsa, dwStackSize, (unsigned int (__stdcall *)(void *)) pfnThreadProc, pvParam, dwCreationFlags, (unsigned int *) pdwThreadId); } private: DWORD m_dwInterval; // interval between alarms LRTCallbackEventProc m_pCallback; // pointer to user callback function VOID *m_pcbParam; // pointer to user callback parameter BOOL m_bRunning; // timer running state HANDLE m_hTimerThread; // handle to timer thread DWORD m_iID; // timer thread id - added for compatibility with Win95/98 // timer clocking tread runtine virtual DWORD WINAPI timerThread(); // wrapper to thread runtine so it can be used within a class static DWORD WINAPI timerThreadAdapter(PVOID _this) { return ((LRTimer*) _this)->timerThread(); } // timer callback APC procedure called when timer is signaled virtual VOID CALLBACK TimerAPCProc(LPVOID, DWORD, DWORD); // wrapper to callback APC procedure so it can be used within a class static 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 0x0500 LRTimer::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 function if (NULL != m_pCallback) (*m_pCallback)(m_pcbParam); #ifdef _DEBUG else printf("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 system if ( bSuccess ) { while (m_bRunning) SleepEx(1, TRUE); // SleepEx(0, TRUE) consumes 100% CPU usage CancelWaitableTimer(hTimer); } else { wsprintfA( szError, "SetWaitableTimer failed with Error %d.", GetLastError() ); #ifdef _DEBUG MessageBoxA( NULL, szError, "Error", MB_ICONEXCLAMATION ); #endif return 1; } CloseHandle(hTimer); return 0; } VOID LRTimer::start() { m_bRunning = TRUE; if (m_hTimerThread != 0) LPTHREAD_START_ROUTINE stop(); #ifndef _INC_CRTDEFS m_hTimerThread = CreateThread(NULL, 0, timerThreadAdapter, this, 0, &m_iID); #else m_hTimerThread = CrtCreateThread(NULL, 0, timerThreadAdapter, this, 0, &m_iID); #endif if (m_hTimerThread == NULL) { #ifdef _DEBUG printf( "CreateThread failed (%d)/n", GetLastError() ); #endif return; } } 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; }