📄 tapiwave.c
字号:
bConnected = TRUE;
MyPrintf(TEXT("CONNECTED.\r\n"));
hWaveThread = CreateThread(NULL, 0, WaveThread, NULL, 0, &dwWaveThreadID);
}
break;
}
break;
}
}
}
BOOL GetCallInfo()
{
LONG lRet;
while (TRUE)
{
if (lRet = lineGetCallInfo(hCall, pLineCallInfo))
{
MyPrintf(TEXT("lineGetCallInfo failed: %s.\r\n"), FormatTapiError(lRet));
return FALSE;
}
if (pLineCallInfo->dwNeededSize > pLineCallInfo->dwTotalSize)
{
LocalReAlloc(pLineCallInfo, pLineCallInfo->dwNeededSize, LMEM_MOVEABLE);
pLineCallInfo->dwTotalSize = pLineCallInfo->dwNeededSize;
continue;
}
return TRUE;
}
}
// Parse the command line and change the default settings.
BOOL SetupEnvironment (int argc, LPTSTR argv[])
{
int i = 0, j;
TCHAR chFlag;
BOOL bMaxCallsSet = FALSE;
if (argc == 1)
{
PrintHelp();
return FALSE;
}
while (++i < argc)
{
j = 0;
if ((argv[i][j] == TEXT('/')) || (argv[i][j] == TEXT('-')) || (argv[i][j] == TEXT('+')))
j = 1;
chFlag = argv[i][j++];
if (argv[i][j] == TEXT(':'))
j++;
if (argv[i][j] == TEXT('\"'))
j++;
switch(tolower(chFlag))
{
case TEXT('?'):
MyPrintf(TEXT("Runs a test of the TAPI and WAVE system.\r\n"));
PrintHelp();
return FALSE;
case TEXT('l'):
dwDeviceID = _ttoi(&argv[i][j]);
if (!dwDeviceID)
{
MyPrintf(TEXT("Invalid [L]ine selection.\r\n"));
bCommandLineError = TRUE;
PrintHelp();
return FALSE;
}
break;
case TEXT('i'):
dwAddressID = _ttoi(&argv[i][j]);
if (!dwAddressID)
{
MyPrintf(TEXT("Invalid Address [I]D selection.\r\n"));
bCommandLineError = TRUE;
PrintHelp();
return FALSE;
}
break;
case TEXT('x'):
bLogExtraInfo = TRUE;
MyPrintf(TEXT("Extra call and data flow information will be displayed.\r\n"));
break;
case TEXT('a'):
bAnswer = TRUE;
break;
case TEXT('d'):
lstrcpy(szPhoneNumber, &argv[i][j]);
bAnswer = FALSE;
break;
case TEXT('f'):
lstrcpy(szFileName, &argv[i][j]);
break;
case TEXT('w'):
WaveInID = _ttoi(&argv[i][j]);
if (!WaveInID && (argv[i][j] != TEXT('0')))
{
MyPrintf(TEXT("Invalid [W]ave ID selection.\r\n"));
bCommandLineError = TRUE;
PrintHelp();
return FALSE;
}
if (WaveInID == 99)
{
MyPrintf(TEXT("Using WAVE_MAPPER.\r\n"));
WaveOutID = WaveInID = WAVE_MAPPER;
dwWaveMapped = 0;
}
else
WaveOutID = WaveInID;
bWaveLocal = TRUE;
break;
case TEXT('z'):
dwWaveMapped = 0;
break;
case TEXT('p'):
dwTimeToWaitBeforePlaying = _ttoi(&argv[i][j]);
break;
}
}
return TRUE;
}
// Print the help screen.
void PrintHelp()
{
MyPrintf(
TEXT("\r\n")
TEXT("%s [options]\r\n")
TEXT("\r\n")
TEXT("[Wave File] File to record to or play back.\r\n")
TEXT("\r\n")
TEXT("Options:\r\n")
TEXT("/L:# Line Device ID (dwDeviceID) to use.\r\n")
//TEXT("/I:# Line Address ID to use. Ignored when AutoAnswer is set.\r\n")
TEXT("/X Display extra call and data flow information.\r\n")
TEXT("/F:file Use file as the wave file\r\n")
TEXT("/D:[number] Dial number\r\n")
TEXT("/A Answer a call\r\n")
TEXT("\r\n")
//TEXT("/W:# Specify a wave ID to use for both IN and OUT.\r\n")
//TEXT(" If this flag is used, all TAPI is bypassed.\r\n")
//TEXT(" A wave ID of 99 means to use WAVE_MAPPER.\r\n")
TEXT("/Z Don't use WAVE_MAPPED\r\n")
TEXT("/P:# Pause # milliseconds after dialing, before playing.\r\n")
TEXT("\r\n")
TEXT("defaults: /L:%lu /I:%lu\n /F:%s /A\r\n"),
TEXT("\r\n"),
szAppName, dwDeviceID, dwAddressID, szFileName);
}
// Break Hander: Try and terminate gracefully.
// Note that ^Break can call lineShutdown *DURING* a lineMakeCall. Very
// unpredictable results; TAPI is not re-entrant and is *usually* protected by
// the Win16 Mutex. However, a Break Handler is a special case.
BOOL BreakHandlerRoutine(DWORD dwCtrlType)
{
// ^BREAK will always break.
// Use with care; it can mess up TAPI if in the middle of a TAPI API.
if (dwCtrlType == CTRL_BREAK_EVENT)
{
MyPrintf(TEXT("\r\n^[Break] terminated! TAPI may not be left in a usable state.\r\n\r\n"));
lineShutdown(hLineApp);
LocalFree(pLineDevStatus);
LocalFree(pLineCallInfo);
LocalFree(pLineCallParams);
ExitProcess(1);
}
else
{
if (hCall)
MyPrintf(TEXT("\r\n^C Dropping call in progress.\r\n\r\n"));
else
MyPrintf(TEXT("\r\n^C stopped.\r\n\r\n"));
StopEverything();
}
return TRUE;
}
BOOL PumpMessages(BOOL bWaitForMessage)
{
static MSG msg;
if (bReadyToEnd)
return FALSE;
if (bWaitForMessage)
{
if (!GetMessage(&msg, NULL, 0, 0))
return FALSE;
}
else
if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
return TRUE;
else
if (msg.message == WM_QUIT)
return FALSE;
TranslateMessage(&msg);
DispatchMessage(&msg);
return TRUE;
}
void StopEverything()
{
bReadyToEnd = TRUE;
WaitForSingleObject(hWaveThread, INFINITE);
if (hCall && !bDropped)
{
dwLineDropAsyncID = lineDrop(hCall, NULL, 0);
if (dwLineDropAsyncID < 0)
{
MyPrintf(TEXT("lineDrop failed. Terminating\r\n"));
hCall = 0;
}
bDropped = TRUE;
}
// lets prime the pump with a message.
PostThreadMessage(dwThreadID, WM_USER, 0, 0);
}
// ---------------
typedef struct MYWAVEDATA_tag
{
struct MYWAVEDATA_tag * pNext;
WAVEHDR wavehdr;
BYTE data[1];
} MYWAVEDATA, *PMYWAVEDATA;
PrintWaveInfo(WaveInID, WaveOutID);
PMYWAVEDATA LoadWaveInfo(WAVEFORMATEX* pFormat);
LPTSTR FormatWaveOutError(MMRESULT mmResult);
LPTSTR FormatWaveInError(MMRESULT mmResult);
// CONNECTED! Now do data steam testing. Return FALSE to stop TAPI.
DWORD WINAPI WaveThread(LPVOID pVoid)
{
DWORD i;
HWAVEIN hWaveIn = NULL;
HWAVEOUT hWaveOut = NULL;
MMRESULT mmResult;
PMYWAVEDATA pWaveData, pWaveCurr;
BYTE WaveFormatBytes[4096];
PWAVEFORMATEX pFormat = (PWAVEFORMATEX) WaveFormatBytes;
// Get the wave devices to use
if (!bWaveLocal)
{
LONG lRet;
if (lRet = lineGetID(0, 0, hCall, LINECALLSELECT_CALL, pVarString, TEXT("wave/in")))
{
MyPrintf(TEXT("lineGetID failed %s\r\n"), FormatTapiError(lRet));
return FALSE;
}
WaveInID = *(UINT *)((LPBYTE)pVarString + pVarString->dwStringOffset);
if (lRet = lineGetID(0, 0, hCall, LINECALLSELECT_CALL, pVarString, TEXT("wave/out")))
{
MyPrintf(TEXT("lineGetID failed %s\r\n"), FormatTapiError(lRet));
return FALSE;
}
WaveOutID = *(UINT *)((LPBYTE)pVarString + pVarString->dwStringOffset);
}
MyPrintf(TEXT("Using WaveInID %lu and WaveOutID %lu\r\n"),
(DWORD) WaveInID, (DWORD) WaveOutID);
PrintWaveInfo(WaveInID, WaveOutID);
// Load the wave data from file. Yes, this assumes we are playing.
pFormat->cbSize = sizeof(WaveFormatBytes);
pWaveData = LoadWaveInfo(pFormat);
if (pWaveData == NULL)
return 0;
// Open a waveform output device.
mmResult = waveOutOpen(&hWaveOut, WaveOutID, pFormat, 0 , 0L, dwWaveMapped);
if (mmResult)
{
MyPrintf(TEXT("waveOutOpen returned %s\r\n"), FormatWaveOutError(mmResult));
goto end;
}
// First, sleep 5 seconds to give the other end time to answer.
// This is an important step as modems have no idea when the other end actually answered.
// A sleep isn't the best way to wait, but it works for now.
MyPrintf(TEXT("Waiting %lu milliseconds for other end to answer because modems don't know when a VOICE call has answered\r\n"),
dwTimeToWaitBeforePlaying);
for(i = dwTimeToWaitBeforePlaying; i; i-=100)
{
if (!(i%1000))
MyPrintf(TEXT("%lu\r\n"), i/1000);
if (bReadyToEnd)
break;
Sleep(100);
}
// Now start playing, looping through the wave file repeatedly.
pWaveCurr = pWaveData;
while(!bReadyToEnd)
{
// If a buffer is queued, wait till its done.
if (pWaveCurr->wavehdr.dwFlags & WHDR_INQUEUE)
{
if (bLogExtraInfo)
MyPrintf(TEXT("Waiting for wave buffer to finish rendering\r\n"));
Sleep(250);
continue;
}
// If we've used it, we need to unprepare the header
if (pWaveCurr->wavehdr.dwFlags & WHDR_DONE )
{
if(mmResult = waveOutUnprepareHeader(hWaveOut, &pWaveCurr->wavehdr, sizeof(WAVEHDR)))
{
MyPrintf(TEXT("waveOutUnprepareHeader returned %s\r\n"), FormatWaveOutError(mmResult));
break;
}
}
pWaveCurr->wavehdr.dwFlags = 0;
// prepare each WAVEHDR
if(mmResult = waveOutPrepareHeader(hWaveOut, &pWaveCurr->wavehdr, sizeof(WAVEHDR)))
{
MyPrintf(TEXT("waveOutPrepareHeader returned %s\r\n"), FormatWaveOutError(mmResult));
break;
}
// Send to the output device.
if (mmResult = waveOutWrite(hWaveOut, &pWaveCurr->wavehdr, sizeof(WAVEHDR)))
{
MyPrintf(TEXT("waveOutWrite returned %s\r\n"), FormatWaveOutError(mmResult));
break;
}
if (!(pWaveCurr->wavehdr.dwFlags & WHDR_INQUEUE))
{
MyPrintf(TEXT("waveOutWrite did not succesfully queue."));
break;
}
if (bLogExtraInfo)
MyPrintf(TEXT("Wave buffer successfully queued.\r\n"));
pWaveCurr = pWaveCurr->pNext;
if (!pWaveCurr)
pWaveCurr = pWaveData;
}
end:
if (hWaveOut)
{
if (mmResult = waveOutReset(hWaveOut))
MyPrintf(TEXT("waveOutReset returned %s\r\n"), FormatWaveOutError(mmResult));
Sleep(500); // Give it 1/2 sec to actually reset.
if(mmResult = waveOutClose(hWaveOut))
MyPrintf(TEXT("waveOutClose returned %s\r\n"), FormatWaveOutError(mmResult));
Sleep(500); // Give it 1/2 sec to clean up.
hWaveOut = 0;
}
while(pWaveData)
{
pWaveCurr = pWaveData;
pWaveData = pWaveCurr->pNext;
LocalFree(pWaveCurr);
}
return FALSE;
}
PrintWaveInfo(WaveInID, WaveOutID)
{
WAVEINCAPS in;
WAVEOUTCAPS out;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -