www.pudn.com > tapiwave.zip > 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#include #include #include #ifdef UNICODE #define TAPI_CURRENT_VERSION 0x00020000 #else #define TAPI_CURRENT_VERSION 0x00010004 #endif #include 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) { 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; if (MMSYSERR_NOERROR == waveInGetDevCaps(WaveInID, &in, sizeof(in))) { MyPrintf( TEXT("WaveInDevCaps:\r\n") TEXT(" wMid: 0x%04X\r\n") TEXT(" wPid: 0x%04X\r\n") TEXT(" vDriverVersion: 0x%04X\r\n") TEXT(" pszPname: %s\r\n") TEXT(" dwFormats: 0x%08X\r\n") TEXT(" dwChannels: 0x%04X\r\n") , in.wMid, in.wPid, in.vDriverVersion, in.szPname, in.dwFormats, in.wChannels); } else MyPrintf(TEXT("waveInGetDevCaps failed.\r\n")); if (MMSYSERR_NOERROR == waveOutGetDevCaps(WaveOutID, &out, sizeof(out))) { MyPrintf( TEXT("WaveOutDevCaps:\r\n") TEXT(" wMid: 0x%04X\r\n") TEXT(" wPid: 0x%04X\r\n") TEXT(" vDriverVersion: 0x%04X\r\n") TEXT(" pszPname: %s\r\n") TEXT(" dwFormats: 0x%08X\r\n") TEXT(" dwChannels: 0x%04X\r\n") , out.wMid, out.wPid, out.vDriverVersion, out.szPname, out.dwFormats, out.wChannels); } else MyPrintf(TEXT("waveInGetDevCaps failed.\r\n")); // TODO Find out if its capable of simultaneous recording and playing } PMYWAVEDATA LoadWaveInfo(WAVEFORMATEX* pFormat) { MMRESULT mmResult; HMMIO hmmio = {0}; MMCKINFO mmckinfoParent; MMCKINFO mmckinfoSubchunk; DWORD dwFmtSize; DWORD dwChunkSize; PMYWAVEDATA pWaveHead = NULL, pWaveCurr = NULL, pWavePrev = NULL; BOOL bLooping = TRUE; DWORD dwBuffers = 0; // Open the given file for reading using buffered I/O. if(!(hmmio = mmioOpen(szFileName, NULL, MMIO_READ | MMIO_ALLOCBUF))) { MyPrintf(TEXT("mmioOpen failed to open file.\r\n")); return NULL; } // Locate a 'RIFF' chunk with a 'WAVE' form type to make sure it's a WAVE file. mmckinfoParent.fccType = mmioFOURCC('W', 'A', 'V', 'E'); if (mmResult = mmioDescend(hmmio, &mmckinfoParent, NULL, MMIO_FINDRIFF)) { MyPrintf(TEXT("mmioDescend RIFF WAVE returned %s\r\n"), FormatWaveOutError(mmResult)); goto end; } // Now, find the format chunk (form type 'fmt '). It should be // a subchunk of the 'RIFF' parent chunk. mmckinfoSubchunk.ckid = mmioFOURCC('f', 'm', 't', ' '); if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) { MyPrintf(TEXT("Wave file corrupt.\r\n")); goto end; } // Get the size of the format chunk, allocate and lock memory for it. dwFmtSize = mmckinfoSubchunk.cksize; if (pFormat->cbSize < dwFmtSize) { MyPrintf(TEXT("Format chunk not big enough.\r\n")); goto end; } // Read the format chunk. if (mmioRead(hmmio, (HPSTR) pFormat, dwFmtSize) != (LONG) dwFmtSize) { MyPrintf(TEXT("mmioRead: failed to read FMT chunk.\r\n")); goto end; } MyPrintf(TEXT("wFormatTag = %lu\r\n"), (DWORD) pFormat->wFormatTag); MyPrintf(TEXT("nChannels = %lu\r\n"), (DWORD) pFormat->nChannels ); MyPrintf(TEXT("nSamplesPerSec = %lu\r\n"), (DWORD) pFormat->nSamplesPerSec); MyPrintf(TEXT("nAvgBytesPerSec = %lu\r\n"), (DWORD) pFormat->nAvgBytesPerSec); MyPrintf(TEXT("nBlockAlign = %lu\r\n"), (DWORD) pFormat->nBlockAlign); MyPrintf(TEXT("wBitsPerSample = %lu\r\n"), (DWORD) pFormat->wBitsPerSample); MyPrintf(TEXT("cbSize = %lu\r\n"), (DWORD) pFormat->cbSize); // Ascend out of the format subchunk. mmioAscend(hmmio, &mmckinfoSubchunk, 0); // Find the data subchunk. mmckinfoSubchunk.ckid = mmioFOURCC('d', 'a', 't', 'a'); if (mmioDescend(hmmio, &mmckinfoSubchunk, &mmckinfoParent, MMIO_FINDCHUNK)) { MyPrintf(TEXT("mmioDescend: No DATA chunk.\r\n")); goto end; } // Get the size of the data subchunk. if (mmckinfoSubchunk.cksize == 0L) { MyPrintf(TEXT("Data chunk actually has no data.\r\n")); goto end; } MyPrintf(TEXT("Size of data is %lu\r\n"),mmckinfoSubchunk.cksize); // Now read the data and allocate MYWAVEDATA buffers dwChunkSize = (pFormat->nAvgBytesPerSec/4); dwChunkSize -= dwChunkSize % pFormat->nBlockAlign; if (dwChunkSize < pFormat->nBlockAlign) { MyPrintf(TEXT("Couldn't calculate a good block size\r\n")); goto end; } while(bLooping) { LONG lRead; pWaveCurr = (PMYWAVEDATA) LocalAlloc(LPTR, dwChunkSize + sizeof(MYWAVEDATA)); pWaveCurr->wavehdr.lpData = pWaveCurr->data; if (pWaveHead == NULL) pWaveHead = pWaveCurr; // Read the waveform data subchunk. lRead = mmioRead(hmmio, pWaveCurr->data, dwChunkSize); pWaveCurr->wavehdr.dwBufferLength = lRead; if (lRead == -1) { MyPrintf(TEXT("Error reading from file.\r\n")); pWaveHead = NULL; // Leak leak goto end; } if (lRead == 0) { LocalFree(pWaveCurr); break; } if ((DWORD)lRead != dwChunkSize) { bLooping = FALSE; } if (pWavePrev != NULL) pWavePrev->pNext = pWaveCurr; pWavePrev = pWaveCurr; dwBuffers++; } MyPrintf(TEXT("There were %lu buffers read from %s\r\n"), dwBuffers, szFileName); end: mmioClose(hmmio, 0); return pWaveHead; } /*=== Printing routines =============================================================*/ /* This is easily used to do error messages like this: MyPrintf(TEXT("API blah failed with error: %s\r\n"), FormatError(GetLastError())); */ #define MAX_PRINT_STRING 1024 //#define MSG_BOX_PRINT #ifdef _DEBUG #define MSG_DEBUG_PRINT #endif #define MSG_CONSOLE_PRINT //#define MSG_FILE_PRINT #ifdef MSG_FILE_PRINT TCHAR szFilePrint[MAX_PATH] = TEXT(".\\out.txt"); BOOL bZeroFile = FALSE; #endif LPTSTR FormatWaveError(MMRESULT mmrError, MMRESULT (WINAPI *pfn) (MMRESULT, LPTSTR, UINT), LPTSTR szErr) { _declspec(thread) static TCHAR szOutput[MAX_PRINT_STRING]; MMRESULT mmResult2; mmResult2 = pfn(mmrError, szOutput, MAX_PRINT_STRING); if (mmResult2 != MMSYSERR_NOERROR) { TCHAR szTmp[256]; MMRESULT mmResult3; mmResult3 = pfn(mmResult2, szOutput, 256); if (mmResult2 != MMSYSERR_NOERROR) wsprintf(szOutput, TEXT("%s returned an %lu on %lu"), szErr, mmResult2, mmrError); else wsprintf(szOutput, TEXT("%s on error %lu"), szTmp, mmrError); } return szOutput; } LPTSTR FormatWaveOutError(MMRESULT mmrError) { return FormatWaveError(mmrError, waveOutGetErrorText, TEXT("waveOutGetError")); } LPTSTR FormatWaveInError(MMRESULT mmrError) { return FormatWaveError(mmrError, waveInGetErrorText, TEXT("waveInGetError")); } // Turn a TAPI Line error into a printable string. LPTSTR FormatTapiError (long lError) { static LPTSTR pszLineError[] = { TEXT("LINEERR No Error"), TEXT("LINEERR_ALLOCATED"), TEXT("LINEERR_BADDEVICEID"), TEXT("LINEERR_BEARERMODEUNAVAIL"), TEXT("LINEERR Unused constant, ERROR!!"), TEXT("LINEERR_CALLUNAVAIL"), TEXT("LINEERR_COMPLETIONOVERRUN"), TEXT("LINEERR_CONFERENCEFULL"), TEXT("LINEERR_DIALBILLING"), TEXT("LINEERR_DIALDIALTONE"), TEXT("LINEERR_DIALPROMPT"), TEXT("LINEERR_DIALQUIET"), TEXT("LINEERR_INCOMPATIBLEAPIVERSION"), TEXT("LINEERR_INCOMPATIBLEEXTVERSION"), TEXT("LINEERR_INIFILECORRUPT"), TEXT("LINEERR_INUSE"), TEXT("LINEERR_INVALADDRESS"), TEXT("LINEERR_INVALADDRESSID"), TEXT("LINEERR_INVALADDRESSMODE"), TEXT("LINEERR_INVALADDRESSSTATE"), TEXT("LINEERR_INVALAPPHANDLE"), TEXT("LINEERR_INVALAPPNAME"), TEXT("LINEERR_INVALBEARERMODE"), TEXT("LINEERR_INVALCALLCOMPLMODE"), TEXT("LINEERR_INVALCALLHANDLE"), TEXT("LINEERR_INVALCALLPARAMS"), TEXT("LINEERR_INVALCALLPRIVILEGE"), TEXT("LINEERR_INVALCALLSELECT"), TEXT("LINEERR_INVALCALLSTATE"), TEXT("LINEERR_INVALCALLSTATELIST"), TEXT("LINEERR_INVALCARD"), TEXT("LINEERR_INVALCOMPLETIONID"), TEXT("LINEERR_INVALCONFCALLHANDLE"), TEXT("LINEERR_INVALCONSULTCALLHANDLE"), TEXT("LINEERR_INVALCOUNTRYCODE"), TEXT("LINEERR_INVALDEVICECLASS"), TEXT("LINEERR_INVALDEVICEHANDLE"), TEXT("LINEERR_INVALDIALPARAMS"), TEXT("LINEERR_INVALDIGITLIST"), TEXT("LINEERR_INVALDIGITMODE"), TEXT("LINEERR_INVALDIGITS"), TEXT("LINEERR_INVALEXTVERSION"), TEXT("LINEERR_INVALGROUPID"), TEXT("LINEERR_INVALLINEHANDLE"), TEXT("LINEERR_INVALLINESTATE"), TEXT("LINEERR_INVALLOCATION"), TEXT("LINEERR_INVALMEDIALIST"), TEXT("LINEERR_INVALMEDIAMODE"), TEXT("LINEERR_INVALMESSAGEID"), TEXT("LINEERR Unused constant, ERROR!!"), TEXT("LINEERR_INVALPARAM"), TEXT("LINEERR_INVALPARKID"), TEXT("LINEERR_INVALPARKMODE"), TEXT("LINEERR_INVALPOINTER"), TEXT("LINEERR_INVALPRIVSELECT"), TEXT("LINEERR_INVALRATE"), TEXT("LINEERR_INVALREQUESTMODE"), TEXT("LINEERR_INVALTERMINALID"), TEXT("LINEERR_INVALTERMINALMODE"), TEXT("LINEERR_INVALTIMEOUT"), TEXT("LINEERR_INVALTONE"), TEXT("LINEERR_INVALTONELIST"), TEXT("LINEERR_INVALTONEMODE"), TEXT("LINEERR_INVALTRANSFERMODE"), TEXT("LINEERR_LINEMAPPERFAILED"), TEXT("LINEERR_NOCONFERENCE"), TEXT("LINEERR_NODEVICE"), TEXT("LINEERR_NODRIVER"), TEXT("LINEERR_NOMEM"), TEXT("LINEERR_NOREQUEST"), TEXT("LINEERR_NOTOWNER"), TEXT("LINEERR_NOTREGISTERED"), TEXT("LINEERR_OPERATIONFAILED"), TEXT("LINEERR_OPERATIONUNAVAIL"), TEXT("LINEERR_RATEUNAVAIL"), TEXT("LINEERR_RESOURCEUNAVAIL"), TEXT("LINEERR_REQUESTOVERRUN"), TEXT("LINEERR_STRUCTURETOOSMALL"), TEXT("LINEERR_TARGETNOTFOUND"), TEXT("LINEERR_TARGETSELF"), TEXT("LINEERR_UNINITIALIZED"), TEXT("LINEERR_USERUSERINFOTOOBIG"), TEXT("LINEERR_REINIT"), TEXT("LINEERR_ADDRESSBLOCKED"), TEXT("LINEERR_BILLINGREJECTED"), TEXT("LINEERR_INVALFEATURE"), TEXT("LINEERR_NOMULTIPLEINSTANCE") }; _declspec(thread) static TCHAR szError[512]; DWORD dwError; HMODULE hTapiUIMod = GetModuleHandle(TEXT("TAPIUI.DLL")); if (hTapiUIMod) { dwError = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, (LPCVOID)hTapiUIMod, TAPIERROR_FORMATMESSAGE(lError), 0, szError, sizeof(szError)/sizeof(TCHAR), NULL); if (dwError) return szError; } // Strip off the high bit to make the error code positive. dwError = (DWORD)lError & 0x7FFFFFFF; if ((lError > 0) || (dwError > sizeof(pszLineError)/sizeof(pszLineError[0]))) { wsprintf(szError, TEXT("Unknown TAPI error code: 0x%lx"), lError); return szError; } return pszLineError[dwError]; } void __cdecl MyPrintf(LPCTSTR pszFormat, ...) { _declspec(thread) static TCHAR szOutput[MAX_PRINT_STRING]; // max printable string length va_list v1; DWORD dwSize; va_start(v1, pszFormat); dwSize = wvsprintf(szOutput, pszFormat, v1); #ifdef MSG_DEBUG_PRINT OutputDebugString(szOutput); #endif #ifdef MSG_CONSOLE_PRINT _tprintf(szOutput); #endif #ifdef MSG_BOX_PRINT MessageBox(NULL, szOutput,TEXT("MyPrintf Output"), MB_OK); #endif #ifdef MSG_FILE_PRINT { static HANDLE hFile = NULL; DWORD dwNumWritten; if (hFile == NULL) { hFile = CreateFile(szFilePrint, GENERIC_WRITE, FILE_SHARE_READ, NULL, bZeroFile ? CREATE_ALWAYS : OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) MyPrintf(TEXT("CreateFile output log file %s failed with %s\r\n"), szFilePrint, FormatError(GetLastError())); else SetFilePointer(hFile, 0, NULL, FILE_END); } if (hFile != INVALID_HANDLE_VALUE) { OVERLAPPED ol = {0}; LockFileEx(hFile, 0, 0, 1, 0, &ol); WriteFile(hFile, szOutput, dwSize*sizeof(TCHAR), &dwNumWritten, NULL); UnlockFileEx(hFile, 0, 1, 0, &ol); } } #endif } LPCTSTR FormatError(DWORD dwError) { _declspec(thread) static TCHAR szBuff[MAX_PRINT_STRING]; return FormatErrorBuffer(dwError, szBuff, MAX_PRINT_STRING); } LPCTSTR FormatErrorBuffer(DWORD dwError, LPTSTR pszBuff, DWORD dwNumChars) { DWORD dwRetFM = 0; dwRetFM = wsprintf(pszBuff, TEXT("%lu - "), dwError); dwRetFM = FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, 0, &pszBuff[dwRetFM], dwNumChars - dwRetFM, NULL); if (dwRetFM == 0) { wsprintf(pszBuff, TEXT("FormatMessage failed on %lu with %lu"), dwError, GetLastError()); } return pszBuff; }