东进的语音卡编程:最简单的电话外呼程序
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;
}