📄 tapiwave.c
字号:
/*----------------------------------------------------------*\
TapiWave
This sample console application is designed to demonstrate
how to use TAPI to play and record wave files using a voice modem.
\*----------------------------------------------------------*/
//#define UNICODE
//#define _UNICODE
#pragma comment(linker, "/subsystem:console")
#pragma comment(lib, "tapi32")
#include <windows.h>
#include <mmsystem.h>
#include <tchar.h>
#include <stdio.h>
#ifdef UNICODE
#define TAPI_CURRENT_VERSION 0x00020000
#else
#define TAPI_CURRENT_VERSION 0x00010004
#endif
#include <tapi.h>
TCHAR szAppName[] = TEXT("TapiWave");
TCHAR szAppFileName[] = TEXT("TapiWave.exe");
// TAPI constants; command line settable
DWORD dwDeviceID = 0;
DWORD dwAddressID = 0;
// TAPI global variables.
HINSTANCE hInstance;
HLINEAPP hLineApp = 0;
DWORD dwNumDevs;
DWORD dwAPIVersion;
HLINE hLine;
HCALL hCall = 0;
LINEEXTENSIONID LineExtensionID;
#if TAPI_CURRENT_VERSION >= 0x00020000
LINEINITIALIZEEXPARAMS lineInitializeExParams = {
sizeof(lineInitializeExParams), 0, 0,
LINEINITIALIZEEXOPTION_USEHIDDENWINDOW,
NULL, 0
};
#endif
TCHAR szPhoneNumber[256] = TEXT("45776");
#define BIGBUFF 8096
LPVARSTRING pVarString = NULL;
LPLINEDEVSTATUS pLineDevStatus = NULL;
LPLINECALLINFO pLineCallInfo = NULL;
LPLINECALLPARAMS pLineCallParams = NULL;
LPLINETRANSLATEOUTPUT pTranslateOutput = NULL;
// State machine information.
DWORD dwMakeCallAsyncID=0;
DWORD dwLineDropAsyncID=0;
BOOL bDropped = FALSE;
BOOL bConnected = FALSE;
BOOL bAnswered = FALSE;
BOOL bReadyToEnd = FALSE;
BOOL bPrintedEnd = FALSE;
// Constants in how the state machine behaves.
BOOL bCommandLineError = FALSE;
BOOL bLogExtraInfo = FALSE;
BOOL bAnswer = TRUE;
// Variables so we can ^C to shutdown and clean up properly.
DWORD dwThreadID;
DWORD dwWaveThreadID;
DWORD dwTimeToWaitBeforePlaying = 5000;
// /// Waveaudio content ///
HANDLE hWaveThread = NULL;
UINT WaveInID = 0;
UINT WaveOutID = 0;
BOOL bWaveLocal = FALSE;
TCHAR szFileName[1024] = TEXT("greeting.wav");
DWORD dwWaveMapped = WAVE_MAPPED;
// Prototypes
void CALLBACK lineCallbackFunc(
DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
DWORD dwParam1, DWORD dwParam2, DWORD dwParam3);
BOOL SetupEnvironment (int argc, LPTSTR argv[]);
void PrintHelp();
BOOL BreakHandlerRoutine(DWORD dwCtrlType);
BOOL GetCallInfo();
void StopEverything();
BOOL PumpMessages(BOOL bWaitForMessage);
DWORD WINAPI WaveThread(LPVOID pVoid);
void PlayFromMemory (LPSTR szWavData);
DWORD WINAPI ThreadRecorded (LPVOID pvThreadParam);
void RecordToMemory (LPSTR szWavData);
void BuildWavData (LPSTR szFilename, LPSTR szWavData, DWORD dwMaxBufferSize);
void __cdecl MyPrintf(LPCTSTR pszFormat, ...);
LPCTSTR FormatError(DWORD dwError);
LPCTSTR FormatErrorBuffer(DWORD dwError, LPTSTR pszBuff, DWORD dwNumChars);
BOOL WINAPI HandlerRoutine(DWORD dwCtrlType);
LPTSTR FormatTapiError (long lError);
// Console app starting place.
int __cdecl _tmain(int argc, _TCHAR *argv[], _TCHAR *envp[])
{
LONG lRet;
MSG msg;
// Setup basic stuff.
hInstance = GetModuleHandle(NULL);
dwThreadID = GetCurrentThreadId();
SetConsoleCtrlHandler((PHANDLER_ROUTINE) BreakHandlerRoutine, TRUE);
if (!SetupEnvironment(argc, argv))
goto end;
if (bWaveLocal)
{
hWaveThread = CreateThread(NULL, 0, WaveThread, NULL, 0, &dwWaveThreadID);
goto end;
}
// Prime the message queue. TAPI callback is called as a result
// of messages being dispatched. By default, console apps don't have
// a message queue to hold these messages. PeekMessage will create it.
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
pVarString = LocalAlloc(LPTR, BIGBUFF);
pLineDevStatus = LocalAlloc(LPTR, BIGBUFF);
pLineCallInfo = LocalAlloc(LPTR, BIGBUFF);
pLineCallParams = LocalAlloc(LPTR, BIGBUFF);
pTranslateOutput = LocalAlloc(LPTR, BIGBUFF);
pVarString -> dwTotalSize = BIGBUFF;
pLineDevStatus -> dwTotalSize = BIGBUFF;
pLineCallInfo -> dwTotalSize = BIGBUFF;
pTranslateOutput -> dwTotalSize = BIGBUFF;
pLineCallParams -> dwTotalSize = BIGBUFF;
pLineCallParams -> dwBearerMode = LINEBEARERMODE_VOICE;
pLineCallParams -> dwMediaMode = LINEMEDIAMODE_AUTOMATEDVOICE,
pLineCallParams -> dwCallParamFlags = 0;
pLineCallParams -> dwAddressMode = LINEADDRESSMODE_ADDRESSID;
pLineCallParams -> dwAddressID = dwAddressID;
#if TAPI_CURRENT_VERSION >= 0x00020000
dwAPIVersion = TAPI_CURRENT_VERSION;
lRet = lineInitializeEx(&hLineApp, hInstance, lineCallbackFunc,
szAppName, &dwNumDevs,
&dwAPIVersion, &lineInitializeExParams);
#else
// Note that you can't use this function and be UNICODE
lRet = lineInitialize(&hLineApp, hInstance, lineCallbackFunc,
szAppName, &dwNumDevs);
#endif
if (lRet)
{
MyPrintf(TEXT("lineInitialize failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
if (lRet = lineNegotiateAPIVersion(hLineApp, dwDeviceID,
0x00010004, 0x00010004, &dwAPIVersion, &LineExtensionID))
{
MyPrintf(TEXT("lineNegotiateAPIVersion failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
if (lRet = lineOpen(hLineApp, dwDeviceID, &hLine, dwAPIVersion, 0, 0,
LINECALLPRIVILEGE_NONE, 0, NULL))
{
MyPrintf(TEXT("lineOpen failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
while (TRUE)
{
if (lRet = lineGetLineDevStatus(hLine, pLineDevStatus))
{
MyPrintf(TEXT("lineGetLineDevStatus failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
if (pLineDevStatus->dwNeededSize > pLineDevStatus->dwTotalSize)
{
LocalReAlloc(pLineDevStatus, pLineDevStatus->dwNeededSize, LMEM_MOVEABLE);
pLineDevStatus->dwTotalSize = pLineDevStatus->dwNeededSize;
continue;
}
break;
}
if (pLineDevStatus -> dwOpenMediaModes)
MyPrintf(TEXT("!!!WARNING!!! Another application is already waiting for calls.\r\n\r\n"));
if (!((pLineDevStatus -> dwLineFeatures) & LINEFEATURE_MAKECALL))
MyPrintf(TEXT("!!!WARNING!!! No call appearances available at this time.\r\n\r\n"));
if (bAnswer)
{
lineClose(hLine);
if (lRet = lineOpen(hLineApp, dwDeviceID, &hLine, dwAPIVersion, 0, 0,
LINECALLPRIVILEGE_OWNER, LINEMEDIAMODE_AUTOMATEDVOICE, NULL))
{
MyPrintf(TEXT("lineOpen failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
MyPrintf(TEXT("Waiting for a call on TAPI Line Device %lu\r\n"), dwDeviceID);
}
else
{
if (lRet = lineTranslateAddress(hLineApp, dwDeviceID, dwAPIVersion, szPhoneNumber,
0, 0, pTranslateOutput))
{
MyPrintf(TEXT("lineTranslateAddress failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
lRet = lineMakeCall(hLine, &hCall, szPhoneNumber, 0, pLineCallParams);
if (lRet < 0)
{
MyPrintf(TEXT("lineMakeCall failed: %s.\r\n"), FormatTapiError(lRet));
goto end;
}
else
{
dwMakeCallAsyncID = lRet;
}
}
// TAPI callback is called only when messages are dispatched!
while (PumpMessages(TRUE));
end:
StopEverything();
if (hLineApp)
lineShutdown(hLineApp);
hLineApp = 0;
if (pLineDevStatus)
LocalFree(pLineDevStatus);
if (pLineCallInfo)
LocalFree(pLineCallInfo);
if (pLineCallParams)
LocalFree(pLineCallParams);
if (pVarString)
LocalFree(pVarString);
if (pTranslateOutput)
LocalFree(pTranslateOutput);
return 1;
}
// Here's the TAPI callback. Mondo switch statement!
void CALLBACK lineCallbackFunc(
DWORD dwDevice, DWORD dwMsg, DWORD dwCallbackInstance,
DWORD dwParam1, DWORD dwParam2, DWORD dwParam3)
{
LONG lRet;
/*
if (bLogExtraInfo)
MyPrintf(TEXT("LCBF: %s\r\n"), // LCBF stands for lineCallBackFunc
FormatLineCallback(
dwDevice, dwMsg, dwCallbackInstance,
dwParam1, dwParam2, dwParam3,
szBuff));
*/
switch(dwMsg)
{
case LINE_LINEDEVSTATE:
if (dwParam1 == LINEDEVSTATE_REINIT)
{
MyPrintf(TEXT("LINEDEVSTATE_REINIT\r\n"));
StopEverything();
}
break;
case LINE_REPLY:
if (dwParam2 == dwLineDropAsyncID)
{
if (dwParam2 != 0)
{
MyPrintf(TEXT("lineDrop LINE_REPLY with failure: %s. Stopping.\r\r\n"),
FormatTapiError((long) dwParam2));
StopEverything();
}
}
else if (dwParam2 == dwMakeCallAsyncID)
{
if (dwParam2 != 0)
{
MyPrintf(TEXT("lineMakeCall LINE_REPLY with failure: %s. Stopping.\r\r\n"),
FormatTapiError((long) dwParam2));
StopEverything();
}
}
// else ignore it.
break;
case LINE_CALLSTATE:
{
// Is this a new call?
if (dwParam3 == LINECALLPRIVILEGE_OWNER)
{
// Do we already have a call?
if (hCall && (hCall != (HCALL) dwDevice))
{
if (dwMsg == LINECALLSTATE_IDLE)
lineDeallocateCall((HCALL) dwDevice);
else
{
MyPrintf(TEXT("New call; %lu, but already managing one. Dropping it.\r\r\n"),
dwDevice);
lineDrop((HCALL) dwDevice, NULL, 0);
}
break;
}
if (hCall)
MyPrintf(
TEXT("Given OWNER privs to a call already owned?\r\n")
TEXT(" - Should only happen if handed a call already owned.\r\n"));
else
{
hCall = (HCALL) dwDevice;
MyPrintf(TEXT("New incoming hCall 0x%lx on TAPI Line Device.\r\r\n"),
hCall, dwDeviceID);
}
}
if (hCall != (HCALL) dwDevice)
{
if (dwMsg == LINECALLSTATE_IDLE)
lineDeallocateCall((HCALL) dwDevice);
else
{
lineDrop((HCALL) dwDevice, NULL, 0);
MyPrintf(TEXT("LINE_CALLSTATE 0x%lx for non-main hCall: 0x%lx. Dropping.\r\n"),
dwParam1, dwDevice);
}
break;
}
switch (dwParam1)
{
case LINECALLSTATE_IDLE:
{
MyPrintf(TEXT("IDLE.\n\r\n"));
lRet = lineDeallocateCall(hCall);
// Should make sure lineDeallocateCall succeeded
hCall = 0;
StopEverything();
break;
}
case LINECALLSTATE_BUSY:
case LINECALLSTATE_DISCONNECTED:
if (dwParam1 == LINECALLSTATE_BUSY)
MyPrintf(TEXT("BUSY. Hanging up.\r\n"));
else
MyPrintf(TEXT("DISCONNECTED. Hanging up.\r\n"));
if (!bDropped)
dwLineDropAsyncID = lineDrop(hCall, NULL, 0);
if (dwLineDropAsyncID < 0)
{
MyPrintf(TEXT("lineDrop failed. Terminating\r\n"));
hCall = 0;
StopEverything();
}
bDropped = TRUE;
break;
case LINECALLSTATE_OFFERING:
case LINECALLSTATE_ACCEPTED: // Could be handed off an accepted call
if (bAnswered)
break;
if (dwParam1 == LINECALLSTATE_OFFERING)
MyPrintf(TEXT("Answering an OFFERING call.\r\n\r\n"));
else
MyPrintf(TEXT("Answering an ACCEPTED call.\r\n\r\n"));
lRet = lineAnswer(hCall, NULL, 0);
// Should check to make sure lineAnswer succeeded
bAnswered = TRUE;
break;
case LINECALLSTATE_CONNECTED:
if (!bConnected)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -