///////////////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1998 Active Voice Corporation. All Rights Reserved. // // Active Agent(r) and Unified Communications(tm) are trademarks of Active Voice Corporation. // // Other brand and product names used herein are trademarks of their respective owners. // // The entire program and user interface including the structure, sequence, selection, // and arrangement of the dialog, the exclusively "yes" and "no" choices represented // by "1" and "2," and each dialog message are protected by copyrights registered in // the United States and by international treaties. // // Protected by one or more of the following United States patents: 5,070,526, 5,488,650, // 5,434,906, 5,581,604, 5,533,102, 5,568,540, 5,625,676, 5,651,054. // // Active Voice Corporation // Seattle, Washington // USA // ///////////////////////////////////////////////////////////////////////////////////////// //// // wavin.c - wav input device functions //// #include "winlocal.h" #include "wavin.h" #include "wav.h" #include "acm.h" #include "calc.h" #include "mem.h" #include "str.h" #include "sys.h" #include "trace.h" #include // allow telephone input functions if defined // #ifdef TELIN #include "telin.h" static HTELIN hTelIn = NULL; #endif //// // private definitions //// #define WAVINCLASS TEXT("WavInClass") // wavin control struct // typedef struct WAVIN { DWORD dwVersion; HINSTANCE hInst; HTASK hTask; UINT idDev; LPWAVEFORMATEX lpwfx; HWND hwndNotify; DWORD dwFlags; BOOL fIsOpen; HWAVEIN hWaveIn; WORD wState; HWND hwndCallback; #ifdef MULTITHREAD HANDLE hThreadCallback; DWORD dwThreadId; HANDLE hEventThreadCallbackStarted; HANDLE hEventDeviceOpened; HANDLE hEventDeviceClosed; HANDLE hEventDeviceStopped; CRITICAL_SECTION critSectionStop; #endif UINT nLastError; int cBufsPending; } WAVIN, FAR *LPWAVIN; // helper functions // LRESULT DLLEXPORT CALLBACK WavInCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); #ifdef MULTITHREAD DWORD WINAPI WavInCallbackThread(LPVOID lpvThreadParameter); #endif static LPWAVIN WavInGetPtr(HWAVIN hWavIn); static HWAVIN WavInGetHandle(LPWAVIN lpWavIn); #ifdef MULTITHREAD static LRESULT SendThreadMessage(DWORD dwThreadId, UINT Msg, LPARAM lParam); #endif //// // public functions //// // WavInGetDeviceCount - return number of wav input devices found // this function takes no arguments // return number of wav input devices found (0 if none) // int DLLEXPORT WINAPI WavInGetDeviceCount(void) { return waveInGetNumDevs(); } // WavInDeviceIsOpen - check if input device is open // (i) device id // -1 open any suitable input device // return TRUE if open // BOOL DLLEXPORT WINAPI WavInDeviceIsOpen(int idDev) { BOOL fSuccess = TRUE; BOOL fIsOpen = FALSE; WAVEFORMATEX wfx; HWAVEIN hWaveIn = NULL; int nLastError; #ifdef TELIN if (idDev == TELIN_DEVICEID) return TelInDeviceIsOpen(idDev); #endif // try to open device // if ((nLastError = waveInOpen(&hWaveIn, idDev, #ifndef _WIN32 (LPWAVEFORMAT) #endif WavFormatPcm(-1, -1, -1, &wfx), 0, 0, 0)) != 0) { if (nLastError == MMSYSERR_ALLOCATED) fIsOpen = TRUE; // device in use else { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("waveInOpen failed (%u)\n"), (unsigned) nLastError); } } // close device // else if (waveInClose(hWaveIn) != 0) fSuccess = TraceFALSE(NULL); return fSuccess ? fIsOpen : FALSE; } // WavInOpen - open wav input device // (i) must be WAVIN_VERSION // (i) instance handle of calling module // (i) device id // -1 open any suitable input device // (i) wave format // (i) notify this window of device events // NULL do not notify // (i) device open timeout in milleseconds // 0 default timeout (30000) // (i) device retry timeout in milleseconds // 0 default timeout (2000) // (i) control flags // WAVIN_NOSYNC do not open synchronous devices // WAVIN_OPENRETRY retry if device busy // WAVIN_OPENASYNC return before notification of device open // WAVIN_CLOSEASYNC return before notification of device close // WAVIN_NOACM do not use audio compression manager // WAVIN_TELRFILE telephone will record audio to file on server #ifdef MULTITHREAD // WAVIN_MULTITHREAD support multiple threads #endif // return handle (NULL if error) // // NOTE: if is specified in WavInOpen, // WM_WAVIN_OPEN will be sent to , // when input device has been opened. // // NOTE: if WAVIN_MULTITHREAD is specified in , // it is assumed that is not a window handle, // but rather the id of the thread to receive notifications // HWAVIN DLLEXPORT WINAPI WavInOpen(DWORD dwVersion, HINSTANCE hInst, int idDev, LPWAVEFORMATEX lpwfx, HWND hwndNotify, DWORD msTimeoutOpen, DWORD msTimeoutRetry, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAVIN lpWavIn = NULL; WNDCLASS wc; #ifdef TELIN if (idDev == TELIN_DEVICEID) { hTelIn = TelInOpen(TELIN_VERSION, hInst, idDev, lpwfx, hwndNotify, msTimeoutOpen, msTimeoutRetry, dwFlags); return (HWAVIN) hTelIn; } #endif if (dwVersion != WAVIN_VERSION) fSuccess = TraceFALSE(NULL); else if (hInst == NULL) fSuccess = TraceFALSE(NULL); else if ((lpWavIn = (LPWAVIN) MemAlloc(NULL, sizeof(WAVIN), 0)) == NULL) fSuccess = TraceFALSE(NULL); else { lpWavIn->dwVersion = dwVersion; lpWavIn->hInst = hInst; lpWavIn->hTask = GetCurrentTask(); lpWavIn->idDev = (UINT) idDev; lpWavIn->lpwfx = NULL; lpWavIn->hwndNotify = hwndNotify; lpWavIn->dwFlags = dwFlags; lpWavIn->fIsOpen = FALSE; lpWavIn->hWaveIn = NULL; lpWavIn->wState = WAVIN_STOPPED; lpWavIn->hwndCallback = NULL; #ifdef MULTITHREAD lpWavIn->hThreadCallback = NULL; lpWavIn->dwThreadId = 0; lpWavIn->hEventThreadCallbackStarted = NULL; lpWavIn->hEventDeviceOpened = NULL; lpWavIn->hEventDeviceClosed = NULL; lpWavIn->hEventDeviceStopped = NULL; #endif lpWavIn->nLastError = 0; lpWavIn->cBufsPending = 0; // memory is allocated such that the client app owns it // if ((lpWavIn->lpwfx = WavFormatDup(lpwfx)) == NULL) fSuccess = TraceFALSE(NULL); } #ifdef MULTITHREAD // handle WAVIN_MULTITHREAD flag // if (fSuccess && (lpWavIn->dwFlags & WAVIN_MULTITHREAD)) { DWORD dwRet; InitializeCriticalSection(&(lpWavIn->critSectionStop)); // we need to know when device has been opened // if ((lpWavIn->hEventDeviceOpened = CreateEvent( NULL, FALSE, FALSE, NULL)) == NULL) { fSuccess = TraceFALSE(NULL); } // we need to know when callback thread begins execution // else if ((lpWavIn->hEventThreadCallbackStarted = CreateEvent( NULL, FALSE, FALSE, NULL)) == NULL) { fSuccess = TraceFALSE(NULL); } // create the callback thread // else if ((lpWavIn->hThreadCallback = CreateThread( NULL, 0, WavInCallbackThread, (LPVOID) lpWavIn, 0, &lpWavIn->dwThreadId)) == NULL) { fSuccess = TraceFALSE(NULL); } // wait for the callback thread to begin execution // else if ((dwRet = WaitForSingleObject( lpWavIn->hEventThreadCallbackStarted, 10000)) != WAIT_OBJECT_0) { fSuccess = TraceFALSE(NULL); } // clean up // if (lpWavIn->hEventThreadCallbackStarted != NULL) { if (!CloseHandle(lpWavIn->hEventThreadCallbackStarted)) fSuccess = TraceFALSE(NULL); else lpWavIn->hEventThreadCallbackStarted = NULL; } } else #endif { // register callback class unless it has been already // if (fSuccess && GetClassInfo(lpWavIn->hInst, WAVINCLASS, &wc) == 0) { wc.hCursor = NULL; wc.hIcon = NULL; wc.lpszMenuName = NULL; wc.hInstance = lpWavIn->hInst; wc.lpszClassName = WAVINCLASS; wc.hbrBackground = NULL; wc.lpfnWndProc = WavInCallback; wc.style = 0L; wc.cbWndExtra = sizeof(lpWavIn); wc.cbClsExtra = 0; if (!RegisterClass(&wc)) fSuccess = TraceFALSE(NULL); } // create the callback window // if (fSuccess && (lpWavIn->hwndCallback = CreateWindowEx( 0L, WAVINCLASS, NULL, 0L, 0, 0, 0, 0, NULL, NULL, lpWavIn->hInst, lpWavIn)) == NULL) { fSuccess = TraceFALSE(NULL); } } if (fSuccess) { DWORD dwTimeout = SysGetTimerCount() + (msTimeoutRetry == 0 ? 2000L : msTimeoutRetry); DWORD dwCallback; DWORD dwFlags; #ifdef MULTITHREAD if (lpWavIn->dwFlags & WAVIN_MULTITHREAD) { dwCallback = lpWavIn->dwThreadId; dwFlags = CALLBACK_THREAD; } else #endif { dwCallback = HandleToUlong(lpWavIn->hwndCallback); dwFlags = CALLBACK_WINDOW; } // allow synchronous device drivers unless WAVIN_NOSYNC specified // if (!(lpWavIn->dwFlags & WAVIN_NOSYNC)) dwFlags |= WAVE_ALLOWSYNC; // open the device // while (fSuccess && (lpWavIn->nLastError = waveInOpen(&lpWavIn->hWaveIn, (UINT) lpWavIn->idDev, #ifndef _WIN32 (LPWAVEFORMAT) #endif lpWavIn->lpwfx, dwCallback, (DWORD) 0, dwFlags)) != 0) { // no need to retry unless the device is busy // if (lpWavIn->nLastError != MMSYSERR_ALLOCATED) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("waveInOpen failed (%u)\n"), (unsigned) lpWavIn->nLastError); } // no need to retry if flag not set // else if (!(lpWavIn->dwFlags & WAVIN_OPENRETRY)) fSuccess = TraceFALSE(NULL); // no more retries if timeout occurred // else if (SysGetTimerCount() >= dwTimeout) fSuccess = TraceFALSE(NULL); else { MSG msg; if (PeekMessage(&msg, lpWavIn->hwndCallback, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else #ifdef _WIN32 Sleep(100); #else WaitMessage(); #endif } } } // make sure a handle was returned // if (fSuccess && lpWavIn->hWaveIn == NULL) fSuccess = TraceFALSE(NULL); // wait for device open notification or timeout // if (fSuccess && !(lpWavIn->dwFlags & WAVIN_OPENASYNC)) { DWORD dwTimeout = SysGetTimerCount() + (msTimeoutOpen == 0 ? 2000L : msTimeoutOpen); #ifdef MULTITHREAD if (lpWavIn->dwFlags & WAVIN_MULTITHREAD) { DWORD dwRet; // wait for the device top be opened // if ((dwRet = WaitForSingleObject( lpWavIn->hEventDeviceOpened, (msTimeoutOpen == 0 ? 30000L : msTimeoutOpen))) != WAIT_OBJECT_0) { fSuccess = TraceFALSE(NULL); } } else #endif while (fSuccess && !lpWavIn->fIsOpen) { MSG msg; if (SysGetTimerCount() >= dwTimeout) fSuccess = TraceFALSE(NULL); else if (PeekMessage(&msg, lpWavIn->hwndCallback, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else WaitMessage(); } } #ifdef MULTITHREAD // clean up // if (lpWavIn != NULL && lpWavIn->hEventDeviceOpened != NULL) { if (!CloseHandle(lpWavIn->hEventDeviceOpened)) fSuccess = TraceFALSE(NULL); else lpWavIn->hEventDeviceOpened = NULL; } if (lpWavIn != NULL && lpWavIn->hThreadCallback != NULL) { if (!CloseHandle(lpWavIn->hThreadCallback)) fSuccess = TraceFALSE(NULL); else lpWavIn->hThreadCallback = NULL; } #endif if (fSuccess) { if ((lpWavIn->nLastError = waveInGetID(lpWavIn->hWaveIn, &lpWavIn->idDev)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("waveInGetID failed (%u)\n"), (unsigned) lpWavIn->nLastError); } } if (!fSuccess) { WavInClose(WavInGetHandle(lpWavIn), 0); lpWavIn = NULL; } return fSuccess ? WavInGetHandle(lpWavIn) : NULL; } // WavInClose - close wav input device // (i) handle returned from WavInOpen // (i) device close timeout in milleseconds // 0 default timeout (30000) // return 0 if success // // NOTE: if was specified in WavInOpen, // WM_WAVIN_CLOSE will be sent to , // when input device has been closed. // int DLLEXPORT WINAPI WavInClose(HWAVIN hWavIn, DWORD msTimeoutClose) { BOOL fSuccess = TRUE; LPWAVIN lpWavIn; #ifdef TELIN if (hWavIn != NULL && hWavIn == (HWAVIN) hTelIn) { int iRet = TelInClose((HTELIN) hWavIn, msTimeoutClose); hTelIn = NULL; return iRet; } #endif if ((lpWavIn = WavInGetPtr(hWavIn)) == NULL) fSuccess = TraceFALSE(NULL); // stop the device // else if (WavInStop(hWavIn, 0) != 0) fSuccess = TraceFALSE(NULL); #ifdef MULTITHREAD // we need to know when device has been closed // else if ((lpWavIn->dwFlags & WAVIN_MULTITHREAD) && (lpWavIn->hEventDeviceClosed = CreateEvent( NULL, FALSE, FALSE, NULL)) == NULL) { fSuccess = TraceFALSE(NULL); } #endif // close the device // else if (lpWavIn->hWaveIn != NULL && (lpWavIn->nLastError = waveInClose(lpWavIn->hWaveIn)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("wavInClose failed (%u)\n"), (unsigned) lpWavIn->nLastError); } // wait for device close notification or timeout // if (fSuccess && !(lpWavIn->dwFlags & WAVIN_CLOSEASYNC)) { DWORD dwTimeout = SysGetTimerCount() + (msTimeoutClose == 0 ? 30000L : msTimeoutClose); #ifdef MULTITHREAD if (lpWavIn->dwFlags & WAVIN_MULTITHREAD) { DWORD dwRet; // wait for the device to be closed // if ((dwRet = WaitForSingleObject( lpWavIn->hEventDeviceClosed, (msTimeoutClose == 0 ? 30000L : msTimeoutClose))) != WAIT_OBJECT_0) { fSuccess = TraceFALSE(NULL); } } else #endif while (fSuccess && lpWavIn->fIsOpen) { MSG msg; if (SysGetTimerCount() >= dwTimeout) fSuccess = TraceFALSE(NULL); else if (PeekMessage(&msg, lpWavIn->hwndCallback, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else WaitMessage(); } } #ifdef MULTITHREAD // clean up // if (lpWavIn != NULL && lpWavIn->hEventDeviceClosed != NULL) { if (!CloseHandle(lpWavIn->hEventDeviceClosed)) fSuccess = TraceFALSE(NULL); else lpWavIn->hEventDeviceClosed = NULL; } #endif if (fSuccess) { #ifdef MULTITHREAD if (lpWavIn->dwFlags & WAVIN_MULTITHREAD) { while (lpWavIn->critSectionStop.OwningThread != NULL) Sleep(100L); DeleteCriticalSection(&(lpWavIn->critSectionStop)); } #endif // device handle is no longer valid // lpWavIn->hWaveIn = NULL; // destroy callback window // if (lpWavIn->hwndCallback != NULL && !DestroyWindow(lpWavIn->hwndCallback)) fSuccess = TraceFALSE(NULL); else if (lpWavIn->hwndCallback = NULL, FALSE) fSuccess = TraceFALSE(NULL); else if (lpWavIn->lpwfx != NULL && WavFormatFree(lpWavIn->lpwfx) != 0) fSuccess = TraceFALSE(NULL); else if (lpWavIn->lpwfx = NULL, FALSE) fSuccess = TraceFALSE(NULL); else if ((lpWavIn = MemFree(NULL, lpWavIn)) != NULL) fSuccess = TraceFALSE(NULL); } return fSuccess ? 0 : -1; } // WavInRecord - submit buffer of samples to wav input device for recording // (i) handle returned from WavInOpen // (o) pointer to buffer to be filled with samples // (i) size of buffer in bytes // return 0 if success // // NOTE: the buffer pointed to by must have been allocated // using MemAlloc(). // // NOTE: if is specified in WavInOpen(), a WM_WAVIN_RECORDDONE // message will be sent to , with set to a pointer to // a RECORDDONE structure, when has been recorded. // int DLLEXPORT WINAPI WavInRecord(HWAVIN hWavIn, LPVOID lpBuf, long sizBuf) { BOOL fSuccess = TRUE; LPWAVIN lpWavIn; LPWAVEHDR lpWaveHdr = NULL; #ifdef TELIN if (hWavIn != NULL && hWavIn == (HWAVIN) hTelIn) return TelInRecord((HTELIN) hWavIn, lpBuf, sizBuf, -1); #endif if ((lpWavIn = WavInGetPtr(hWavIn)) == NULL) fSuccess = TraceFALSE(NULL); else if (lpBuf == NULL) fSuccess = TraceFALSE(NULL); else if ((lpWaveHdr = (LPWAVEHDR) MemAlloc(NULL, sizeof(WAVEHDR), 0)) == NULL) { fSuccess = TraceFALSE(NULL); } else { lpWaveHdr->lpData = (LPSTR) lpBuf; lpWaveHdr->dwBufferLength = (DWORD) sizBuf; if ((lpWavIn->nLastError = waveInPrepareHeader(lpWavIn->hWaveIn, lpWaveHdr, sizeof(WAVEHDR))) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("waveInPrepareHeader failed (%u)\n"), (unsigned) lpWavIn->nLastError); } else if ((lpWavIn->nLastError = waveInAddBuffer(lpWavIn->hWaveIn, lpWaveHdr, sizeof(WAVEHDR))) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("waveInAddBuffer failed (%u)\n"), (unsigned) lpWavIn->nLastError); } else if ((lpWavIn->nLastError = waveInStart(lpWavIn->hWaveIn)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("waveInStart failed (%u)\n"), (unsigned) lpWavIn->nLastError); } else { ++lpWavIn->cBufsPending; lpWavIn->wState = WAVIN_RECORDING; } } return fSuccess ? 0 : -1; } // WavInStop - stop recording into buffer(s) sent to wav input device // (i) handle returned from WavInOpen // (i) device stop timeout in milleseconds // 0 default timeout (2000) // return 0 if success // int DLLEXPORT WINAPI WavInStop(HWAVIN hWavIn, DWORD msTimeoutStop) { BOOL fSuccess = TRUE; LPWAVIN lpWavIn; #ifdef TELIN if (hWavIn != NULL && hWavIn == (HWAVIN) hTelIn) return TelInStop((HTELIN) hWavIn, msTimeoutStop); #endif if ((lpWavIn = WavInGetPtr(hWavIn)) == NULL) fSuccess = TraceFALSE(NULL); #ifdef MULTITHREAD else if ((lpWavIn->dwFlags & WAVIN_MULTITHREAD) && (EnterCriticalSection(&(lpWavIn->critSectionStop)), FALSE)) ; #endif // make sure device is recording // else if (WavInGetState(hWavIn) == WAVIN_STOPPED) ; // not an error to call this function when already stopped else if (lpWavIn->wState = WAVIN_STOPPING, FALSE) ; #ifdef MULTITHREAD // we need to know when device has been stopped // else if ((lpWavIn->dwFlags & WAVIN_MULTITHREAD) && (lpWavIn->hEventDeviceStopped = CreateEvent( NULL, FALSE, FALSE, NULL)) == NULL) { fSuccess = TraceFALSE(NULL); } #endif // stop the device // else if ((lpWavIn->nLastError = waveInReset(lpWavIn->hWaveIn)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("waveInReset failed (%u)\n"), (unsigned) lpWavIn->nLastError); } // wait for all pending buffers to complete // #ifdef MULTITHREAD else if (lpWavIn->dwFlags & WAVIN_MULTITHREAD) { DWORD dwRet; LeaveCriticalSection(&(lpWavIn->critSectionStop)); // wait for the device to be stopped // if ((dwRet = WaitForSingleObject( lpWavIn->hEventDeviceStopped, (msTimeoutStop == 0 ? 10000L : msTimeoutStop))) != WAIT_OBJECT_0) { fSuccess = TraceFALSE(NULL); } EnterCriticalSection(&(lpWavIn->critSectionStop)); } #endif else { DWORD dwTimeout = SysGetTimerCount() + (msTimeoutStop == 0 ? 2000L : msTimeoutStop); while (fSuccess && lpWavIn->cBufsPending > 0) { MSG msg; // check for timeout // if (SysGetTimerCount() >= dwTimeout) fSuccess = TraceFALSE(NULL); else if (PeekMessage(&msg, lpWavIn->hwndCallback, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else WaitMessage(); } } #ifdef MULTITHREAD // clean up // if (lpWavIn != NULL && lpWavIn->hEventDeviceStopped != NULL) { if (!CloseHandle(lpWavIn->hEventDeviceStopped)) fSuccess = TraceFALSE(NULL); else lpWavIn->hEventDeviceStopped = NULL; } if (lpWavIn != NULL && (lpWavIn->dwFlags & WAVIN_MULTITHREAD)) LeaveCriticalSection(&(lpWavIn->critSectionStop)); #endif return fSuccess ? 0 : -1; } // WavInGetState - return current wav input device state // (i) handle returned from WavInOpen // return WAVIN_STOPPED, WAVIN_RECORDING, or 0 if error // WORD DLLEXPORT WINAPI WavInGetState(HWAVIN hWavIn) { BOOL fSuccess = TRUE; LPWAVIN lpWavIn; #ifdef TELIN if (hWavIn != NULL && hWavIn == (HWAVIN) hTelIn) return TelInGetState((HTELIN) hWavIn); #endif if ((lpWavIn = WavInGetPtr(hWavIn)) == NULL) fSuccess = TraceFALSE(NULL); return fSuccess ? lpWavIn->wState : 0; } // WavInGetPosition - get milleseconds of elapsed recording // (i) handle returned from WavInOpen // return 0 if success // long DLLEXPORT WINAPI WavInGetPosition(HWAVIN hWavIn) { BOOL fSuccess = TRUE; LPWAVIN lpWavIn; MMTIME mmtime; long msPosition; #ifdef TELIN if (hWavIn != NULL && hWavIn == (HWAVIN) hTelIn) return TelInGetPosition((HTELIN) hWavIn); #endif MemSet(&mmtime, 0, sizeof(mmtime)); // we will be requesting position in millesconds // mmtime.wType = TIME_MS; if ((lpWavIn = WavInGetPtr(hWavIn)) == NULL) fSuccess = TraceFALSE(NULL); // get device position // else if ((lpWavIn->nLastError = waveInGetPosition( lpWavIn->hWaveIn, &mmtime, sizeof(MMTIME))) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("waveInGetPosition failed (%u)\n"), (unsigned) lpWavIn->nLastError); } // see what type of position was returned // else switch (mmtime.wType) { case TIME_MS: { // we got milleseconds; no conversion required // msPosition = (long) mmtime.u.ms; } break; case TIME_SAMPLES: { // convert samples to millesconds // msPosition = (long) MULDIVU32(mmtime.u.sample, 1000L, lpWavIn->lpwfx->nSamplesPerSec); } break; case TIME_BYTES: { // convert bytes to millesconds // msPosition = (long) MULDIVU32(mmtime.u.cb, 1000L, lpWavIn->lpwfx->nAvgBytesPerSec); } break; case TIME_SMPTE: case TIME_MIDI: default: fSuccess = TraceFALSE(NULL); break; } return fSuccess ? msPosition : -1; } // WavInGetId - return id of wav input device // (i) handle returned from WavInOpen // return device id (-1 if error) // int DLLEXPORT WINAPI WavInGetId(HWAVIN hWavIn) { BOOL fSuccess = TRUE; LPWAVIN lpWavIn; #ifdef TELIN if (hWavIn != NULL && hWavIn == (HWAVIN) hTelIn) return TelInGetId((HTELIN) hWavIn); #endif if ((lpWavIn = WavInGetPtr(hWavIn)) == NULL) fSuccess = TraceFALSE(NULL); return fSuccess ? lpWavIn->idDev : -1; } // WavInGetName - get name of wav input device // (i) handle returned from WavInOpen // NULL use unopened device specified in // (i) device id (ignored if is not NULL) // -1 any suitable input device // (o) buffer to hold device name // (i) size of buffer // return 0 if success // int DLLEXPORT WINAPI WavInGetName(HWAVIN hWavIn, int idDev, LPTSTR lpszName, int sizName) { BOOL fSuccess = TRUE; LPWAVIN lpWavIn; #ifdef TELIN if (idDev == TELIN_DEVICEID || (hWavIn != NULL && hWavIn == (HWAVIN) hTelIn)) return TelInGetName((HTELIN) hWavIn, idDev, lpszName, sizName); #endif if (hWavIn != NULL) { if ((lpWavIn = WavInGetPtr(hWavIn)) == NULL) fSuccess = TraceFALSE(NULL); else idDev = lpWavIn->idDev; } if (fSuccess) { WAVEINCAPS wic; UINT nLastError; if ((nLastError = waveInGetDevCaps(idDev, &wic, sizeof(WAVEINCAPS))) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("waveInGetDevCaps failed (%u)\n"), (unsigned) nLastError); } else if (lpszName != NULL) StrNCpy(lpszName, wic.szPname, sizName); if (hWavIn != NULL && lpWavIn != NULL) lpWavIn->nLastError = nLastError; } return fSuccess ? 0 : -1; } // WavInGetIdByName - get id of wav input device, lookup by name // (i) device name #ifdef _WIN32 // NULL or TEXT("") get preferred device id #endif // (i) reserved; must be zero // return device id (-1 if error) // int WINAPI WavInGetIdByName(LPCTSTR lpszName, DWORD dwFlags) { UINT idDev; UINT cDev = (UINT) WavInGetDeviceCount(); // If no device specified, get the preferred device if ( !lpszName || (_tcslen(lpszName) <= 0) ) { DWORD dwTemp; DWORD dwRet = waveInMessage( (HWAVEIN)(DWORD_PTR)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR) &idDev, (DWORD_PTR) &dwTemp ); if ( dwRet == MMSYSERR_NOERROR ) return idDev; } else { // Device specified, search by name for ( idDev = 0; idDev < cDev; ++idDev ) { TCHAR szName[256]; if ( WavInGetName(NULL, idDev, szName, SIZEOFARRAY(szName)) == 0 ) { if ( _tcsicmp(lpszName, szName) == 0 ) return idDev; } } } // No match for device name TraceFALSE(NULL); return -1; } // WavInSupportsFormat - return TRUE if device supports specified format // (i) handle returned from WavInOpen // NULL use unopened device specified in // (i) device id (ignored if is not NULL) // -1 any suitable input device // (i) wave format // return TRUE if device supports specified format // BOOL DLLEXPORT WINAPI WavInSupportsFormat(HWAVIN hWavIn, int idDev, LPWAVEFORMATEX lpwfx) { BOOL fSuccess = TRUE; LPWAVIN lpWavIn; BOOL fSupportsFormat; #ifdef TELIN if (idDev == TELIN_DEVICEID || (hWavIn != NULL && hWavIn == (HWAVIN) hTelIn)) return TelInSupportsFormat((HTELIN) hWavIn, idDev, lpwfx); #endif if (hWavIn != NULL) { if ((lpWavIn = WavInGetPtr(hWavIn)) == NULL) fSuccess = TraceFALSE(NULL); else idDev = lpWavIn->idDev; } if (fSuccess) { UINT nLastError; // query the device // if ((nLastError = waveInOpen(NULL, (UINT) idDev, #ifndef _WIN32 (LPWAVEFORMAT) #endif lpwfx, 0, 0, WAVE_FORMAT_QUERY)) != 0) { fSupportsFormat = FALSE; #if 1 if (TraceGetLevel(NULL) >= 9) { TracePrintf_0(NULL, 9, TEXT("unsupported format:\n")); WavFormatDump(lpwfx); } #endif if (nLastError != WAVERR_BADFORMAT) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("waveInOpen/FormatQuery failed (%u)\n"), (unsigned) nLastError); } } else fSupportsFormat = TRUE; if (hWavIn != NULL && lpWavIn != NULL) lpWavIn->nLastError = nLastError; } return fSuccess ? fSupportsFormat : FALSE; } // WavInFormatSuggest - suggest a new format which the device supports // (i) handle returned from WavInOpen // NULL use unopened device specified in // (i) device id (ignored if is not NULL) // -1 any suitable input device // (i) source format // (i) control flags // WAVIN_NOACM do not use audio compression manager // return pointer to suggested format, NULL if error // // NOTE: the format structure returned is dynamically allocated. // Use WavFormatFree() to free the buffer. // LPWAVEFORMATEX DLLEXPORT WINAPI WavInFormatSuggest( HWAVIN hWavIn, int idDev, LPWAVEFORMATEX lpwfxSrc, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAVIN lpWavIn; LPWAVEFORMATEX lpwfxSuggest = NULL; LPWAVEFORMATEX lpwfxTemp = NULL; HACM hAcm = NULL; #ifdef TELIN if (idDev == TELIN_DEVICEID || (hWavIn != NULL && hWavIn == (HWAVIN) hTelIn)) return TelInFormatSuggest((HTELIN) hWavIn, idDev, lpwfxSrc, dwFlags); #endif if (hWavIn != NULL) { if ((lpWavIn = WavInGetPtr(hWavIn)) == NULL) fSuccess = TraceFALSE(NULL); else { idDev = lpWavIn->idDev; if (lpWavIn->dwFlags & WAVIN_NOACM) dwFlags |= WAVIN_NOACM; } } if ((hAcm = AcmInit(ACM_VERSION, SysGetTaskInstance(NULL), (dwFlags & WAVIN_NOACM) ? ACM_NOACM : 0)) == NULL) fSuccess = TraceFALSE(NULL); else if (!WavFormatIsValid(lpwfxSrc)) fSuccess = TraceFALSE(NULL); else if ((lpwfxTemp = WavFormatDup(lpwfxSrc)) == NULL) fSuccess = TraceFALSE(NULL); // get suggested format, see if it is supported // if (fSuccess && lpwfxSuggest == NULL) { if ((lpwfxSuggest = AcmFormatSuggest(hAcm, lpwfxTemp, -1, -1, -1, -1, 0)) == NULL) fSuccess = TraceFALSE(NULL); else if (WavFormatFree(lpwfxTemp) != 0) fSuccess = TraceFALSE(NULL); else if (!WavInSupportsFormat(NULL, idDev, lpwfxSuggest)) { lpwfxTemp = lpwfxSuggest; lpwfxSuggest = NULL; } } // get suggested PCM format, see if it is supported // if (fSuccess && lpwfxSuggest == NULL && lpwfxTemp->wFormatTag != WAVE_FORMAT_PCM) { if ((lpwfxSuggest = AcmFormatSuggest(hAcm, lpwfxTemp, WAVE_FORMAT_PCM, -1, -1, -1, 0)) == NULL) fSuccess = TraceFALSE(NULL); else if (WavFormatFree(lpwfxTemp) != 0) fSuccess = TraceFALSE(NULL); else if (!WavInSupportsFormat(NULL, idDev, lpwfxSuggest)) { lpwfxTemp = lpwfxSuggest; lpwfxSuggest = NULL; } } // get suggested PCM mono format, see if it is supported // if (fSuccess && lpwfxSuggest == NULL && lpwfxTemp->nChannels != 1) { if ((lpwfxSuggest = AcmFormatSuggest(hAcm, lpwfxTemp, WAVE_FORMAT_PCM, -1, -1, 1, 0)) == NULL) fSuccess = TraceFALSE(NULL); else if (WavFormatFree(lpwfxTemp) != 0) fSuccess = TraceFALSE(NULL); else if (!WavInSupportsFormat(NULL, idDev, lpwfxSuggest)) { lpwfxTemp = lpwfxSuggest; lpwfxSuggest = NULL; } } // get suggested PCM 8-bit mono format, see if it is supported // if (fSuccess && lpwfxSuggest == NULL && lpwfxTemp->wBitsPerSample != 8) { if ((lpwfxSuggest = AcmFormatSuggest(hAcm, lpwfxTemp, WAVE_FORMAT_PCM, -1, 8, 1, 0)) == NULL) fSuccess = TraceFALSE(NULL); else if (WavFormatFree(lpwfxTemp) != 0) fSuccess = TraceFALSE(NULL); else if (!WavInSupportsFormat(NULL, idDev, lpwfxSuggest)) { lpwfxTemp = lpwfxSuggest; lpwfxSuggest = NULL; } } // get suggested PCM 11025Hz 8-bit mono format, see if it is supported // if (fSuccess && lpwfxSuggest == NULL && lpwfxTemp->nSamplesPerSec != 11025) { if ((lpwfxSuggest = AcmFormatSuggest(hAcm, lpwfxTemp, WAVE_FORMAT_PCM, 11025, 8, 1, 0)) == NULL) fSuccess = TraceFALSE(NULL); else if (WavFormatFree(lpwfxTemp) != 0) fSuccess = TraceFALSE(NULL); else if (!WavInSupportsFormat(NULL, idDev, lpwfxSuggest)) { lpwfxTemp = lpwfxSuggest; lpwfxSuggest = NULL; } } // last resort; see if MULAW 8000Hz 8-bit mono format is supported // if (fSuccess && lpwfxSuggest == NULL) { #if 0 if ((lpwfxSuggest = AcmFormatSuggest(hAcm, lpwfxTemp, WAVE_FORMAT_MULAW, 8000, 8, 1, 0)) == NULL) #else if ((lpwfxSuggest = WavFormatMulaw(NULL, 8000)) == NULL) #endif fSuccess = TraceFALSE(NULL); else if (WavFormatFree(lpwfxTemp) != 0) fSuccess = TraceFALSE(NULL); else if (!WavInSupportsFormat(NULL, idDev, lpwfxSuggest)) { // no more chances for success // fSuccess = TraceFALSE(NULL); if (WavFormatFree(lpwfxSuggest) != 0) fSuccess = TraceFALSE(NULL); } } // clean up // if (hAcm != NULL && AcmTerm(hAcm) != 0) fSuccess = TraceFALSE(NULL); else hAcm = NULL; return fSuccess ? lpwfxSuggest : NULL; } // WavInTerm - shut down wav input residuals, if any // (i) instance handle of calling module // (i) control flags // WAV_TELTHUNK terminate telephone thunking layer // return 0 if success // int DLLEXPORT WINAPI WavInTerm(HINSTANCE hInst, DWORD dwFlags) { BOOL fSuccess = TRUE; if (hInst == NULL) fSuccess = TraceFALSE(NULL); #ifdef TELIN else if ((dwFlags & WAV_TELTHUNK) && TelInTerm(hInst, dwFlags) != 0) fSuccess = TraceFALSE(NULL); #endif return fSuccess ? 0 : -1; } //// // helper functions //// #ifdef MULTITHREAD DWORD WINAPI WavInCallbackThread(LPVOID lpvThreadParameter) { BOOL fSuccess = TRUE; MSG msg; LPWAVIN lpWavIn = (LPWAVIN) lpvThreadParameter; // make sure message queue is created before calling SetEvent // PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE); // notify main thread that callback thread has begun execution // if (!SetEvent(lpWavIn->hEventThreadCallbackStarted)) { fSuccess = TraceFALSE(NULL); } while (fSuccess && GetMessage(&msg, NULL, 0, 0)) { WavInCallback((HWND) lpWavIn, msg.message, msg.wParam, msg.lParam); // exit thread when when have processed last expected message // if (msg.message == MM_WIM_CLOSE) break; } return 0; } #endif // WavInCallback - window procedure for wavin callback // LRESULT DLLEXPORT CALLBACK WavInCallback(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { BOOL fSuccess = TRUE; LRESULT lResult; LPWAVIN lpWavIn; #ifdef MULTITHREAD if (!IsWindow(hwnd)) lpWavIn = (LPWAVIN) hwnd; else #endif // retrieve lpWavIn from window extra bytes // lpWavIn = (LPWAVIN) GetWindowLongPtr(hwnd, 0); switch (msg) { case WM_NCCREATE: { LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam; LPWAVIN lpWavIn = (LPWAVIN) lpcs->lpCreateParams; // store lpWavIn in window extra bytes // SetWindowLongPtr(hwnd, 0, (LONG_PTR) lpWavIn); lResult = DefWindowProc(hwnd, msg, wParam, lParam); } break; case MM_WIM_OPEN: { HWAVEIN hWaveIn = (HWAVEIN) wParam; TraceOutput(NULL, 5, TEXT("MM_WIM_OPEN\n")); #ifdef MULTITHREAD if (!(lpWavIn->dwFlags & WAVIN_MULTITHREAD) && hWaveIn != lpWavIn->hWaveIn) #else if (hWaveIn != lpWavIn->hWaveIn) #endif fSuccess = TraceFALSE(NULL); else { lpWavIn->fIsOpen = TRUE; #ifdef MULTITHREAD // notify main thread that device is open // if ((lpWavIn->dwFlags & WAVIN_MULTITHREAD) && !SetEvent(lpWavIn->hEventDeviceOpened)) { fSuccess = TraceFALSE(NULL); } #endif // send notification of device opening // #ifdef MULTITHREAD if (lpWavIn->dwFlags & WAVIN_MULTITHREAD) { if ( lpWavIn->hwndNotify ) PostThreadMessage( HandleToUlong(lpWavIn->hwndNotify), WM_WAVIN_OPEN, 0, 0); } else #endif { if (lpWavIn->hwndNotify != NULL && IsWindow(lpWavIn->hwndNotify)) { SendMessage(lpWavIn->hwndNotify, WM_WAVIN_OPEN, 0, 0); } } } lResult = 0L; } break; case MM_WIM_CLOSE: { HWAVEIN hWaveIn = (HWAVEIN) wParam; TraceOutput(NULL, 5, TEXT("MM_WIM_CLOSE\n")); #ifdef MULTITHREAD if (!(lpWavIn->dwFlags & WAVIN_MULTITHREAD) && hWaveIn != lpWavIn->hWaveIn) #else if (hWaveIn != lpWavIn->hWaveIn) #endif fSuccess = TraceFALSE(NULL); else { lpWavIn->fIsOpen = FALSE; // send notification of device closure // #ifdef MULTITHREAD if (lpWavIn->dwFlags & WAVIN_MULTITHREAD) { if ( lpWavIn->hwndNotify ) PostThreadMessage(HandleToUlong(lpWavIn->hwndNotify), WM_WAVIN_CLOSE, 0, 0); } else #endif { if (lpWavIn->hwndNotify != NULL && IsWindow(lpWavIn->hwndNotify)) { SendMessage(lpWavIn->hwndNotify, WM_WAVIN_CLOSE, 0, 0); } } #ifdef MULTITHREAD // notify main thread that device is closed // if ((lpWavIn->dwFlags & WAVIN_MULTITHREAD) && !SetEvent(lpWavIn->hEventDeviceClosed)) { fSuccess = TraceFALSE(NULL); } #endif } lResult = 0L; } break; case MM_WIM_DATA: { HWAVEIN hWaveIn = (HWAVEIN) wParam; LPWAVEHDR lpWaveHdr = (LPWAVEHDR) lParam; LPVOID lpBuf; long sizBuf; long lBytesRecorded; TracePrintf_1(NULL, 5, TEXT("MM_WIM_DATA (%lu)\n"), (unsigned long) lpWaveHdr->dwBytesRecorded); #ifdef MULTITHREAD if (lpWavIn->dwFlags & WAVIN_MULTITHREAD) EnterCriticalSection(&(lpWavIn->critSectionStop)); #endif #ifdef MULTITHREAD if (!(lpWavIn->dwFlags & WAVIN_MULTITHREAD) && hWaveIn != lpWavIn->hWaveIn) #else if (hWaveIn != lpWavIn->hWaveIn) #endif fSuccess = TraceFALSE(NULL); else if (lpWaveHdr == NULL) fSuccess = TraceFALSE(NULL); // NULL buffer is possible with telephone, this is ok // else if ((lpBuf = (LPVOID) lpWaveHdr->lpData) == NULL, FALSE) ; else if (sizBuf = (long) lpWaveHdr->dwBufferLength, FALSE) fSuccess = TraceFALSE(NULL); else if (lBytesRecorded = (long) lpWaveHdr->dwBytesRecorded, FALSE) fSuccess = TraceFALSE(NULL); else if (!(lpWaveHdr->dwFlags & WHDR_DONE)) fSuccess = TraceFALSE(NULL); else if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) fSuccess = TraceFALSE(NULL); else if ((lpWavIn->nLastError = waveInUnprepareHeader( lpWavIn->hWaveIn, lpWaveHdr, sizeof(WAVEHDR))) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("waveInUnprepareHeader failed (%u)\n"), (unsigned) lpWavIn->nLastError); } else if ((lpWaveHdr = MemFree(NULL, lpWaveHdr)) != NULL) fSuccess = TraceFALSE(NULL); else if (--lpWavIn->cBufsPending < 0) fSuccess = TraceFALSE(NULL); // device is no longer recording if no more buffers pending // else if (lpWavIn->cBufsPending == 0) { lpWavIn->wState = WAVIN_STOPPED; #ifdef MULTITHREAD // notify main thread that device is stopped // if ((lpWavIn->dwFlags & WAVIN_MULTITHREAD) && lpWavIn->hEventDeviceStopped != NULL && !SetEvent(lpWavIn->hEventDeviceStopped)) { fSuccess = TraceFALSE(NULL); } #endif } if (fSuccess) { RECORDDONE recorddone; recorddone.lpBuf = lpBuf; recorddone.sizBuf = sizBuf; recorddone.lBytesRecorded = lBytesRecorded; // send notification of recording completion // #ifdef MULTITHREAD if (lpWavIn->dwFlags & WAVIN_MULTITHREAD) { if ( lpWavIn->hwndNotify ) { SendThreadMessage(HandleToUlong(lpWavIn->hwndNotify), WM_WAVIN_RECORDDONE, (LPARAM) (LPVOID) &recorddone); } } else #endif { if (lpWavIn->hwndNotify != NULL && IsWindow(lpWavIn->hwndNotify)) { SendMessage(lpWavIn->hwndNotify, WM_WAVIN_RECORDDONE, 0, (LPARAM) (LPVOID) &recorddone); } } } lResult = 0L; #ifdef MULTITHREAD if (lpWavIn->dwFlags & WAVIN_MULTITHREAD) LeaveCriticalSection(&(lpWavIn->critSectionStop)); #endif } break; default: lResult = DefWindowProc(hwnd, msg, wParam, lParam); break; } return lResult; } // WavInGetPtr - verify that wavin handle is valid, // (i) handle returned from WavInInit // return corresponding wavin pointer (NULL if error) // static LPWAVIN WavInGetPtr(HWAVIN hWavIn) { BOOL fSuccess = TRUE; LPWAVIN lpWavIn; if ((lpWavIn = (LPWAVIN) hWavIn) == NULL) fSuccess = TraceFALSE(NULL); else if (IsBadWritePtr(lpWavIn, sizeof(WAVIN))) fSuccess = TraceFALSE(NULL); #ifdef CHECKTASK // make sure current task owns the wavin handle // else if (lpWavIn->hTask != GetCurrentTask()) fSuccess = TraceFALSE(NULL); #endif return fSuccess ? lpWavIn : NULL; } // WavInGetHandle - verify that wavin pointer is valid, // (i) pointer to WAVIN struct // return corresponding wavin handle (NULL if error) // static HWAVIN WavInGetHandle(LPWAVIN lpWavIn) { BOOL fSuccess = TRUE; HWAVIN hWavIn; if ((hWavIn = (HWAVIN) lpWavIn) == NULL) fSuccess = TraceFALSE(NULL); return fSuccess ? hWavIn : NULL; } #ifdef MULTITHREAD static LRESULT SendThreadMessage(DWORD dwThreadId, UINT Msg, LPARAM lParam) { BOOL fSuccess = TRUE; HANDLE hEventMessageProcessed = NULL; DWORD dwRet; // we need to know when message has been processed // if ((hEventMessageProcessed = CreateEvent( NULL, FALSE, FALSE, NULL)) == NULL) { fSuccess = TraceFALSE(NULL); } // post message to thread, send event handle as wParam // else if (!PostThreadMessage(dwThreadId, Msg, (WPARAM) hEventMessageProcessed, lParam)) { fSuccess = TraceFALSE(NULL); } // wait for the message to be processed // else if ((dwRet = WaitForSingleObject( hEventMessageProcessed, INFINITE)) != WAIT_OBJECT_0) { fSuccess = TraceFALSE(NULL); } // clean up // if (hEventMessageProcessed != NULL) { if (!CloseHandle(hEventMessageProcessed)) fSuccess = TraceFALSE(NULL); else hEventMessageProcessed = NULL; } return 0L; } #endif