///////////////////////////////////////////////////////////////////////////////////////// // // 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 // ///////////////////////////////////////////////////////////////////////////////////////// //// // wav.c - wave file format functions //// #include "winlocal.h" #include #include "wav.h" #ifdef MULTITHREAD #include #endif #include "wavin.h" #include "wavout.h" #include "wavmixer.h" #include "acm.h" #include "mem.h" #include "str.h" #include "trace.h" #include "mmio.h" #ifdef AVTSM #include "avtsm.h" #define TSMTHUNK #ifdef TSMTHUNK #include "tsmthunk.h" #endif #define TSM_OUTBUF_SIZE_FACT 4 #endif // allow telephone output functions if defined // #ifdef TELOUT #include "telout.h" #endif // allow telephone input functions if defined // #ifdef TELIN #include "telin.h" #endif // use telephone thunk layer if defined // #ifdef TELTHUNK #include "telthunk.h" #endif #if defined(TELOUT) || defined(TELIN) #include "telwav.h" #include "vox.h" #endif //// // private definitions //// #define WAVCLASS TEXT("WavClass") #define PLAYCHUNKCOUNT_DEFAULT 3 #define PLAYCHUNKCOUNT_MIN 1 #define PLAYCHUNKCOUNT_MAX 16 #define PLAYCHUNKSIZE_DEFAULT 666 #define PLAYCHUNKSIZE_MIN 111 #define PLAYCHUNKSIZE_MAX 9999999 #define RECORDCHUNKCOUNT_DEFAULT 3 #define RECORDCHUNKCOUNT_MIN 1 #define RECORDCHUNKCOUNT_MAX 16 #define RECORDCHUNKSIZE_DEFAULT 666 #define RECORDCHUNKSIZE_MIN 111 #define RECORDCHUNKSIZE_MAX 9999999 // index into format arrays // #define FORMATFILE 0 #define FORMATPLAY 1 #define FORMATRECORD 2 // internal state flags // #define WAVSTATE_STOPPLAY 0x00000010 #define WAVSTATE_STOPRECORD 0x00000020 #define WAVSTATE_AUTOSTOP 0x00000040 #define WAVSTATE_AUTOCLOSE 0x00000080 // internal array of current handles // #define HWAVOUT_MAX 100 #define HWAVIN_MAX 100 #ifdef TELOUT #define HWAVOUT_MIN -2 #define HWAVOUT_OFFSET 2 #else #define HWAVOUT_MIN -1 #define HWAVOUT_OFFSET 1 #endif static HWAV ahWavOutCurr[HWAVOUT_MAX + HWAVOUT_OFFSET] = { 0 }; #ifdef TELIN #define HWAVIN_MIN -2 #define HWAVIN_OFFSET 2 #else #define HWAVIN_MIN -1 #define HWAVIN_OFFSET 1 #endif static HWAV ahWavInCurr[HWAVIN_MAX + HWAVIN_OFFSET] = { 0 }; // internal storage of defaults // static int cPlayChunksDefault = PLAYCHUNKCOUNT_DEFAULT; static long msPlayChunkSizeDefault = PLAYCHUNKSIZE_DEFAULT; static int cRecordChunksDefault = RECORDCHUNKCOUNT_DEFAULT; static long msRecordChunkSizeDefault = RECORDCHUNKSIZE_DEFAULT; // wavinit control struct // typedef struct WAVINIT { DWORD dwVersion; HINSTANCE hInst; HTASK hTask; DWORD dwFlags; UINT nLastError; HACM hAcm; HACMDRV hAcmDrv; #ifdef TELTHUNK HTELTHUNK hTelThunk; #endif #ifdef TSMTHUNK HTSMTHUNK hTsmThunk; #endif } WAVINIT, FAR *LPWAVINIT; // wav control struct // typedef struct WAV { DWORD dwVersion; HINSTANCE hInst; HTASK hTask; DWORD dwFlags; LPWAVEFORMATEX lpwfx[3]; LPMMIOPROC lpIOProc; int cPlayChunks; long msPlayChunkSize; int cRecordChunks; long msRecordChunkSize; HWND hwndNotify; #ifdef MULTITHREAD HANDLE hThreadCallback; DWORD dwThreadId; HANDLE hEventThreadCallbackStarted; HANDLE hEventStopped; #endif UINT nLastError; HMMIO hmmio; MMCKINFO ckRIFF; MMCKINFO ckfmt; MMCKINFO ckdata; long cbData; long lDataOffset; long lDataPos; long msPositionStop; HWAVIN hWavIn; HWAVOUT hWavOut; HACM hAcm; DWORD dwState; HGLOBAL hResource; long lPosFmt; DWORD dwFlagsPlay; DWORD dwFlagsRecord; int nVolumeLevel; int dwFlagsVolume; int nSpeedLevel; DWORD dwFlagsSpeed; PLAYSTOPPEDPROC lpfnPlayStopped; HANDLE hUserPlayStopped; RECORDSTOPPEDPROC lpfnRecordStopped; DWORD dwUserRecordStopped; #ifdef MULTITHREAD HRESULT hrCoInitialize; #endif #ifdef AVTSM HTSM hTsm; #endif LPTSTR lpszFileName; long msMaxSize; } WAV, FAR *LPWAV; // helper functions // static int WavStopPlay(HWAV hWav); static int WavStopRecord(HWAV hWav); static int WavStopOutputDevice(int idDev, DWORD dwFlags); static int WavStopInputDevice(int idDev, DWORD dwFlags); static HWAV WavGetOutputHandle(int idDev); static HWAV WavGetInputHandle(int idDev); static int WavPlayNextChunk(HWAV hWav); static int WavRecordNextChunk(HWAV hWav); static int WavNotifyCreate(LPWAV lpWav); static int WavNotifyDestroy(LPWAV lpWav); #ifdef MULTITHREAD DWORD WINAPI WavCallbackThread(LPVOID lpvThreadParameter); #endif LRESULT DLLEXPORT CALLBACK WavNotify(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); static int WavCalcPositionStop(HWAV hWav, long cbPosition); static int WavSeekTraceBefore(LPWAV lpWav, long lOffset, int nOrigin); static int WavSeekTraceAfter(LPWAV lpWav, long lPos, long lOffset, int nOrigin); static LPWAV WavGetPtr(HWAV hWav); static HWAV WavGetHandle(LPWAV lpWav); static LPWAVINIT WavInitGetPtr(HWAVINIT hWavInit); static HWAVINIT WavInitGetHandle(LPWAVINIT lpWavInit); #ifdef MULTITHREAD static int SetEventMessageProcessed(LPWAV lpWav, HANDLE hEventMessageProcessed); #endif static int WavTempStop(HWAV hWav, LPWORD lpwStatePrev, LPINT lpidDevPrev); static int WavTempResume(HWAV hWav, WORD wStatePrev, int idDevPrev); //// // public functions //// // WavInit - initialize wav engine // (i) must be WAV_VERSION // (i) instance handle of calling module // (i) control flags // WAV_TELTHUNK initialize telephone thunking layer // WAV_NOTSMTHUNK do not initialize tsm thunking layer // WAV_NOACM do not use audio compression manager // WAV_VOXADPCM load acm driver for Dialogic OKI ADPCM // return handle (NULL if error) // HWAVINIT WINAPI WavInit(DWORD dwVersion, HINSTANCE hInst, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAVINIT lpWavInit = NULL; if (dwVersion != WAV_VERSION) fSuccess = TraceFALSE(NULL); else if (hInst == NULL) fSuccess = TraceFALSE(NULL); else if ((lpWavInit = (LPWAVINIT) MemAlloc(NULL, sizeof(WAVINIT), 0)) == NULL) fSuccess = TraceFALSE(NULL); else { lpWavInit->dwVersion = dwVersion; lpWavInit->hInst = hInst; lpWavInit->hTask = GetCurrentTask(); lpWavInit->dwFlags = dwFlags; lpWavInit->hAcm = NULL; lpWavInit->hAcmDrv = NULL; #ifdef TELTHUNK lpWavInit->hTelThunk = NULL; #endif #ifdef TSMTHUNK lpWavInit->hTsmThunk = NULL; #endif // start the acm engine before any other Wav or Acm functions called // if ((lpWavInit->hAcm = AcmInit(ACM_VERSION, lpWavInit->hInst, (lpWavInit->dwFlags & WAV_NOACM) ? ACM_NOACM : 0)) == NULL) fSuccess = TraceFALSE(NULL); // load voxadpcm driver if specified // else if ((dwFlags & WAV_VOXADPCM) && (!(dwFlags & WAV_NOACM)) && (lpWavInit->hAcmDrv = AcmDriverLoad(lpWavInit->hAcm, MM_ACTIVEVOICE, MM_ACTIVEVOICE_ACM_VOXADPCM, #ifdef _WIN32 TEXT("avvox.acm"), #else TEXT("voxadpcm.acm"), #endif "DriverProc", 0)) == NULL) { fSuccess = TraceFALSE(NULL); } #ifdef TELTHUNK // initialize telephone thunking layer if specified // else if ((dwFlags & WAV_TELTHUNK) && (lpWavInit->hTelThunk = TelThunkInit(TELTHUNK_VERSION, lpWavInit->hInst)) == NULL) { fSuccess = TraceFALSE(NULL); } #endif #ifdef TSMTHUNK // initialize tsm thunking layer if specified // else if (!(dwFlags & WAV_NOTSMTHUNK) && (lpWavInit->hTsmThunk = TsmThunkInit(TSMTHUNK_VERSION, lpWavInit->hInst)) == NULL) { fSuccess = TraceTRUE(NULL); // not a fatal error } #endif } if (!fSuccess) { WavTerm(WavInitGetHandle(lpWavInit)); lpWavInit = NULL; } return fSuccess ? WavInitGetHandle(lpWavInit) : NULL; } // WavTerm - shut down wav engine // (i) handle returned from WavInit // return 0 if success // int WINAPI WavTerm(HWAVINIT hWavInit) { BOOL fSuccess = TRUE; LPWAVINIT lpWavInit; if ((lpWavInit = WavInitGetPtr(hWavInit)) == NULL) fSuccess = TraceFALSE(NULL); else if (WavOutTerm(lpWavInit->hInst, lpWavInit->dwFlags) != 0) fSuccess = TraceFALSE(NULL); else if (WavInTerm(lpWavInit->hInst, lpWavInit->dwFlags) != 0) fSuccess = TraceFALSE(NULL); else { #ifdef TELTHUNK // shut down telephone thunking layer if necessary // if (lpWavInit->hTelThunk != NULL && TelThunkTerm(lpWavInit->hTelThunk) != 0) fSuccess = TraceFALSE(NULL); else lpWavInit->hTelThunk = NULL; #endif #ifdef TSMTHUNK // shut down tsm thunking layer if necessary // if (lpWavInit->hTsmThunk != NULL && TsmThunkTerm(lpWavInit->hTsmThunk) != 0) fSuccess = TraceFALSE(NULL); else lpWavInit->hTsmThunk = NULL; #endif // unload voxadpcm driver if necessary // if (lpWavInit->hAcmDrv != NULL && AcmDriverUnload(lpWavInit->hAcm, lpWavInit->hAcmDrv) != 0) fSuccess = TraceFALSE(NULL); else lpWavInit->hAcmDrv = NULL; // shut down acm engine // if (lpWavInit->hAcm != NULL && AcmTerm(lpWavInit->hAcm) != 0) fSuccess = TraceFALSE(NULL); else lpWavInit->hAcm = NULL; if (fSuccess && (lpWavInit = MemFree(NULL, lpWavInit)) != NULL) fSuccess = TraceFALSE(NULL); } return fSuccess ? 0 : -1; } // WavOpen - open or create wav file // (i) must be WAV_VERSION // (i) instance handle of calling module // (i) name of file to open or create // (i) wave format // NULL use format from header or default // (i) address of i/o procedure to use // NULL use default i/o procedure // (i) data to pass to i/o procedure during open // NULL no data to pass // (i) control flags // WAV_READ open file for reading (default) // WAV_WRITE open file for writing // WAV_READWRITE open file for reading and writing // WAV_DENYNONE allow other programs read and write access // WAV_DENYREAD prevent other programs from read access // WAV_DENYWRITE prevent other programs from write access // WAV_EXCLUSIVE prevent other programs from read or write // WAV_CREATE create new file or truncate existing file // WAV_NORIFF file has no RIFF/WAV header // WAV_MEMORY points to memory block // WAV_RESOURCE points to wave resource // WAV_NOACM do not use audio compression manager // WAV_DELETE delete specified file, return TRUE if success // WAV_EXIST return TRUE if specified file exists // WAV_GETTEMP create temp file, return TRUE if success // WAV_TELRFILE telephone will play audio from file on server #ifdef MULTITHREAD // WAV_MULTITHREAD support multiple threads (default) // WAV_SINGLETHREAD do not support multiple threads // WAV_COINITIALIZE call CoInitialize in all secondary threads #endif // return handle (NULL if error) // // NOTE: if WAV_CREATE or WAV_NORIFF are used in , then the // parameter must be specified. If is NULL, the // current default format is assumed. // WavSetFormat() can be used to set or override the defaults. // // NOTE: if WAV_RESOURCE is specified in , then // must point to a WAVE resource in the module specified by . // If the first character of the string is a pound sign (#), the remaining // characters represent a decimal number that specifies the resource id. // // NOTE: if WAV_MEMORY is specified in , then // must be a pointer to a memory block obtained by calling MemAlloc(). // // NOTE: if is not NULL, this i/o procedure will be called // for opening, closing, reading, writing, and seeking the wav file. // If is not NULL, this array of three (3) DWORDs will be // passed to the i/o procedure when the wav file is opened. // See the Windows mmioOpen() and mmioInstallIOProc() function for details // on these parameters. Also, the WAV_MEMORY and WAV_RESOURCE flags may // only be used when is NULL. // HWAV WINAPI WavOpen(DWORD dwVersion, HINSTANCE hInst, LPCTSTR lpszFileName, LPWAVEFORMATEX lpwfx, LPMMIOPROC lpIOProc, DWORD FAR *lpadwInfo, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAV lpWav = NULL; #ifdef MULTITHREAD // assume WAV_MULTITHREAD unless WAV_SINGLETHREAD specified // if (!(dwFlags & WAV_SINGLETHREAD)) dwFlags |= WAV_MULTITHREAD; #endif if (dwVersion != WAV_VERSION) fSuccess = TraceFALSE(NULL); else if (hInst == NULL) fSuccess = TraceFALSE(NULL); // special case flags that don't actually open a file // return TRUE if success, ignore all other flags // else if ((dwFlags & WAV_EXIST) || (dwFlags & WAV_DELETE) || (dwFlags & WAV_GETTEMP)) { DWORD dwOpenFlags = 0; HMMIO hmmio; if (dwFlags & WAV_EXIST) dwOpenFlags |= MMIO_EXIST; else if (dwFlags & WAV_DELETE) dwOpenFlags |= MMIO_DELETE; else if (dwFlags & WAV_GETTEMP) dwOpenFlags |= MMIO_GETTEMP; // use specified i/o procedure // if (lpIOProc != NULL) { MMIOINFO mmioinfo; MemSet(&mmioinfo, 0, sizeof(mmioinfo)); mmioinfo.pIOProc = lpIOProc; // pass data to the i/o procedure // if (lpadwInfo != NULL) MemCpy(mmioinfo.adwInfo, lpadwInfo, sizeof(mmioinfo.adwInfo)); hmmio = mmioOpen((LPTSTR) lpszFileName, &mmioinfo, dwOpenFlags); } // default i/o procedure // else { hmmio = mmioOpen((LPTSTR) lpszFileName, NULL, dwOpenFlags); } if ((dwFlags & WAV_EXIST) && hmmio == (HMMIO) FALSE) fSuccess = FALSE; // no trace else if ((dwFlags & WAV_DELETE) && hmmio == (HMMIO) FALSE) fSuccess = TraceFALSE(NULL); else if ((dwFlags & WAV_GETTEMP) && hmmio == (HMMIO) FALSE) fSuccess = TraceFALSE(NULL); return (HWAV)IntToPtr(fSuccess); } else if ((lpWav = (LPWAV) MemAlloc(NULL, sizeof(WAV), 0)) == NULL) fSuccess = TraceFALSE(NULL); else { lpWav->dwVersion = dwVersion; lpWav->hInst = hInst; lpWav->hTask = GetCurrentTask(); lpWav->dwFlags = dwFlags; lpWav->lpwfx[FORMATFILE] = NULL; lpWav->lpwfx[FORMATPLAY] = NULL; lpWav->lpwfx[FORMATRECORD] = NULL; lpWav->lpIOProc = lpIOProc; lpWav->cPlayChunks = cPlayChunksDefault; lpWav->msPlayChunkSize = msPlayChunkSizeDefault; lpWav->cRecordChunks = cRecordChunksDefault; lpWav->msRecordChunkSize = msRecordChunkSizeDefault; lpWav->hwndNotify = NULL; #ifdef MULTITHREAD lpWav->hThreadCallback = NULL; lpWav->dwThreadId = 0; lpWav->hEventThreadCallbackStarted = NULL; lpWav->hEventStopped = NULL; #endif lpWav->nLastError = 0; lpWav->hmmio = NULL; lpWav->cbData = 0; lpWav->lDataOffset = 0; lpWav->lDataPos = 0; lpWav->hWavIn = NULL; lpWav->hWavOut = NULL; lpWav->hAcm = NULL; lpWav->msPositionStop = 0L; lpWav->dwState = 0L; lpWav->hResource = NULL; lpWav->lPosFmt = -1; lpWav->dwFlagsPlay = 0; lpWav->dwFlagsRecord = 0; lpWav->nVolumeLevel = 50; lpWav->dwFlagsVolume = 0; lpWav->nSpeedLevel = 100; lpWav->dwFlagsSpeed = 0; lpWav->lpfnPlayStopped = NULL; lpWav->hUserPlayStopped = 0; lpWav->lpfnRecordStopped = NULL; lpWav->dwUserRecordStopped = 0; #ifdef MULTITHREAD lpWav->hrCoInitialize = E_UNEXPECTED; #endif #ifdef AVTSM lpWav->hTsm = NULL; #endif lpWav->lpszFileName = NULL; lpWav->msMaxSize = 0; // start the acm engine before any other Wav or Acm functions called // if ((lpWav->hAcm = AcmInit(ACM_VERSION, lpWav->hInst, (lpWav->dwFlags & WAV_NOACM) ? ACM_NOACM : 0)) == NULL) fSuccess = TraceFALSE(NULL); else { // assume default wave format if none specified // if (lpwfx == NULL) { WAVEFORMATEX wfx; if (WavSetFormat(WavGetHandle(lpWav), WavFormatPcm(-1, -1, -1, &wfx), WAV_FORMATALL) != 0) fSuccess = TraceFALSE(NULL); } // set specified wave format // else if (WavSetFormat(WavGetHandle(lpWav), lpwfx, WAV_FORMATALL) != 0) fSuccess = TraceFALSE(NULL); } } // load WAVE resource if specified // if (fSuccess && (dwFlags & WAV_RESOURCE)) { HRSRC hResInfo; LPVOID lpResource; if ((hResInfo = FindResource(hInst, lpszFileName, TEXT("WAVE"))) == NULL) fSuccess = TraceFALSE(NULL); else if ((lpWav->hResource = LoadResource(hInst, hResInfo)) == NULL) fSuccess = TraceFALSE(NULL); else if ((lpResource = LockResource(lpWav->hResource)) == NULL) fSuccess = TraceFALSE(NULL); else { // now points to a memory block // lpszFileName = lpResource; dwFlags |= WAV_MEMORY; } } if (fSuccess && (dwFlags & WAV_MEMORY)) { // i/o procedure can not be specified with memory block // lpIOProc = NULL; lpadwInfo = NULL; } if (fSuccess) { DWORD dwOpenFlags = 0; if (lpWav->dwFlags & WAV_READ) dwOpenFlags |= MMIO_READ; if (lpWav->dwFlags & WAV_WRITE) dwOpenFlags |= MMIO_WRITE; if (lpWav->dwFlags & WAV_READWRITE) dwOpenFlags |= MMIO_READWRITE; if (lpWav->dwFlags & WAV_CREATE) dwOpenFlags |= MMIO_CREATE; if (lpWav->dwFlags & WAV_DENYNONE) dwOpenFlags |= MMIO_DENYNONE; if (lpWav->dwFlags & WAV_DENYREAD) dwOpenFlags |= MMIO_DENYREAD; if (lpWav->dwFlags & WAV_DENYWRITE) dwOpenFlags |= MMIO_DENYWRITE; if (lpWav->dwFlags & WAV_EXCLUSIVE) dwOpenFlags |= MMIO_EXCLUSIVE; // open/create disk wav file with specified i/o procedure // if (lpIOProc != NULL) { MMIOINFO mmioinfo; MemSet(&mmioinfo, 0, sizeof(mmioinfo)); mmioinfo.pIOProc = lpIOProc; // pass data to the i/o procedure // if (lpadwInfo != NULL) MemCpy(mmioinfo.adwInfo, lpadwInfo, sizeof(mmioinfo.adwInfo)); if ((lpWav->hmmio = mmioOpen((LPTSTR) lpszFileName, &mmioinfo, dwOpenFlags)) == NULL) { fSuccess = TraceFALSE(NULL); } } // open/create a memory wav file if WAV_MEMORY specified // else if (lpWav->dwFlags & WAV_MEMORY) { MMIOINFO mmioinfo; MemSet(&mmioinfo, 0, sizeof(mmioinfo)); mmioinfo.fccIOProc = FOURCC_MEM; mmioinfo.pchBuffer = (HPSTR) lpszFileName; if (lpszFileName == NULL) { // expandable memory file // mmioinfo.cchBuffer = 0; mmioinfo.adwInfo[0] = (DWORD) (16 * 1024); } else { // expandable memory file // mmioinfo.cchBuffer = (long) MemSize(NULL, (LPVOID) lpszFileName); mmioinfo.adwInfo[0] = (DWORD) 16384; } if ((lpWav->hmmio = mmioOpen(NULL, &mmioinfo, dwOpenFlags)) == NULL) { fSuccess = TraceFALSE(NULL); } } // otherwise open/create disk wav file // else { if ((lpWav->lpszFileName = StrDup(lpszFileName)) == NULL) fSuccess = TraceFALSE(NULL); else if ((lpWav->hmmio = mmioOpen((LPTSTR) lpszFileName, NULL, dwOpenFlags)) == NULL) { fSuccess = TraceFALSE(NULL); } } } // handle reading of RIFF file chunks if necessary // if (fSuccess && !(lpWav->dwFlags & WAV_CREATE) && !(lpWav->dwFlags & WAV_NORIFF)) { MMCKINFO ck; // search for RIFF chunk with form type WAV // if ((lpWav->nLastError = mmioDescend(lpWav->hmmio, &lpWav->ckRIFF, NULL, MMIO_FINDRIFF)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("mmioDescend failed (%u)\n"), (unsigned) lpWav->nLastError); } // search for "fmt " subchunk // ck.ckid = mmioFOURCC('f', 'm', 't', ' '); if (fSuccess && (lpWav->nLastError = mmioDescend(lpWav->hmmio, &ck, &lpWav->ckRIFF, MMIO_FINDCHUNK)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("mmioDescend failed (%u)\n"), (unsigned) lpWav->nLastError); } // save position of "fmt " chunk data so we can seek there later // else if (fSuccess && (lpWav->lPosFmt = mmioSeek(lpWav->hmmio, 0, SEEK_CUR)) == -1) { fSuccess = TraceFALSE(NULL); } // check for file corruption // if (fSuccess && (ck.dwDataOffset + ck.cksize) > (lpWav->ckRIFF.dwDataOffset + lpWav->ckRIFF.cksize)) { fSuccess = TraceFALSE(NULL); } if (fSuccess) { LPWAVEFORMATEX lpwfx = NULL; DWORD cksize; // save fmt chunk info // lpWav->ckfmt = ck; // fmt chunk must be no smaller than WAVEFORMAT struct // if ((cksize = max(ck.cksize, sizeof(WAVEFORMAT))) < sizeof(WAVEFORMAT)) fSuccess = TraceFALSE(NULL); // allocate space for WAVEFORMATEX struct // else if ((lpwfx = (LPWAVEFORMATEX) MemAlloc(NULL, max(sizeof(WAVEFORMATEX), cksize), 0)) == NULL) fSuccess = TraceFALSE(NULL); // read the fmt chunk // else if (mmioRead(lpWav->hmmio, (HPSTR) lpwfx, (LONG) cksize) != (LONG) cksize) fSuccess = TraceFALSE(NULL); // seek to beginning of next chunk if necessary // else if (ck.cksize > cksize && mmioSeek(lpWav->hmmio, ck.cksize - cksize, SEEK_CUR) == -1) fSuccess = TraceFALSE(NULL); // calculate bits per sample if necessary // else if (lpwfx->wFormatTag == WAVE_FORMAT_PCM && lpwfx->wBitsPerSample == 0) { // NOTE: this only works for PCM data with // sample size that is a multiple of 8 bits // lpwfx->wBitsPerSample = (lpwfx->nBlockAlign * 8) / lpwfx->nChannels; } // save format for later // if (fSuccess && WavSetFormat(WavGetHandle(lpWav), lpwfx, WAV_FORMATALL) != 0) fSuccess = TraceFALSE(NULL); // clean up // if (lpwfx != NULL && (lpwfx = MemFree(NULL, lpwfx)) != NULL) fSuccess = TraceFALSE(NULL); } // search for "data" subchunk // ck.ckid = mmioFOURCC('d', 'a', 't', 'a'); if (fSuccess && (lpWav->nLastError = mmioDescend(lpWav->hmmio, &ck, &lpWav->ckRIFF, MMIO_FINDCHUNK)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("mmioDescend failed (%u)\n"), (unsigned) lpWav->nLastError); } // check for file corruption // if (fSuccess && (ck.dwDataOffset + ck.cksize) > (lpWav->ckRIFF.dwDataOffset + lpWav->ckRIFF.cksize)) { fSuccess = TraceFALSE(NULL); } if (fSuccess) { // save data chunk info // lpWav->ckdata = ck; // save data size and offset for later // lpWav->cbData = (long) ck.cksize; lpWav->lDataOffset = (long) ck.dwDataOffset; } } // handle creation of RIFF file chunks if necessary // else if (fSuccess && (lpWav->dwFlags & WAV_CREATE) && !(lpWav->dwFlags & WAV_NORIFF)) { lpWav->ckRIFF.ckid = mmioFOURCC('R', 'I', 'F', 'F'); lpWav->ckRIFF.cksize = 0; // unknown lpWav->ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E'); lpWav->ckfmt.ckid = mmioFOURCC('f', 'm', 't', ' '); lpWav->ckfmt.cksize = WavFormatGetSize(lpWav->lpwfx[FORMATFILE]); lpWav->ckdata.ckid = mmioFOURCC('d', 'a', 't', 'a'); lpWav->ckdata.cksize = 0; // unknown // create RIFF chunk with form type WAV // if ((lpWav->nLastError = mmioCreateChunk(lpWav->hmmio, &lpWav->ckRIFF, MMIO_CREATERIFF)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("mmioCreateChunk failed (%u)\n"), (unsigned) lpWav->nLastError); } // create 'fmt ' chunk // else if ((lpWav->nLastError = mmioCreateChunk(lpWav->hmmio, &lpWav->ckfmt, 0)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("mmioCreateChunk failed (%u)\n"), (unsigned) lpWav->nLastError); } // save position of "fmt " chunk data so we can seek there later // else if ((lpWav->lPosFmt = mmioSeek(lpWav->hmmio, 0, SEEK_CUR)) == -1) { fSuccess = TraceFALSE(NULL); } // write 'fmt ' chunk data // else if (mmioWrite(lpWav->hmmio, (HPSTR) lpWav->lpwfx[FORMATFILE], WavFormatGetSize(lpWav->lpwfx[FORMATFILE])) != WavFormatGetSize(lpWav->lpwfx[FORMATFILE])) { fSuccess = TraceFALSE(NULL); } // ascend out of 'fmt ' chunk // else if ((lpWav->nLastError = mmioAscend(lpWav->hmmio, &lpWav->ckfmt, 0)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("mmioAscend failed (%u)\n"), (unsigned) lpWav->nLastError); } // create the 'data' chunk which holds the waveform samples // else if ((lpWav->nLastError = mmioCreateChunk(lpWav->hmmio, &lpWav->ckdata, 0)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("mmioCreateChunk failed (%u)\n"), (unsigned) lpWav->nLastError); } // calculate beginning offset of data chunk // else if ((lpWav->lDataOffset = mmioSeek(lpWav->hmmio, 0, SEEK_CUR)) == -1) fSuccess = TraceFALSE(NULL); } // calculate size of data chunk (file size) for non-RIFF files // else if (fSuccess && !(lpWav->dwFlags & WAV_CREATE) && (lpWav->dwFlags & WAV_NORIFF)) { // RFileIOProc already knows the file size // if (lpWav->lpIOProc != NULL && (lpWav->dwFlags & WAV_TELRFILE)) { long lSize; // retrieve size of remote file from i/o procedure // if ((lSize = (long) WavSendMessage(WavGetHandle(lpWav), MMIOM_GETINFO, 1, 0)) == (long) -1) fSuccess = TraceFALSE(NULL); else lpWav->cbData = (long) lSize; } else { LONG lPosCurr; LONG lPosEnd; // save current position // if ((lPosCurr = mmioSeek(lpWav->hmmio, 0, SEEK_CUR)) == -1) fSuccess = TraceFALSE(NULL); // seek to end of file // else if ((lPosEnd = mmioSeek(lpWav->hmmio, 0, SEEK_END)) == -1) fSuccess = TraceFALSE(NULL); // restore current position // else if (mmioSeek(lpWav->hmmio, lPosCurr, SEEK_SET) == -1) fSuccess = TraceFALSE(NULL); else lpWav->cbData = (long) lPosEnd; // + 1; } } if (fSuccess) { TracePrintf_4(NULL, 6, TEXT("After WavOpen: lpWav->lDataOffset=%ld, lpWav->lDataPos=%ld, lpWav->cbData=%ld, lpWav->msPositionStop=%ld\n"), (long) lpWav->lDataOffset, (long) lpWav->lDataPos, (long) lpWav->cbData, (long) lpWav->msPositionStop); } if (!fSuccess) { WavClose(WavGetHandle(lpWav)); lpWav = NULL; } return fSuccess ? WavGetHandle(lpWav) : NULL; } // WavClose - close wav file // (i) handle returned from WavOpen // return 0 if success // int WINAPI WavClose(HWAV hWav) { BOOL fSuccess = TRUE; LPWAV lpWav; #ifdef _WIN32 long lPosTruncate = -1; #endif if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // stop playback or record if necessary // else if (WavStop(hWav) != 0) fSuccess = TraceFALSE(NULL); // update the RIFF header chunks if dirty flag set // else if (lpWav->hmmio != NULL && !(lpWav->dwFlags & WAV_NORIFF) && ((lpWav->ckdata.dwFlags & MMIO_DIRTY) || (lpWav->ckRIFF.dwFlags & MMIO_DIRTY))) { #if 0 // seek to end of file // if (mmioSeek(lpWav->hmmio, 0, SEEK_END) == -1) { fSuccess = TraceFALSE(NULL); } #else // seek to end of the data // if (mmioSeek(lpWav->hmmio, lpWav->lDataOffset + lpWav->cbData, SEEK_SET) == -1) { fSuccess = TraceFALSE(NULL); } #endif // ascend out of the 'data' chunk; chunk size will be written // else if ((lpWav->nLastError = mmioAscend(lpWav->hmmio, &lpWav->ckdata, 0)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("mmioAscend failed (%u)\n"), (unsigned) lpWav->nLastError); } // ascend out of the 'RIFF' chunk; chunk size will be written // else if ((lpWav->nLastError = mmioAscend(lpWav->hmmio, &lpWav->ckRIFF, 0)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("mmioAscend failed (%u)\n"), (unsigned) lpWav->nLastError); } #if 0 // seek to beginning of file // else if (mmioSeek(lpWav->hmmio, 0, SEEK_SET) == -1) { fSuccess = TraceFALSE(NULL); } // search for RIFF chunk with form type WAV // else if ((lpWav->nLastError = mmioDescend(lpWav->hmmio, &lpWav->ckRIFF, NULL, MMIO_FINDRIFF)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("mmioDescend failed (%u)\n"), (unsigned) lpWav->nLastError); } // search for 'fmt ' chunk // else if ((lpWav->nLastError = mmioDescend(lpWav->hmmio, &lpWav->ckfmt, &lpWav->ckRIFF, MMIO_FINDCHUNK)) != 0) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("mmioDescend failed (%u)\n"), (unsigned) lpWav->nLastError); } #else // seek to beginning of "fmt " chunk data // else if (mmioSeek(lpWav->hmmio, lpWav->lPosFmt, SEEK_SET) == -1) { fSuccess = TraceFALSE(NULL); } #endif // write 'fmt ' chunk data // $FIXUP - what happens if current file format struct size // is larger than the original format? // else if (mmioWrite(lpWav->hmmio, (HPSTR) lpWav->lpwfx[FORMATFILE], WavFormatGetSize(lpWav->lpwfx[FORMATFILE])) != WavFormatGetSize(lpWav->lpwfx[FORMATFILE])) { fSuccess = TraceFALSE(NULL); } #ifdef _WIN32 // see if we need to truncate file // else if (lpWav->lpIOProc == NULL && !(lpWav->dwFlags & WAV_MEMORY) && !(lpWav->dwFlags & WAV_RESOURCE)) { if (mmioSeek(lpWav->hmmio, 0, SEEK_END) > lpWav->lDataOffset + lpWav->cbData) { lPosTruncate = lpWav->lDataOffset + lpWav->cbData; } } #endif } if (fSuccess) { // close the file // if (lpWav->hmmio != NULL && mmioClose(lpWav->hmmio, 0) != 0) fSuccess = TraceFALSE(NULL); else lpWav->hmmio = NULL; // close acm engine // if (lpWav->hAcm != NULL && AcmTerm(lpWav->hAcm) != 0) fSuccess = TraceFALSE(NULL); else lpWav->hAcm = NULL; // free wave resource // if (lpWav->hResource != NULL) { UnlockResource(lpWav->hResource); if (!FreeResource(lpWav->hResource)) fSuccess = TraceFALSE(NULL); else lpWav->hResource = NULL; } #ifdef _WIN32 // truncate file if necessary // if (lPosTruncate != -1 && lpWav->lpszFileName != NULL) { HANDLE hFile = INVALID_HANDLE_VALUE; // open file // if ((hFile = CreateFile(lpWav->lpszFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE) { fSuccess = TraceFALSE(NULL); } // seek to truncate position // else if (SetFilePointer(hFile, lPosTruncate, NULL, (DWORD) FILE_BEGIN) == 0xFFFFFFFF) { fSuccess = TraceFALSE(NULL); } // truncate file // else if (!SetEndOfFile(hFile)) fSuccess = TraceFALSE(NULL); // close file // if (hFile != INVALID_HANDLE_VALUE && !CloseHandle(hFile)) fSuccess = TraceFALSE(NULL); } #endif // free formats // if (1) { int iType; for (iType = FORMATFILE; fSuccess && iType <= FORMATRECORD; ++iType) { if (lpWav->lpwfx[iType] != NULL && WavFormatFree(lpWav->lpwfx[iType]) != 0) fSuccess = TraceFALSE(NULL); } } // free file name string // if (lpWav->lpszFileName != NULL) { StrDupFree(lpWav->lpszFileName); lpWav->lpszFileName = NULL; } if ((lpWav = MemFree(NULL, lpWav)) != NULL) fSuccess = TraceFALSE(NULL); } return fSuccess ? 0 : -1; } // WavPlayEx - play data from wav file // (i) handle returned from WavOpen // (i) wav output device id // -1 use any suitable output device // (i) function to call when play is stopped // NULL do not notify // (i) param to pass to lpfnPlayStopped // (i) reserved; must be zero // (i) control flags // WAV_PLAYASYNC return when playback starts (default) // WAV_PLAYSYNC return after playback completes // WAV_NOSTOP if device already playing, don't stop it // WAV_AUTOSTOP stop playback when eof reached (default) // WAV_NOAUTOSTOP continue playback until WavStop called // WAV_AUTOCLOSE close wav file after playback stops // WAV_OPENRETRY if output device busy, retry for up to 2 sec // return 0 if success // // NOTE: data from the wav file is sent to the output device in chunks. // Chunks are submitted to an output device queue, so that when one // chunk is finished playing, another is ready to start playing. By // default, each chunk is large enough to hold approximately 666 ms // of sound, and 3 chunks are maintained in the output device queue. // WavSetChunks() can be used to override the defaults. // // NOTE: if WAV_NOSTOP is specified in , and the device specified // by is already in use, this function returns without playing. // Unless this flag is specified, the specified device will be stopped // so that the new sound can be played. // // NOTE: if WAV_AUTOSTOP is specified in , WavStop() will be // called automatically when end of file is reached. This is the // default behavior, but can be overridden by using the WAV_NOAUTOSTOP // flag. WAV_NOAUTOSTOP is useful if you are playing a file that // is growing dynamically as another program writes to it. If this is // the case, also use the WAV_DENYNONE flag when calling WavOpen(). // // NOTE: if WAV_AUTOCLOSE is specified in , WavClose() will // be called automatically when playback completes. This will happen // when WavStop() is called explicitly, or when WavPlay() reaches end // of file and WAV_NOAUTOSTOP was not specified. WAV_AUTOCLOSE is useful // when used with WAV_PLAYASYNC, since cleanup occurs automatically. // The handle is thereafter invalid, and should not be used again. // int WINAPI WavPlay(HWAV hWav, int idDev, DWORD dwFlags) { return WavPlayEx(hWav, idDev, NULL, NULL, 0, dwFlags); } int DLLEXPORT WINAPI WavPlayEx(HWAV hWav, int idDev, PLAYSTOPPEDPROC lpfnPlayStopped, HANDLE hUserPlayStopped, DWORD dwReserved, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAV lpWav; int i; LPWAVEFORMATEX lpwfxWavOutOpen = NULL; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // make sure output device is not already open for this file // else if (lpWav->hWavOut != NULL) fSuccess = TraceFALSE(NULL); #ifdef MULTITHREAD // we need to know when we can exit // else if ((lpWav->dwFlags & WAV_MULTITHREAD) && (dwFlags & WAV_PLAYSYNC) && (lpWav->hEventStopped = CreateEvent( NULL, FALSE, FALSE, NULL)) == NULL) { fSuccess = TraceFALSE(NULL); } #endif // make sure output device is not playing // else if (WavStopOutputDevice(idDev, dwFlags) != 0) fSuccess = TraceFALSE(NULL); // set new playback format if device cannot handle the current format // else if (!WavOutSupportsFormat(NULL, idDev, lpWav->lpwfx[FORMATPLAY])) { LPWAVEFORMATEX lpwfxPlay = NULL; if ((lpwfxPlay = WavOutFormatSuggest(NULL, idDev, lpWav->lpwfx[FORMATPLAY], (lpWav->dwFlags & WAV_NOACM) ? WAVOUT_NOACM : 0)) != NULL && WavSetFormat(hWav, lpwfxPlay, WAV_FORMATPLAY) != 0) { fSuccess = TraceFALSE(NULL); } if (lpwfxPlay != NULL && WavFormatFree(lpwfxPlay) != 0) fSuccess = TraceFALSE(NULL); else lpwfxPlay = NULL; } if (!fSuccess) ; // create the notification callback window // else if (WavNotifyCreate(lpWav) != 0) fSuccess = TraceFALSE(NULL); // non-standard playback speed must be handled here // else if (lpWav->nSpeedLevel != 100) { #ifdef AVTSM // use time scale modification engine // if (!(lpWav->dwFlagsSpeed & WAVSPEED_NOTSM)) { long sizBufPlay; // calculate the size of output chunk // if ((sizBufPlay = WavCalcChunkSize(lpWav->lpwfx[FORMATPLAY], lpWav->msPlayChunkSize, TRUE)) <= 0) { fSuccess = TraceFALSE(NULL); } // initialize time scale modification engine // else if ((lpWav->hTsm = TsmInit(TSM_VERSION, lpWav->hInst, lpWav->lpwfx[FORMATPLAY], 2, sizBufPlay * TSM_OUTBUF_SIZE_FACT, 0)) == NULL) { fSuccess = TraceFALSE(NULL); } // set the speed // else if (TsmSetSpeed(lpWav->hTsm, lpWav->nSpeedLevel, 0) != 0) fSuccess = TraceFALSE(NULL); } else #endif // device supports playback rate directly // if (!(lpWav->dwFlagsSpeed & WAVSPEED_NOPLAYBACKRATE)) { // we must wait until device has been opened // ; } // device supports adjusted format // else if (!(lpWav->dwFlagsSpeed & WAVSPEED_NOFORMATADJUST)) { // device supports adjusted format with acm // if (!(lpWav->dwFlagsSpeed & WAVSPEED_NOACM)) { #if 0 LPWAVEFORMATEX lpwfxPlay = NULL; if ((lpwfxPlay = WavFormatDup(lpWav->lpwfx[FORMATPLAY])) == NULL) fSuccess = TraceFALSE(NULL); // we must double sample rate so that adjusted format works // else if (lpWav->nSpeedLevel < 100 && WavFormatSpeedAdjust(lpwfxPlay, 200, 0) != 0) { fSuccess = TraceFALSE(NULL); } // we must halve sample rate so that adjusted format works // else if (lpWav->nSpeedLevel > 100 && WavFormatSpeedAdjust(lpwfxPlay, 50, 0) != 0) { fSuccess = TraceFALSE(NULL); } else if (WavSetFormat(hWav, lpwfxPlay, WAV_FORMATPLAY) != 0) fSuccess = TraceFALSE(NULL); if (lpwfxPlay != NULL && WavFormatFree(lpwfxPlay) != 0) fSuccess = TraceFALSE(NULL); else lpwfxPlay = NULL; #endif } if (fSuccess) { if ((lpwfxWavOutOpen = WavFormatDup(lpWav->lpwfx[FORMATPLAY])) == NULL) fSuccess = TraceFALSE(NULL); // adjust output device format to reflect current speed // else if (WavFormatSpeedAdjust(lpwfxWavOutOpen, lpWav->nSpeedLevel, 0) != 0) fSuccess = TraceFALSE(NULL); } } } if (!fSuccess) ; // open output device // else if ((lpWav->hWavOut = WavOutOpen(WAVOUT_VERSION, lpWav->hInst, idDev, lpwfxWavOutOpen == NULL ? lpWav->lpwfx[FORMATPLAY] : lpwfxWavOutOpen, #ifdef MULTITHREAD lpWav->dwFlags & WAV_MULTITHREAD ? (HWND)(DWORD_PTR)lpWav->dwThreadId : #endif lpWav->hwndNotify, 0, 0, #ifdef TELOUT ((lpWav->dwFlags & WAV_TELRFILE) ? WAVOUT_TELRFILE : 0) | #endif #ifdef MULTITHREAD ((lpWav->dwFlags & WAV_MULTITHREAD) ? WAVOUT_MULTITHREAD : 0) | #endif ((dwFlags & WAV_OPENRETRY) ? WAVOUT_OPENRETRY : 0) | ((lpWav->dwFlags & WAV_NOACM) ? WAVOUT_NOACM : 0))) == NULL) fSuccess = TraceFALSE(NULL); // save PlayStopped params for later // else if (lpWav->lpfnPlayStopped = lpfnPlayStopped, FALSE) ; else if (lpWav->hUserPlayStopped = hUserPlayStopped, FALSE) ; // set the device volume if necessary // else if (lpWav->nVolumeLevel != 50 && WavOutSetVolume(lpWav->hWavOut, -1, lpWav->nVolumeLevel) != 0) fSuccess = TraceFALSE(NULL); // set the device playback rate if necessary // else if (lpWav->nSpeedLevel != 100 && !(lpWav->dwFlagsSpeed & WAVSPEED_NOPLAYBACKRATE) && WavOutSetSpeed(lpWav->hWavOut, lpWav->nSpeedLevel) != 0) fSuccess = TraceFALSE(NULL); // setup acm conversion if play format different than file format // else if (WavFormatCmp(lpWav->lpwfx[FORMATFILE], lpWav->lpwfx[FORMATPLAY]) != 0 && AcmConvertInit(lpWav->hAcm, lpWav->lpwfx[FORMATFILE], lpWav->lpwfx[FORMATPLAY], NULL, 0) != 0) { fSuccess = TraceFALSE(NULL); } #if 0 // pause output device before sending chunks to play // else if (WavOutPause(lpWav->hWavOut) != 0) fSuccess = TraceFALSE(NULL); #endif // associate wav handle with device id // if (fSuccess) { int idDev; if ((idDev = WavOutGetId(lpWav->hWavOut)) < HWAVOUT_MIN || idDev >= HWAVOUT_MAX) fSuccess = TraceFALSE(NULL); else ahWavOutCurr[idDev + HWAVOUT_OFFSET] = WavGetHandle(lpWav); } // remember the flags used in case we need them later // if (fSuccess) lpWav->dwFlagsPlay = dwFlags; // set the WAVSTATE_AUTOSTOP flag for later if necessary // if (fSuccess && !(dwFlags & WAV_NOAUTOSTOP)) lpWav->dwState |= WAVSTATE_AUTOSTOP; // set the WAVSTATE_AUTOCLOSE flag for later if necessary // if (fSuccess && (dwFlags & WAV_AUTOCLOSE)) lpWav->dwState |= WAVSTATE_AUTOCLOSE; // load output device queue with chunks to play // for (i = 0; fSuccess && i < lpWav->cPlayChunks; ++i) { if (WavPlayNextChunk(hWav) != 0) fSuccess = TraceFALSE(NULL); } #if 0 // start playback // if (fSuccess && WavOutResume(lpWav->hWavOut) != 0) fSuccess = TraceFALSE(NULL); #endif // loop until playback complete if WAV_PLAYSYNC flag specified // if (fSuccess && (dwFlags & WAV_PLAYSYNC)) { #ifdef MULTITHREAD // handle WAV_MULTITHREAD flag // if (fSuccess && (lpWav->dwFlags & WAV_MULTITHREAD)) { // wait for the play to end // if (WaitForSingleObject(lpWav->hEventStopped, INFINITE) != WAIT_OBJECT_0) { fSuccess = TraceFALSE(NULL); } // clean up // else if (lpWav->hEventStopped != NULL) { if (!CloseHandle(lpWav->hEventStopped)) fSuccess = TraceFALSE(NULL); else lpWav->hEventStopped = NULL; } } else #endif // check for valid pointer because WAV_AUTOCLOSE flag // could cause hWav to be invalidated during this loop // while (WavGetPtr(hWav) != NULL && WavGetState(hWav) != WAV_STOPPED) { MSG msg; if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else WaitMessage(); } } // close output device only if error or playback complete // if (!fSuccess || (dwFlags & WAV_PLAYSYNC)) { if (WavGetPtr(hWav) != NULL && WavStopPlay(hWav) != 0) fSuccess = TraceFALSE(NULL); } // clean up // if (lpwfxWavOutOpen != NULL && WavFormatFree(lpwfxWavOutOpen) != 0) fSuccess = TraceFALSE(NULL); else lpwfxWavOutOpen = NULL; return fSuccess ? 0 : -1; } // WavRecordEx - record data to wav file // (i) handle returned from WavOpen // (i) wav input device id // -1 use any suitable input device // (i) function to call when record is stopped // NULL do not notify // (i) param to pass to lpfnRecordStopped // (i) stop recording if file reaches this size // 0 no maximum size // (i) control flags // WAV_RECORDASYNC return when recording starts (default) // WAV_RECORDSYNC return after recording completes // WAV_NOSTOP if device already recording, don't stop it // WAV_OPENRETRY if input device busy, retry for up to 2 sec // return 0 if success // // NOTE: data from the input device is written to the wav file in chunks. // Chunks are submitted to an input device queue, so that when one // chunk is finished recording, another is ready to start recording. // By default, each chunk is large enough to hold approximately 666 ms // of sound, and 3 chunks are maintained in the input device queue. // WavSetChunks() can be used to override the defaults. // // NOTE: if WAV_NOSTOP is specified in , and the device specified // by is already in use, this function returns without recording. // Unless this flag is specified, the specified device will be stopped // so that the new sound can be recorded. // int WINAPI WavRecord(HWAV hWav, int idDev, DWORD dwFlags) { return WavRecordEx(hWav, idDev, NULL, 0, 0, dwFlags); } int DLLEXPORT WINAPI WavRecordEx(HWAV hWav, int idDev, RECORDSTOPPEDPROC lpfnRecordStopped, DWORD dwUserRecordStopped, long msMaxSize, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAV lpWav; int i; LPWAVEFORMATEX lpwfxRecord = NULL; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // make sure input device is not already open for this file // else if (lpWav->hWavIn != NULL) fSuccess = TraceFALSE(NULL); #ifdef MULTITHREAD // we need to know when we can exit // else if ((lpWav->dwFlags & WAV_MULTITHREAD) && (dwFlags & WAV_RECORDSYNC) && (lpWav->hEventStopped = CreateEvent( NULL, FALSE, FALSE, NULL)) == NULL) { fSuccess = TraceFALSE(NULL); } #endif // make sure input device is not recording // else if (WavStopInputDevice(idDev, dwFlags) != 0) fSuccess = TraceFALSE(NULL); // set new recording format if device cannot handle the current format // else if (!WavInSupportsFormat(NULL, idDev, lpWav->lpwfx[FORMATRECORD]) && (lpwfxRecord = WavInFormatSuggest(NULL, idDev, lpWav->lpwfx[FORMATRECORD], (lpWav->dwFlags & WAV_NOACM) ? WAVIN_NOACM : 0)) != NULL && WavSetFormat(hWav, lpwfxRecord, WAV_FORMATRECORD) != 0) { fSuccess = TraceFALSE(NULL); } // create the notification callback window // else if (WavNotifyCreate(lpWav) != 0) fSuccess = TraceFALSE(NULL); // open input device // else if ((lpWav->hWavIn = WavInOpen(WAVIN_VERSION, lpWav->hInst, idDev, lpWav->lpwfx[FORMATRECORD], #ifdef MULTITHREAD lpWav->dwFlags & WAV_MULTITHREAD ? (HWND)(DWORD_PTR)lpWav->dwThreadId : #endif lpWav->hwndNotify, 0, 0, #ifdef TELIN ((lpWav->dwFlags & WAV_TELRFILE) ? WAVIN_TELRFILE : 0) | #endif #ifdef MULTITHREAD ((lpWav->dwFlags & WAV_MULTITHREAD) ? WAVOUT_MULTITHREAD : 0) | #endif ((dwFlags & WAV_OPENRETRY) ? WAVIN_OPENRETRY : 0) | ((lpWav->dwFlags & WAV_NOACM) ? WAVIN_NOACM : 0))) == NULL) fSuccess = TraceFALSE(NULL); // save params for later // else if (lpWav->lpfnRecordStopped = lpfnRecordStopped, FALSE) ; else if (lpWav->dwUserRecordStopped = dwUserRecordStopped, FALSE) ; else if (lpWav->msMaxSize = msMaxSize, FALSE) ; // setup acm conversion if file format different than record format // else if (WavFormatCmp(lpWav->lpwfx[FORMATRECORD], lpWav->lpwfx[FORMATFILE]) != 0 && AcmConvertInit(lpWav->hAcm, lpWav->lpwfx[FORMATRECORD], lpWav->lpwfx[FORMATFILE], NULL, 0) != 0) { fSuccess = TraceFALSE(NULL); } // associate wav handle with device id // if (fSuccess) { int idDev; if ((idDev = WavInGetId(lpWav->hWavIn)) < HWAVIN_MIN || idDev >= HWAVIN_MAX) TraceFALSE(NULL); else ahWavInCurr[idDev + HWAVIN_OFFSET] = WavGetHandle(lpWav); } // remember the flags used in case we need them later // if (fSuccess) lpWav->dwFlagsRecord = dwFlags; // load input device queue with chunks to play // for (i = 0; fSuccess && i < lpWav->cRecordChunks; ++i) { if (WavRecordNextChunk(hWav) != 0) fSuccess = TraceFALSE(NULL); } // set the WAVSTATE_AUTOSTOP flag for later if necessary // if (fSuccess && (dwFlags & WAV_AUTOSTOP)) lpWav->dwState |= WAVSTATE_AUTOSTOP; // set the WAVSTATE_AUTOCLOSE flag for later if necessary // if (fSuccess && (dwFlags & WAV_AUTOCLOSE)) lpWav->dwState |= WAVSTATE_AUTOCLOSE; // loop until recording complete if WAV_RECORDSYNC flag specified // if (fSuccess && (dwFlags & WAV_RECORDSYNC)) { #ifdef MULTITHREAD // handle WAV_MULTITHREAD flag // if (fSuccess && (lpWav->dwFlags & WAV_MULTITHREAD)) { // wait for the record to end // if (WaitForSingleObject(lpWav->hEventStopped, INFINITE) != WAIT_OBJECT_0) { fSuccess = TraceFALSE(NULL); } // clean up // else if (lpWav->hEventStopped != NULL) { if (!CloseHandle(lpWav->hEventStopped)) fSuccess = TraceFALSE(NULL); else lpWav->hEventStopped = NULL; } } else #endif while (WavGetState(hWav) != WAV_STOPPED) { MSG msg; if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else WaitMessage(); } } // close input device only if error or recording complete // if (!fSuccess || (dwFlags & WAV_RECORDSYNC)) { if (WavGetPtr(hWav) != NULL && WavStopRecord(hWav) != 0) fSuccess = TraceFALSE(NULL); } if (lpwfxRecord != NULL && WavFormatFree(lpwfxRecord) != 0) fSuccess = TraceFALSE(NULL); else lpwfxRecord = NULL; return fSuccess ? 0 : -1; } // WavStop - stop playing and/or recording // (i) handle returned from WavOpen // return 0 if success // int WINAPI WavStop(HWAV hWav) { BOOL fSuccess = TRUE; // stop playing // if (WavStopPlay(hWav) != 0) fSuccess = TraceFALSE(NULL); // stop recording // if (WavStopRecord(hWav) != 0) fSuccess = TraceFALSE(NULL); return fSuccess ? 0 : -1; } // WavRead - read data from wav file // (i) handle returned from WavOpen // (o) buffer to contain bytes read // (i) size of buffer in bytes // return bytes read (-1 if error) // // NOTE : Even if the read operation does not reach the end of file, // the number of bytes returned could be less than if data // decompression is performed by the wav file's I/O procedure. See the // parameter in WavOpen. It is safest to keep calling // WavRead() until 0 bytes are read. // long DLLEXPORT WINAPI WavRead(HWAV hWav, void _huge *hpBuf, long sizBuf) { BOOL fSuccess = TRUE; LPWAV lpWav; long lBytesRead; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else if (hpBuf == NULL) fSuccess = TraceFALSE(NULL); // make sure we don't read beyond the end of the data // NOTE: cbData might not be accurate if file is growing dynamically, // so it is ok to read beyond eof if sharing flags are set // else if (!(lpWav->dwFlags & WAV_DENYNONE) && !(lpWav->dwFlags & WAV_DENYREAD) && (sizBuf = min(sizBuf, lpWav->cbData - lpWav->lDataPos)) < 0) fSuccess = TraceFALSE(NULL); // do the read // else if ((lBytesRead = mmioRead(lpWav->hmmio, hpBuf, sizBuf)) < 0) fSuccess = TraceFALSE(NULL); else if (TracePrintf_1(NULL, 5, TEXT("WavRead (%ld)\n"), (long) lBytesRead), FALSE) fSuccess = TraceFALSE(NULL); // adjust current data position // (and total data bytes, if file has grown) // else if ((lpWav->lDataPos += lBytesRead) > lpWav->cbData) { if ((lpWav->dwFlags & WAV_DENYNONE) || (lpWav->dwFlags & WAV_DENYREAD)) lpWav->cbData = lpWav->lDataPos; else fSuccess = TraceFALSE(NULL); } // calculate new stop position if stopped // if (fSuccess && WavCalcPositionStop(hWav, lpWav->lDataPos) != 0) fSuccess = TraceFALSE(NULL); return fSuccess ? lBytesRead : -1; } // WavWrite - write data to wav file // (i) handle returned from WavOpen // (i) buffer containing bytes to write // (i) size of buffer in bytes // return bytes written (-1 if error) // // NOTE : Even if the write operation successfully completes, // the number of bytes returned could be less than if data // compression is performed by the wav file's I/O procedure. See the // parameter in WavOpen. It is safest to assume no error // in WavWrite() occurred if the return value is greater than 0. // long DLLEXPORT WINAPI WavWrite(HWAV hWav, void _huge *hpBuf, long sizBuf) { BOOL fSuccess = TRUE; LPWAV lpWav; long lBytesWritten; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // special case: truncate file at current position // else if (hpBuf == NULL && sizBuf == 0) { if (WavSetLength(hWav, WavGetPosition(hWav)) < 0) return -1; else return 0; } else if (hpBuf == NULL) fSuccess = TraceFALSE(NULL); // do the write // else if ((lBytesWritten = mmioWrite(lpWav->hmmio, hpBuf, sizBuf)) < 0) fSuccess = TraceFALSE(NULL); // set dirty flags // else if (lpWav->ckdata.dwFlags |= MMIO_DIRTY, lpWav->ckRIFF.dwFlags |= MMIO_DIRTY, FALSE) ; else if (TracePrintf_1(NULL, 5, TEXT("WavWrite (%ld)\n"), (long) lBytesWritten), FALSE) fSuccess = TraceFALSE(NULL); // adjust current data position // (and total data bytes, if file has grown) // else if ((lpWav->lDataPos += lBytesWritten) > lpWav->cbData) lpWav->cbData = lpWav->lDataPos; // calculate new stop position if stopped // if (fSuccess && WavCalcPositionStop(hWav, lpWav->lDataPos) != 0) fSuccess = TraceFALSE(NULL); return fSuccess ? lBytesWritten : -1; } // WavSeek - seek within wav file data // (i) handle returned from WavOpen // (i) bytes to move pointer // (i) position to move from // 0 move pointer relative to start of data chunk // 1 move pointer relative to current position // 2 move pointer relative to end of data chunk // return new file position (-1 if error) // long DLLEXPORT WINAPI WavSeek(HWAV hWav, long lOffset, int nOrigin) { BOOL fSuccess = TRUE; LPWAV lpWav; long lPos; BOOL fWavTell; BOOL fWavSeekTrace; // WavSeek(..., 0, 1) is same as WavTell(); i.e. no position change // fWavTell = (BOOL) (lOffset == 0L && nOrigin == 1); // traces only if position change with high trace level // fWavSeekTrace = (BOOL) (!fWavTell && TraceGetLevel(NULL) >= 6); if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // debug trace output before the seek // else if (fWavSeekTrace && WavSeekTraceBefore(lpWav, lOffset, nOrigin) != 0) fSuccess = TraceFALSE(NULL); // SEEK_SET: adjust offset relative to beginning of file // else if (nOrigin == 0 && (lOffset += lpWav->lDataOffset, FALSE)) fSuccess = TraceFALSE(NULL); // SEEK_CUR: adjust offset relative to beginning of file // else if (nOrigin == 1 && (lOffset += lpWav->lDataOffset + lpWav->lDataPos, FALSE)) fSuccess = TraceFALSE(NULL); // SEEK_END: adjust offset relative to beginning of file // else if (nOrigin == 2 && (lOffset += lpWav->lDataOffset + lpWav->cbData, FALSE)) fSuccess = TraceFALSE(NULL); // seek is always relative to the beginning of file // else if (nOrigin = 0, FALSE) ; // do the seek // else if ((lPos = mmioSeek(lpWav->hmmio, lOffset, nOrigin)) < 0) fSuccess = TraceFALSE(NULL); // adjust current data position // else if ((lpWav->lDataPos = lPos - lpWav->lDataOffset) < 0) fSuccess = TraceFALSE(NULL); // adjust total data bytes, if file has grown // else if (lpWav->lDataPos > lpWav->cbData) { if ((lpWav->dwFlags & WAV_DENYNONE) || (lpWav->dwFlags & WAV_DENYREAD)) lpWav->cbData = lpWav->lDataPos; else fSuccess = TraceFALSE(NULL); } // calculate new stop position if stopped // NOTE: we skip this if position unchanged // if (fSuccess && !fWavTell && WavCalcPositionStop(hWav, lpWav->lDataPos) != 0) fSuccess = TraceFALSE(NULL); // debug trace output after the seek // if (fSuccess && fWavSeekTrace && WavSeekTraceAfter(lpWav, lPos, lOffset, nOrigin) != 0) fSuccess = TraceFALSE(NULL); return fSuccess ? lpWav->lDataPos : -1; } // WavGetState - return current wav state // (i) handle returned from WavOpen // return WAV_STOPPED, WAV_PLAYING, WAV_RECORDING, or 0 if error // WORD DLLEXPORT WINAPI WavGetState(HWAV hWav) { BOOL fSuccess = TRUE; LPWAV lpWav; WORD wState = WAV_STOPPED; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else if (lpWav->hWavOut != NULL) { switch (WavOutGetState(lpWav->hWavOut)) { case WAVOUT_PLAYING: wState = WAV_PLAYING; break; case WAVOUT_STOPPING: wState = WAV_STOPPING; break; case WAVOUT_STOPPED: case WAVOUT_PAUSED: wState = WAV_STOPPED; break; case 0: default: fSuccess = TraceFALSE(NULL); break; } } else if (lpWav->hWavIn != NULL) { switch (WavInGetState(lpWav->hWavIn)) { case WAVIN_RECORDING: wState = WAV_RECORDING; break; case WAVIN_STOPPING: wState = WAV_STOPPING; break; case WAVIN_STOPPED: wState = WAV_STOPPED; break; case 0: default: fSuccess = TraceFALSE(NULL); break; } } // if we are in the middle of WavStopPlay() or WavStopRecord() // then set state to WAV_STOPPING, regardless of device state // if (fSuccess && ((lpWav->dwState & WAVSTATE_STOPPLAY) || (lpWav->dwState & WAVSTATE_STOPRECORD))) { wState = WAV_STOPPING; } return fSuccess ? wState : 0; } // WavGetLength - get current wav data length in milleseconds // (i) handle returned from WavOpen // return milleseconds if success, otherwise -1 // long DLLEXPORT WINAPI WavGetLength(HWAV hWav) { BOOL fSuccess = TRUE; LPWAV lpWav; long msLength; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else { msLength = WavFormatBytesToMilleseconds( lpWav->lpwfx[FORMATFILE], (DWORD) lpWav->cbData); } return fSuccess ? msLength : -1; } // WavSetLength - set current wav data length in milleseconds // (i) handle returned from WavOpen // (i) length in milleseconds // return new length in milleseconds if success, otherwise -1 // // NOTE: afterwards, the current wav data position is set to either // the previous wav data position or , whichever is smaller. // long DLLEXPORT WINAPI WavSetLength(HWAV hWav, long msLength) { BOOL fSuccess = TRUE; LPWAV lpWav; TracePrintf_1(NULL, 6, TEXT("WavSetLength(%ld)\n"), (long) msLength); if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // new length must be reasonable // else if (msLength < 0 || msLength > WavGetLength(hWav)) fSuccess = TraceFALSE(NULL); else { long lBlockAlign; // convert to byte offset in file // lpWav->cbData = WavFormatMillesecondsToBytes( lpWav->lpwfx[FORMATFILE], (DWORD) msLength); // $FIXUP - add parameter to // WavFormatMillesecondsToBytes() and WavFormatBytesToMilleseconds() // if ((lBlockAlign = (long) lpWav->lpwfx[FORMATFILE]->nBlockAlign) > 0) { // round down to nearest block boundary // lpWav->cbData = lBlockAlign * (lpWav->cbData / lBlockAlign); } // set dirty flags // lpWav->ckdata.dwFlags |= MMIO_DIRTY; lpWav->ckRIFF.dwFlags |= MMIO_DIRTY; // adjust current data position if necessary // if (lpWav->lDataPos > lpWav->cbData) { lpWav->lDataPos = lpWav->cbData; // calculate new stop position if stopped // if (fSuccess && WavCalcPositionStop(hWav, lpWav->lDataPos) != 0) fSuccess = TraceFALSE(NULL); } } return fSuccess ? WavGetLength(hWav) : -1; } // WavGetPosition - get current wav data position in milleseconds // (i) handle returned from WavOpen // return milleseconds if success, otherwise -1 // long DLLEXPORT WINAPI WavGetPosition(HWAV hWav) { BOOL fSuccess = TRUE; LPWAV lpWav; long msPosition; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else switch (WavGetState(hWav)) { case WAV_PLAYING: { long msPositionPlay = 0L; // get position relative to start of playback // if ((msPositionPlay = WavOutGetPosition(lpWav->hWavOut)) == -1) fSuccess = TraceFALSE(NULL); else { // if necessary, adjust position to compensate for non-standard speed // if (lpWav->nSpeedLevel != 100 && ( #ifdef AVTSM !(lpWav->dwFlagsSpeed & WAVSPEED_NOTSM) || #endif !(lpWav->dwFlagsSpeed & WAVSPEED_NOFORMATADJUST))) { msPositionPlay = msPositionPlay * lpWav->nSpeedLevel / 100; } // calc position relative to start of file // msPosition = lpWav->msPositionStop + msPositionPlay; } } break; case WAV_RECORDING: { long msPositionRecord = 0L; // get position relative to start of recording // if ((msPositionRecord = WavInGetPosition(lpWav->hWavIn)) == -1) fSuccess = TraceFALSE(NULL); else { // calc position relative to start of file // msPosition = lpWav->msPositionStop + msPositionRecord; } } break; default: { long cbPosition; // get current file position // if ((cbPosition = WavSeek(hWav, 0, 1)) == -1) fSuccess = TraceFALSE(NULL); // convert file position to milleseconds // else { msPosition = WavFormatBytesToMilleseconds( lpWav->lpwfx[FORMATFILE], (DWORD) cbPosition); } } break; } return fSuccess ? msPosition : -1; } // WavSetPosition - set current wav data position in milleseconds // (i) handle returned from WavOpen // (i) position in milleseconds // return new position in milleseconds if success, otherwise -1 // long DLLEXPORT WINAPI WavSetPosition(HWAV hWav, long msPosition) { BOOL fSuccess = TRUE; LPWAV lpWav; WORD wStatePrev; int idDevPrev; long cbPosition; long cbPositionNew; long msPositionNew; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else if (WavTempStop(hWav, &wStatePrev, &idDevPrev) != 0) fSuccess = TraceFALSE(NULL); else { long lBlockAlign; TracePrintf_1(NULL, 6, TEXT("WavSetPosition(%ld)\n"), (long) msPosition); // convert to byte offset in file // cbPosition = WavFormatMillesecondsToBytes( lpWav->lpwfx[FORMATFILE], (DWORD) msPosition); if ((lBlockAlign = (long) lpWav->lpwfx[FORMATFILE]->nBlockAlign) > 0) { // round down to nearest block boundary // cbPosition = lBlockAlign * (cbPosition / lBlockAlign); } // seek to new position // if ((cbPositionNew = WavSeek(hWav, cbPosition, 0)) == -1) fSuccess = TraceFALSE(NULL); // convert the new position to milleseconds // if (fSuccess) { msPositionNew = WavFormatBytesToMilleseconds( lpWav->lpwfx[FORMATFILE], (DWORD) cbPositionNew); } if (WavTempResume(hWav, wStatePrev, idDevPrev) != 0) fSuccess = TraceFALSE(NULL); } return fSuccess ? msPositionNew : -1; } // WavGetFormat - get wav format // (i) handle returned from WavOpen // (i) control flags // WAV_FORMATFILE get format of data in file // WAV_FORMATPLAY get format of output device // WAV_FORMATRECORD get format of input device // return pointer to specified format, NULL if error // // NOTE: the format structure returned is dynamically allocated. // Use WavFormatFree() to free the buffer. // LPWAVEFORMATEX DLLEXPORT WINAPI WavGetFormat(HWAV hWav, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAV lpWav; int iType = 0; LPWAVEFORMATEX lpwfx; if (dwFlags & WAV_FORMATFILE) iType = FORMATFILE; if (dwFlags & WAV_FORMATPLAY) iType = FORMATPLAY; if (dwFlags & WAV_FORMATRECORD) iType = FORMATRECORD; // // We need to take care if hWav is NULL // if( NULL != hWav ) { if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else if ((lpwfx = WavFormatDup(lpWav->lpwfx[iType])) == NULL) fSuccess = TraceFALSE(NULL); } else fSuccess = TraceFALSE(NULL); return fSuccess ? lpwfx : NULL; } // WavSetFormat - set wav format // (i) handle returned from WavOpen // (i) wav format // (i) control flags // WAV_FORMATFILE set format of data in file // WAV_FORMATPLAY set format of output device // WAV_FORMATRECORD set format of input device // WAV_FORMATALL set all formats // return 0 if success // int DLLEXPORT WINAPI WavSetFormat(HWAV hWav, LPWAVEFORMATEX lpwfx, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAV lpWav; WORD wStatePrev; int idDevPrev; if (hWav != NULL && (lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else if (!WavFormatIsValid(lpwfx) != 0) fSuccess = TraceFALSE(NULL); else if (WavTempStop(hWav, &wStatePrev, &idDevPrev) != 0) fSuccess = TraceFALSE(NULL); else { int iType; for (iType = FORMATFILE; fSuccess && iType <= FORMATRECORD; ++iType) { if (iType == FORMATFILE && !(dwFlags & WAV_FORMATFILE)) continue; if (iType == FORMATPLAY && !(dwFlags & WAV_FORMATPLAY)) continue; if (iType == FORMATRECORD && !(dwFlags & WAV_FORMATRECORD)) continue; // free previous format // if (lpWav->lpwfx[iType] != NULL && WavFormatFree(lpWav->lpwfx[iType]) != 0) fSuccess = TraceFALSE(NULL); // save new format // else if ((lpWav->lpwfx[iType] = WavFormatDup(lpwfx)) == NULL) fSuccess = TraceFALSE(NULL); // trace format text // else if (TraceGetLevel(NULL) >= 5) { TCHAR szText[512]; switch (iType) { case FORMATFILE: TraceOutput(NULL, 5, TEXT("FORMATFILE:\t")); break; case FORMATPLAY: TraceOutput(NULL, 5, TEXT("FORMATPLAY:\t")); break; case FORMATRECORD: TraceOutput(NULL, 5, TEXT("FORMATRECORD:\t")); break; default: break; } if (AcmFormatGetText(lpWav->hAcm, lpWav->lpwfx[iType], szText, SIZEOFARRAY(szText), 0) != 0) ; // fSuccess = TraceFALSE(NULL); else { TracePrintf_1(NULL, 5, TEXT("%s\n"), (LPTSTR) szText); #if 0 WavFormatDump(lpWav->lpwfx[iType]); #endif } } } if (WavTempResume(hWav, wStatePrev, idDevPrev) != 0) fSuccess = TraceFALSE(NULL); } return fSuccess ? 0 : -1; } // WavChooseFormat - choose and set audio format from dialog box // (i) handle returned from WavOpen // (i) owner of dialog box // NULL no owner // (i) title of the dialog box // NULL use default title ("Sound Selection") // (i) control flags // WAV_FORMATFILE set format of data in file // WAV_FORMATPLAY set format of output device // WAV_FORMATRECORD set format of input device // WAV_FORMATALL set all formats // return 0 if success // int DLLEXPORT WINAPI WavChooseFormat(HWAV hWav, HWND hwndOwner, LPCTSTR lpszTitle, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAV lpWav; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else { LPWAVEFORMATEX lpwfx = NULL; LPWAVEFORMATEX lpwfxNew = NULL; DWORD dwFlagsChoose = 0; // see which format we are choosing // if (dwFlags & WAV_FORMATFILE) lpwfx = lpWav->lpwfx[FORMATFILE]; else if (dwFlags & WAV_FORMATPLAY) lpwfx = lpWav->lpwfx[FORMATPLAY]; else if (dwFlags & WAV_FORMATRECORD) lpwfx = lpWav->lpwfx[FORMATRECORD]; #if 0 // restrict choices if necessary // if (dwFlags == WAV_FORMATPLAY) dwFlagsChoose |= ACM_FORMATPLAY; if (dwFlags == WAV_FORMATRECORD) dwFlagsChoose |= ACM_FORMATRECORD; #endif // get chosen format // if ((lpwfxNew = AcmFormatChoose(lpWav->hAcm, hwndOwner, lpszTitle, lpwfx, dwFlagsChoose)) == NULL) ; // no format chosen // set chosen format // else if (WavSetFormat(hWav, lpwfxNew, dwFlags) != 0) fSuccess = TraceFALSE(NULL); // free chosen format struct // if (lpwfxNew != NULL && WavFormatFree(lpwfxNew) != 0) fSuccess = TraceFALSE(NULL); } return fSuccess ? 0 : -1; } // WavGetVolume - get current volume level // (i) handle returned from WavOpen // (i) wav output device id // -1 use any suitable output device // (i) reserved; must be zero // return volume level (0 minimum through 100 maximum, -1 if error) // int DLLEXPORT WINAPI WavGetVolume(HWAV hWav, int idDev, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAV lpWav; int nLevel; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else nLevel = lpWav->nVolumeLevel; return fSuccess ? nLevel : -1; } // WavSetVolume - set current volume level // (i) handle returned from WavOpen // (i) wav output device id // -1 use any suitable output device // (i) volume level // 0 minimum volume // 100 maximum volume // (i) control flags // WAVVOLUME_MIXER set volume through mixer device // return 0 if success // int DLLEXPORT WINAPI WavSetVolume(HWAV hWav, int idDev, int nLevel, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAV lpWav; TracePrintf_4(NULL, 6, TEXT("WavSetVolume(hWav=%p, idDev=%d, nLevel=%d, dwFlags=%08X)\n"), hWav, idDev, nLevel, dwFlags); if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else if (nLevel == lpWav->nVolumeLevel) ; // nothing to be done else if (dwFlags & WAVVOLUME_MIXER) { HWAVMIXER hWavMixer = NULL; if ((hWavMixer = WavMixerInit(WAVMIXER_VERSION, lpWav->hInst, (LPARAM) idDev, 0, 0, WAVMIXER_WAVEOUT)) == NULL) fSuccess = TraceFALSE(NULL); else if (WavMixerSetVolume(hWavMixer, nLevel, 0) < 0) fSuccess = TraceFALSE(NULL); if (hWavMixer != NULL && WavMixerTerm(hWavMixer) != 0) fSuccess = TraceFALSE(NULL); } else if (!(dwFlags & WAVVOLUME_MIXER)) { if (!WavOutSupportsVolume(lpWav->hWavOut, idDev)) fSuccess = TraceFALSE(NULL); // set the device volume if we are currently playing // else if (WavGetState(hWav) == WAV_PLAYING && WavOutSetVolume(lpWav->hWavOut, idDev, nLevel) != 0) fSuccess = TraceFALSE(NULL); } if (fSuccess) lpWav->nVolumeLevel = nLevel; return fSuccess ? 0 : -1; } // WavSupportsVolume - check if audio can be played at specified volume // (i) handle returned from WavOpen // (i) wav output device id // -1 any suitable output device // (i) volume level // 0 minimum volume // 100 maximum volume // (i) control flags // WAVVOLUME_MIXER check volume support through mixer device // return TRUE if supported // BOOL DLLEXPORT WINAPI WavSupportsVolume(HWAV hWav, int idDev, int nLevel, DWORD dwFlags) { BOOL fSuccess = TRUE; BOOL fSupportsVolume = FALSE; LPWAV lpWav; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else if (dwFlags & WAVVOLUME_MIXER) { HWAVMIXER hWavMixer = NULL; if ((hWavMixer = WavMixerInit(WAVMIXER_VERSION, lpWav->hInst, (LPARAM) idDev, 0, 0, WAVMIXER_WAVEOUT)) == NULL) fSuccess = TraceFALSE(NULL); else if (WavMixerSupportsVolume(hWavMixer, 0)) fSupportsVolume = TRUE; if (hWavMixer != NULL && WavMixerTerm(hWavMixer) != 0) fSuccess = TraceFALSE(NULL); } else if (!(dwFlags & WAVVOLUME_MIXER)) { // see if the device driver supports volume directly // if (WavOutSupportsVolume(NULL, idDev)) fSupportsVolume = TRUE; } return fSuccess ? fSupportsVolume : FALSE; } // WavGetSpeed - get current speed level // (i) handle returned from WavOpen // (i) wav output device id // -1 use any suitable output device // (i) reserved; must be zero // return speed level (100 is normal, 50 is half, 200 is double, -1 if error) // int DLLEXPORT WINAPI WavGetSpeed(HWAV hWav, int idDev, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAV lpWav; int nLevel; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else nLevel = lpWav->nSpeedLevel; return fSuccess ? nLevel : -1; } // WavSetSpeed - set current speed level // (i) handle returned from WavOpen // (i) wav output device id // -1 use any suitable output device // (i) speed level // 50 half speed // 100 normal speed // 200 double speed, etc. // (i) control flags #ifdef AVTSM // WAVSPEED_NOTSM do not use time scale modification engine #endif // WAVSPEED_NOPLAYBACKRATE do not use device driver playback rate // WAVSPEED_NOFORMATADJUST do not use adjusted format to open device // WAVSPEED_NOACM do not use audio compression manager // return 0 if success // // NOTE: In order to accomodate the specified speed change, it is _possible_ // that this function will in turn call WavSetFormat(hWav, ..., WAV_FORMATPLAY) // to change the playback format of the specified file. You can prevent this // side-effect by specifying the WAVSPEED_NOACM flag, but this reduces the likelihood // that WavSetSpeed will succeed. // int DLLEXPORT WINAPI WavSetSpeed(HWAV hWav, int idDev, int nLevel, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAV lpWav; WORD wStatePrev; int idDevPrev; TracePrintf_4(NULL, 6, TEXT("WavSetSpeed(hWav=%p, idDev=%d, nLevel=%d, dwFlags=%08X)\n"), hWav, idDev, nLevel, dwFlags); if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else if (nLevel == lpWav->nSpeedLevel) ; // nothing to be done else if (WavTempStop(hWav, &wStatePrev, &idDevPrev) != 0) fSuccess = TraceFALSE(NULL); else { if (nLevel == 100) { // normal speed is a special case // lpWav->nSpeedLevel = 100; lpWav->dwFlagsSpeed = 0; } #ifdef AVTSM else if (!(dwFlags & WAVSPEED_NOTSM) && WavSupportsSpeed(hWav, idDev, nLevel, WAVSPEED_NOACM | WAVSPEED_NOFORMATADJUST | WAVSPEED_NOPLAYBACKRATE)) { // use time scale modification engine // lpWav->nSpeedLevel = nLevel; lpWav->dwFlagsSpeed = WAVSPEED_NOACM | WAVSPEED_NOFORMATADJUST | WAVSPEED_NOPLAYBACKRATE; } else if (!(dwFlags & WAVSPEED_NOTSM) && WavSupportsSpeed(hWav, idDev, nLevel, WAVSPEED_NOFORMATADJUST | WAVSPEED_NOPLAYBACKRATE)) { #if 1 WAVEFORMATEX wfxTsm; // try a format that the tsm engine will handle // if (WavSetFormat(hWav, WavFormatPcm( lpWav->lpwfx[FORMATPLAY]->nSamplesPerSec, 16, 1, &wfxTsm), WAV_FORMATPLAY) != 0) fSuccess = TraceFALSE(NULL); #endif // use time scale modification engine with adjusted format // lpWav->nSpeedLevel = nLevel; lpWav->dwFlagsSpeed = WAVSPEED_NOFORMATADJUST | WAVSPEED_NOPLAYBACKRATE; } #endif else if (!(dwFlags & WAVSPEED_NOPLAYBACKRATE) && WavSupportsSpeed(hWav, idDev, nLevel, WAVSPEED_NOACM | WAVSPEED_NOFORMATADJUST | WAVSPEED_NOTSM)) { // device supports playback rate directly // lpWav->nSpeedLevel = nLevel; lpWav->dwFlagsSpeed = WAVSPEED_NOACM | WAVSPEED_NOFORMATADJUST | WAVSPEED_NOTSM; } else if (!(dwFlags & WAVSPEED_NOFORMATADJUST) && WavSupportsSpeed(hWav, idDev, nLevel, WAVSPEED_NOACM | WAVSPEED_NOPLAYBACKRATE | WAVSPEED_NOTSM)) { // device supports adjusted format without acm // lpWav->nSpeedLevel = nLevel; lpWav->dwFlagsSpeed = WAVSPEED_NOACM | WAVSPEED_NOPLAYBACKRATE | WAVSPEED_NOTSM; } else if (!(dwFlags & WAVSPEED_NOFORMATADJUST) && !(dwFlags & WAVSPEED_NOACM) && WavSupportsSpeed(hWav, idDev, nLevel, WAVSPEED_NOPLAYBACKRATE | WAVSPEED_NOTSM)) { #if 1 LPWAVEFORMATEX lpwfxPlay = NULL; if ((lpwfxPlay = WavFormatDup(lpWav->lpwfx[FORMATPLAY])) == NULL) fSuccess = TraceFALSE(NULL); // we must double sample rate so that adjusted format works // else if (nLevel < 100 && WavFormatSpeedAdjust(lpwfxPlay, 200, 0) != 0) { fSuccess = TraceFALSE(NULL); } // we must halve sample rate so that adjusted format works // else if (nLevel > 100 && WavFormatSpeedAdjust(lpwfxPlay, 50, 0) != 0) { fSuccess = TraceFALSE(NULL); } else if (WavSetFormat(hWav, lpwfxPlay, WAV_FORMATPLAY) != 0) fSuccess = TraceFALSE(NULL); if (lpwfxPlay != NULL && WavFormatFree(lpwfxPlay) != 0) fSuccess = TraceFALSE(NULL); else lpwfxPlay = NULL; #endif // device supports adjusted format with acm // lpWav->nSpeedLevel = nLevel; lpWav->dwFlagsSpeed = WAVSPEED_NOPLAYBACKRATE | WAVSPEED_NOTSM; } else fSuccess = TraceFALSE(NULL); if (WavTempResume(hWav, wStatePrev, idDevPrev) != 0) fSuccess = TraceFALSE(NULL); } return fSuccess ? 0 : -1; } // WavSupportsSpeed - check if audio can be played at specified speed // (i) handle returned from WavOpen // (i) wav output device id // -1 any suitable output device // (i) speed level // 50 half speed // 100 normal speed // 200 double speed, etc. // (i) control flags #ifdef AVTSM // WAVSPEED_NOTSM do not use time scale modification engine #endif // WAVSPEED_NOPLAYBACKRATE do not use device driver playback rate // WAVSPEED_NOFORMATADJUST do not use adjusted format to open device // WAVSPEED_NOACM do not use audio compression manager // return TRUE if supported // BOOL DLLEXPORT WINAPI WavSupportsSpeed(HWAV hWav, int idDev, int nLevel, DWORD dwFlags) { BOOL fSuccess = TRUE; BOOL fSupportsSpeed = FALSE; LPWAV lpWav; #ifdef AVTSM WAVEFORMATEX wfxTsm; #endif if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // normal speed is a special case // else if (nLevel == 100 && WavOutSupportsFormat(NULL, idDev, lpWav->lpwfx[FORMATPLAY])) { fSupportsSpeed = TRUE; } #ifdef AVTSM // see if time scale modification will work // else if (!(dwFlags & WAVSPEED_NOTSM) && TsmSupportsSpeed(nLevel, lpWav->lpwfx[FORMATPLAY], 0)) { fSupportsSpeed = TRUE; } // see if time scale modification will work with PCM 16-bit mono // else if (!(dwFlags & WAVSPEED_NOTSM) && !(dwFlags & WAVSPEED_NOACM) && TsmSupportsSpeed(nLevel, WavFormatPcm( lpWav->lpwfx[FORMATPLAY]->nSamplesPerSec, 16, 1, &wfxTsm), 0)) { fSupportsSpeed = TRUE; } #endif // see if the device driver supports playback rate directly // else if (!(dwFlags & WAVSPEED_NOPLAYBACKRATE) && WavOutSupportsSpeed(NULL, idDev)) { fSupportsSpeed = TRUE; } else if (!(dwFlags & WAVSPEED_NOFORMATADJUST)) { LPWAVEFORMATEX lpwfx = NULL; if ((lpwfx = WavFormatDup(lpWav->lpwfx[FORMATPLAY])) == NULL) fSuccess = TraceFALSE(NULL); else if (WavFormatSpeedAdjust(lpwfx, nLevel, 0) != 0) fSuccess = TraceTRUE(NULL); // see if device supports playback using adjusted format // else if (WavOutSupportsFormat(NULL, idDev, lpwfx)) { fSupportsSpeed = TRUE; } if (lpwfx != NULL && WavFormatFree(lpwfx) != 0) fSuccess = TraceFALSE(NULL); else lpwfx = NULL; // as a last resort, see if doubling or halving the sample rate // would allow us to use a wave format that has been adjusted // if (!fSupportsSpeed && !(dwFlags & WAVSPEED_NOACM)) { LPWAVEFORMATEX lpwfx = NULL; if (nLevel < 100) nLevel = nLevel * 100 / 50; else if (nLevel > 100) nLevel = nLevel * 100 / 200; if ((lpwfx = WavFormatDup(lpWav->lpwfx[FORMATPLAY])) == NULL) fSuccess = TraceFALSE(NULL); else if (WavFormatSpeedAdjust(lpwfx, nLevel, 0) != 0) fSuccess = TraceTRUE(NULL); // see if device supports playback using adjusted format // else if (WavOutSupportsFormat(NULL, idDev, lpwfx)) { fSupportsSpeed = TRUE; } if (lpwfx != NULL && WavFormatFree(lpwfx) != 0) fSuccess = TraceFALSE(NULL); else lpwfx = NULL; } } return fSuccess ? fSupportsSpeed : FALSE; } // WavGetChunks - get chunk count and size // (i) handle returned from WavOpen // NULL get default chunk count and size // (o) buffer to hold chunk count // NULL do not get chunk count // (o) buffer to hold chunk size // NULL do not get chunk size // (i) TRUE for playback, FALSE for recording // return 0 if success // int DLLEXPORT WINAPI WavGetChunks(HWAV hWav, int FAR *lpcChunks, long FAR *lpmsChunkSize, BOOL fWavOut) { BOOL fSuccess = TRUE; LPWAV lpWav; if (hWav != NULL && (lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else { if (lpcChunks != NULL) { if (hWav == NULL) { *lpcChunks = fWavOut ? cPlayChunksDefault : cRecordChunksDefault; } else { *lpcChunks = fWavOut ? lpWav->cPlayChunks : lpWav->cRecordChunks; } } if (lpmsChunkSize != NULL) { if (hWav == NULL) { *lpmsChunkSize = fWavOut ? msPlayChunkSizeDefault : msRecordChunkSizeDefault; } else { *lpmsChunkSize = fWavOut ? lpWav->msPlayChunkSize : lpWav->msRecordChunkSize; } } } return fSuccess ? 0 : -1; } // WavSetChunks - set chunk count and size // (i) handle returned from WavOpen // NULL set default chunk count and size // (i) number of chunks in device queue // -1 do not set chunk count // (i) chunk size in milleseconds // -1 do not set chunk size // (i) TRUE for playback, FALSE for recording // return 0 if success // int DLLEXPORT WINAPI WavSetChunks(HWAV hWav, int cChunks, long msChunkSize, BOOL fWavOut) { BOOL fSuccess = TRUE; LPWAV lpWav; if (hWav != NULL && (lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else if (fWavOut && cChunks != -1 && (cChunks < PLAYCHUNKCOUNT_MIN || cChunks > PLAYCHUNKCOUNT_MAX)) fSuccess = TraceFALSE(NULL); else if (fWavOut && msChunkSize != -1 && (msChunkSize < PLAYCHUNKSIZE_MIN || msChunkSize > PLAYCHUNKSIZE_MAX)) fSuccess = TraceFALSE(NULL); else if (!fWavOut && cChunks != -1 && (cChunks < RECORDCHUNKCOUNT_MIN || cChunks > RECORDCHUNKCOUNT_MAX)) fSuccess = TraceFALSE(NULL); else if (!fWavOut && msChunkSize != -1 && (msChunkSize < RECORDCHUNKSIZE_MIN || msChunkSize > RECORDCHUNKSIZE_MAX)) fSuccess = TraceFALSE(NULL); else { if (fWavOut && cChunks != -1) { if (hWav == NULL) cPlayChunksDefault = cChunks; else lpWav->cPlayChunks = cChunks; } if (fWavOut && msChunkSize != -1) { if (hWav == NULL) msPlayChunkSizeDefault = msChunkSize; else lpWav->msPlayChunkSize = msChunkSize; } if (!fWavOut && cChunks != -1) { if (hWav == NULL) cRecordChunksDefault = cChunks; else lpWav->cRecordChunks = cChunks; } if (!fWavOut && msChunkSize != -1) { if (hWav == NULL) msRecordChunkSizeDefault = msChunkSize; else lpWav->msRecordChunkSize = msChunkSize; } } return fSuccess ? 0 : -1; } // WavCalcChunkSize - calculate chunk size in bytes // (i) wav format // (i) chunk size in milleseconds // -1 default chunk size // (i) TRUE for playback, FALSE for recording // return chunk size in bytes (-1 if success) // long DLLEXPORT WINAPI WavCalcChunkSize(LPWAVEFORMATEX lpwfx, long msChunkSize, BOOL fWavOut) { BOOL fSuccess = TRUE; long cbChunkSize; if (msChunkSize == -1) { msChunkSize = fWavOut ? PLAYCHUNKSIZE_DEFAULT : RECORDCHUNKSIZE_DEFAULT; } if (!WavFormatIsValid(lpwfx) != 0) fSuccess = TraceFALSE(NULL); else if (fWavOut && (msChunkSize < PLAYCHUNKSIZE_MIN || msChunkSize > PLAYCHUNKSIZE_MAX)) fSuccess = TraceFALSE(NULL); else if (!fWavOut && (msChunkSize < RECORDCHUNKSIZE_MIN || msChunkSize > RECORDCHUNKSIZE_MAX)) fSuccess = TraceFALSE(NULL); #if 0 // this only works for PCM else { int nBytesPerSample; // calculate bytes per sample // nBytesPerSample = lpwfx->nChannels * (((lpwfx->wBitsPerSample - 1) / 8) + 1); // calculate chunk size in bytes // cbChunkSize = msChunkSize * lpwfx->nSamplesPerSec * nBytesPerSample / 1000L; // round up to nearest 1K bytes // cbChunkSize = 1024L * ((cbChunkSize + 1023L) / 1024L); } #else else { long lBlockAlign; // calculate chunk size in bytes // cbChunkSize = msChunkSize * lpwfx->nAvgBytesPerSec / 1000L; // round up to nearest block boundary // if ((lBlockAlign = (long) lpwfx->nBlockAlign) > 0) { cbChunkSize = lBlockAlign * ((cbChunkSize + lBlockAlign - 1) / lBlockAlign); } } #endif return fSuccess ? cbChunkSize : -1; } // WavCopy - copy data from one open wav file to another // (i) source handle returned from WavOpen // (i) destination handle returned from WavOpen // (o) pointer to copy buffer // NULL allocate buffer internally // (i) size of copy buffer // -1 default buffer size (16K) // (i) function that returns TRUE if user aborts // NULL don't check for user abort // (i) parameter passed to // (i) control flags // WAV_NOACM do not use audio compression manager // return 0 if success (-1 if error, +1 if user abort) // int DLLEXPORT WINAPI WavCopy(HWAV hWavSrc, HWAV hWavDst, void _huge *hpBuf, long sizBuf, USERABORTPROC lpfnUserAbort, DWORD dwUser, DWORD dwFlags) { BOOL fSuccess = TRUE; LPWAV lpWavSrc; LPWAV lpWavDst; LPWAVEFORMATEX lpwfxSrc; LPWAVEFORMATEX lpwfxDst; BOOL fFreeBuf = (BOOL) (hpBuf == NULL); BOOL fUserAbort = FALSE; // calc buffer size if none supplied // if (sizBuf <= 0) sizBuf = 16 * 1024; if ((lpWavSrc = WavGetPtr(hWavSrc)) == NULL) fSuccess = TraceFALSE(NULL); else if ((lpWavDst = WavGetPtr(hWavDst)) == NULL) fSuccess = TraceFALSE(NULL); // get source file format // else if ((lpwfxSrc = WavGetFormat(hWavSrc, WAV_FORMATFILE)) == NULL) fSuccess = TraceFALSE(NULL); // get destination file format // else if ((lpwfxDst = WavGetFormat(hWavDst, WAV_FORMATFILE)) == NULL) fSuccess = TraceFALSE(NULL); // allocate buffer if none supplied // else if (hpBuf == NULL && (hpBuf = MemAlloc(NULL, sizBuf, 0)) == NULL) fSuccess = TraceFALSE(NULL); // do simple copy if no conversion is required // else if (WavFormatCmp(lpwfxSrc, lpwfxDst) == 0) { long lBytesReadTotal = 0; long lBytesTotal = max(1, lpWavSrc->cbData - lpWavSrc->lDataPos); while (fSuccess) { long lBytesRead; long lBytesWritten; // check for user abort, notify of percent complete // if (lpfnUserAbort != NULL && (*lpfnUserAbort)(dwUser, (int) (lBytesReadTotal / lBytesTotal))) { fUserAbort = TRUE; break; } // fill copy buffer // else if ((lBytesRead = WavRead(hWavSrc, hpBuf, sizBuf)) < 0) fSuccess = TraceFALSE(NULL); // keep running total // else if ((lBytesReadTotal += lBytesRead) < 0) fSuccess = TraceFALSE(NULL); // check for end of file // else if (lBytesRead == 0) break; // eof // write the buffer // else if ((lBytesWritten = WavWrite(hWavDst, hpBuf, lBytesRead)) < 0) fSuccess = TraceFALSE(NULL); } // notify of 100% complete // if (fSuccess && lpfnUserAbort != NULL) (*lpfnUserAbort)(dwUser, 100); } // different formats require conversion during copy // else { long lBytesReadTotal = 0; long lBytesTotal = max(1, lpWavSrc->cbData - lpWavSrc->lDataPos); HACM hAcm = NULL; long sizBufRead; void _huge *hpBufRead = NULL; // turn on WAV_NOACM flag if either file was opened with it // if ((lpWavSrc->dwFlags & WAV_NOACM) || (lpWavDst->dwFlags & WAV_NOACM)) dwFlags |= WAV_NOACM; // start acm engine // if ((hAcm = AcmInit(ACM_VERSION, lpWavSrc->hInst, (dwFlags & WAV_NOACM) ? ACM_NOACM : 0)) == NULL) fSuccess = TraceFALSE(NULL); // start conversion engine // else if (AcmConvertInit(hAcm, lpwfxSrc, lpwfxDst, NULL, 0) != 0) fSuccess = TraceFALSE(NULL); // calc how many bytes required for read buffer // else if ((sizBufRead = AcmConvertGetSizeSrc(hAcm, sizBuf)) <= 0) fSuccess = TraceFALSE(NULL); // allocate read buffer // else if ((hpBufRead = (void _huge *) MemAlloc(NULL, sizBufRead, 0)) == NULL) { fSuccess = TraceFALSE(NULL); } // do the conversion during the copy // else while (fSuccess) { long lBytesRead; long lBytesConverted; long lBytesWritten; // check for user abort, notify of percent complete // if (lpfnUserAbort != NULL && (*lpfnUserAbort)(dwUser, (int) (lBytesReadTotal / lBytesTotal))) { fUserAbort = TRUE; break; } // fill read buffer // else if ((lBytesRead = WavRead(hWavSrc, hpBufRead, sizBufRead)) < 0) fSuccess = TraceFALSE(NULL); // keep running total // else if ((lBytesReadTotal += lBytesRead) < 0) fSuccess = TraceFALSE(NULL); // check for end of file // else if (lBytesRead == 0) break; // eof // convert the data // else if ((lBytesConverted = AcmConvert(hAcm, hpBufRead, lBytesRead, hpBuf, sizBuf, 0)) < 0) { fSuccess = TraceFALSE(NULL); } // write the buffer // else if ((lBytesWritten = WavWrite(hWavDst, hpBuf, lBytesConverted)) < 0) { fSuccess = TraceFALSE(NULL); } } // notify of 100% complete // if (fSuccess && lpfnUserAbort != NULL) (*lpfnUserAbort)(dwUser, 100); // clean up // if (hpBufRead != NULL && (hpBufRead = MemFree(NULL, hpBufRead)) != NULL) fSuccess = TraceFALSE(NULL); // NOTE: AcmConvertTerm() is called from AcmTerm() // if (hAcm != NULL && AcmTerm(hAcm) != 0) fSuccess = TraceFALSE(NULL); } // clean up // if (hpBuf != NULL && fFreeBuf && (hpBuf = MemFree(NULL, hpBuf)) != NULL) fSuccess = TraceFALSE(NULL); if (!fSuccess) return -1; else if (fUserAbort) return +1; else return 0; } #ifdef AVTSM // WavReadFormatSpeed - read data from wav file, then format it for speed // (i) handle returned from WavOpen // (o) buffer to contain bytes read // (i) size of buffer in bytes // return bytes formatted for speed in (-1 if error) // // NOTE: this function reads a block of data, and then converts it // from the file format to the speed format, unless those formats // are identical. // long DLLEXPORT WINAPI WavReadFormatSpeed(HWAV hWav, void _huge *hpBufSpeed, long sizBufSpeed) { BOOL fSuccess = TRUE; LPWAV lpWav; long sizBufPlay; void _huge *hpBufPlay = NULL; long lBytesPlay; long lBytesSpeed = 0; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // see if speed conversion required // else if (lpWav->nSpeedLevel == 100 || (lpWav->dwFlagsSpeed & WAVSPEED_NOTSM)) { // no, so just convert file format to play format // if ((lBytesSpeed = WavReadFormatPlay(hWav, hpBufSpeed, sizBufSpeed)) < 0) fSuccess = TraceFALSE(NULL); } // calc how many bytes required for play buffer // else if ((sizBufPlay = sizBufSpeed * (lpWav->nSpeedLevel - 2) / 100) <= 0) fSuccess = TraceFALSE(NULL); // round down to nearest block boundary // else if (lpWav->lpwfx[FORMATPLAY]->nBlockAlign > 0 && (sizBufPlay = lpWav->lpwfx[FORMATPLAY]->nBlockAlign * (sizBufPlay / lpWav->lpwfx[FORMATPLAY]->nBlockAlign)) <= 0) { fSuccess = TraceFALSE(NULL); } // allocate play buffer // else if ((hpBufPlay = (void _huge *) MemAlloc(NULL, sizBufPlay, 0)) == NULL) { fSuccess = TraceFALSE(NULL); } // fill play buffer // else if ((lBytesPlay = WavReadFormatPlay(hWav, hpBufPlay, sizBufPlay)) < 0) fSuccess = TraceFALSE(NULL); // convert the data from playback format to speed format // else if (lBytesPlay > 0 && (lBytesSpeed = TsmConvert(lpWav->hTsm, hpBufPlay, lBytesPlay, hpBufSpeed, sizBufSpeed, 0)) < 0) { fSuccess = TraceFALSE(NULL); } // clean up // if (hpBufPlay != NULL && (hpBufPlay = MemFree(NULL, hpBufPlay)) != NULL) fSuccess = TraceFALSE(NULL); return fSuccess ? lBytesSpeed : -1; } #endif // WavReadFormatPlay - read data from wav file, then format it for playback // (i) handle returned from WavOpen // (o) buffer to contain bytes read // (i) size of buffer in bytes // return bytes formatted for playback in (-1 if error) // // NOTE: this function reads a block of data, and then converts it // from the file format to the playback format, unless those formats // are identical. // long DLLEXPORT WINAPI WavReadFormatPlay(HWAV hWav, void _huge *hpBufPlay, long sizBufPlay) { BOOL fSuccess = TRUE; LPWAV lpWav; long sizBufRead; void _huge *hpBufRead = NULL; long lBytesRead; long lBytesPlay = 0; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // see if format conversion required // else if (WavFormatCmp(lpWav->lpwfx[FORMATFILE], lpWav->lpwfx[FORMATPLAY]) == 0) { // no, so just read block directly into the play buffer // if ((lBytesPlay = WavRead(hWav, hpBufPlay, sizBufPlay)) < 0) fSuccess = TraceFALSE(NULL); } // calc how many bytes required for read buffer // else if ((sizBufRead = AcmConvertGetSizeSrc(lpWav->hAcm, sizBufPlay)) <= 0) fSuccess = TraceFALSE(NULL); // allocate read buffer // else if ((hpBufRead = (void _huge *) MemAlloc(NULL, sizBufRead, 0)) == NULL) { fSuccess = TraceFALSE(NULL); } // fill read buffer // else if ((lBytesRead = WavRead(hWav, hpBufRead, sizBufRead)) < 0) fSuccess = TraceFALSE(NULL); // convert the data from file format to playback format // else if (lBytesRead > 0 && (lBytesPlay = AcmConvert(lpWav->hAcm, hpBufRead, lBytesRead, hpBufPlay, sizBufPlay, 0)) < 0) { fSuccess = TraceFALSE(NULL); } // clean up // if (hpBufRead != NULL && (hpBufRead = MemFree(NULL, hpBufRead)) != NULL) fSuccess = TraceFALSE(NULL); return fSuccess ? lBytesPlay : -1; } // WavWriteFormatRecord - write data to file after formatting it for file // (i) handle returned from WavOpen // (i) buffer containing bytes in record format // (i) size of buffer in bytes // return bytes written (-1 if error) // // NOTE: this function converts a block of data from the record // format to the file format (unless those formats are identical), // and then writes the data to disk. // long DLLEXPORT WINAPI WavWriteFormatRecord(HWAV hWav, void _huge *hpBufRecord, long sizBufRecord) { BOOL fSuccess = TRUE; LPWAV lpWav; long sizBufWrite; void _huge *hpBufWrite = NULL; long lBytesWrite; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // see if format conversion required // else if (WavFormatCmp(lpWav->lpwfx[FORMATRECORD], lpWav->lpwfx[FORMATFILE]) == 0) { // no, so just write record buffer directly to the file // if ((lBytesWrite = WavWrite(hWav, hpBufRecord, sizBufRecord)) < 0) fSuccess = TraceFALSE(NULL); } // calc how many bytes required for write buffer // else if ((sizBufWrite = AcmConvertGetSizeDst(lpWav->hAcm, sizBufRecord)) <= 0) fSuccess = TraceFALSE(NULL); // allocate write buffer // else if ((hpBufWrite = (void _huge *) MemAlloc(NULL, sizBufWrite, 0)) == NULL) { fSuccess = TraceFALSE(NULL); } // convert the data from record format to file format // else if ((lBytesWrite = AcmConvert(lpWav->hAcm, hpBufRecord, sizBufRecord, hpBufWrite, sizBufWrite, 0)) < 0) { fSuccess = TraceFALSE(NULL); } // write buffer to disk // else if ((lBytesWrite = WavWrite(hWav, hpBufWrite, lBytesWrite)) < 0) fSuccess = TraceFALSE(NULL); // clean up // if (hpBufWrite != NULL && (hpBufWrite = MemFree(NULL, hpBufWrite)) != NULL) fSuccess = TraceFALSE(NULL); return fSuccess ? lBytesWrite : -1; } // WavGetOutputDevice - get handle to open wav output device // (i) handle returned from WavOpen // return handle to wav output device (NULL if device not open or error) // // NOTE: this function is useful only during playback (after calling // WavPlay() and before calling WavStop()). The returned device handle // can then be used when calling the WavOut functions in wavout.h // HWAVOUT DLLEXPORT WINAPI WavGetOutputDevice(HWAV hWav) { BOOL fSuccess = TRUE; LPWAV lpWav; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); return fSuccess ? lpWav->hWavOut : NULL; } // WavGetInputDevice - get handle to open wav input device // (i) handle returned from WavOpen // return handle to wav input device (NULL if device not open or error) // // NOTE: this function is useful only during recording (after calling // WavRecord() and before calling WavStop()). The returned device handle // can then be used when calling the WavIn functions in wavin.h // HWAVIN DLLEXPORT WINAPI WavGetInputDevice(HWAV hWav) { BOOL fSuccess = TRUE; LPWAV lpWav; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); return fSuccess ? lpWav->hWavIn : NULL; } // WavPlaySound - play wav file // (i) must be WAV_VERSION // (i) instance handle of calling module // (i) wav output device id // -1 use any suitable output device // (i) name of file to play // NULL stop playing current sound, if any // (i) wave format // NULL use format from header or default // (i) address of i/o procedure to use // NULL use default i/o procedure // (i) data to pass to i/o procedure during open // NULL no data to pass // (i) control flags // WAV_ASYNC return when playback starts (default) // WAV_SYNC return after playback completes // WAV_FILENAME points to a filename // WAV_RESOURCE points to a resource // WAV_MEMORY points to memory block // WAV_NODEFAULT if sound not found, do not play default // WAV_LOOP loop sound until WavPlaySound called again // WAV_NOSTOP if device already playing, don't stop it // WAV_NORIFF file has no RIFF/WAV header // WAV_NOACM do not use audio compression manager // WAV_OPENRETRY if output device busy, retry for up to 2 sec #ifdef MULTITHREAD // WAV_MULTITHREAD support multiple threads (default) // WAV_SINGLETHREAD do not support multiple threads // WAV_COINITIALIZE call CoInitialize in all secondary threads #endif // return 0 if success // // NOTE: if WAV_NORIFF is specified in , then the // parameter must be specified. If is NULL, the // current default format is assumed. // WavSetFormat() can be used to set or override the defaults. // // NOTE: if WAV_FILENAME is specified in , then // must point to a file name. // // NOTE: if WAV_RESOURCE is specified in , then // must point to a WAVE resource in the module specified by . // If the first character of the string is a pound sign (#), the remaining // characters represent a decimal number that specifies the resource id. // // NOTE: if WAV_MEMORY is specified in , then // must be a pointer to a memory block containing a wav file image. // The pointer must be obtained by calling MemAlloc(). // // NOTE: if neither WAV_FILENAME, WAV_RESOURCE, or WAV_MEMORY is specified // in , the [sounds] section of win.ini or the registry is // searched for an entry matching . If no matching entry // is found, is assumed to be a file name. // // NOTE: if WAV_NODEFAULT is specified in , no default sound // will be played. Unless this flag is specified, the default system // event sound entry will be played if the sound specified in // is not found. // // NOTE: if WAV_LOOP is specified in , the sound specified in // will be played repeatedly, until WavPlaySound() is // called again. The WAV_ASYNC flag must be specified when using this flag. // // NOTE: if WAV_NOSTOP is specified in , and the device specified // by is already in use, this function returns without playing. // Unless this flag is specified, the specified device will be stopped // so that the new sound can be played. // // NOTE: if is not NULL, this i/o procedure will be called // for opening, closing, reading, writing, and seeking the wav file. // If is not NULL, this array of three (3) DWORDs will be // passed to the i/o procedure when the wav file is opened. // See the Windows mmioOpen() and mmioInstallIOProc() function for details // on these parameters. Also, the WAV_MEMORY and WAV_RESOURCE flags may // only be used when is NULL. // int DLLEXPORT WINAPI WavPlaySound(DWORD dwVersion, HINSTANCE hInst, int idDev, LPCTSTR lpszFileName, LPWAVEFORMATEX lpwfx, LPMMIOPROC lpIOProc, DWORD FAR *lpadwInfo, DWORD dwFlags) { BOOL fSuccess = TRUE; HWAV hWav = NULL; LPCTSTR lpszSound = lpszFileName; TCHAR szSound[_MAX_PATH]; // stop current sound if necessary // if (lpszFileName == NULL && WavStopOutputDevice(idDev, dwFlags) != 0) fSuccess = TraceFALSE(NULL); if (lpszSound != NULL) { // search win.ini or registry if necessary // if (!(dwFlags & WAV_FILENAME) && !(dwFlags & WAV_RESOURCE) && !(dwFlags & WAV_MEMORY)) { if (GetProfileString(TEXT("Sounds"), lpszFileName, TEXT(""), szSound, SIZEOFARRAY(szSound)) > 0) { LPTSTR lpszComma; // ignore text description starting with comma // if ((lpszComma = StrChr(szSound, ',')) != NULL) *lpszComma = '\0'; if (*szSound != '\0') lpszSound = szSound; } } // open sound // if ((hWav = WavOpen(WAV_VERSION, hInst, lpszSound, lpwfx, lpIOProc, lpadwInfo, dwFlags | WAV_READ)) == NULL) { // play default sound unless WAV_NODEFAULT flag set // if (!(dwFlags & WAV_NODEFAULT)) { // find system default sound // if (GetProfileString(TEXT("Sounds"), TEXT("SystemDefault"), TEXT(""), szSound, SIZEOFARRAY(szSound)) > 0) { LPTSTR lpszComma; // ignore text description starting with comma // if ((lpszComma = StrChr(szSound, ',')) != NULL) *lpszComma = '\0'; // open system default sound // if (*szSound != '\0' && (hWav = WavOpen(WAV_VERSION, hInst, szSound, NULL, NULL, NULL, WAV_READ)) == NULL) fSuccess = TraceFALSE(NULL); } } } // play the sound // if (fSuccess && hWav != NULL) { if (dwFlags & WAV_ASYNC) dwFlags |= WAV_PLAYASYNC; if (dwFlags & WAV_SYNC) dwFlags |= WAV_PLAYSYNC; if (WavPlay(hWav, idDev, dwFlags | WAV_AUTOCLOSE) != 0) fSuccess = TraceFALSE(NULL); } // clean up // if (!fSuccess && hWav != NULL && WavClose(hWav) != 0) fSuccess = TraceFALSE(NULL); } return fSuccess ? 0 : -1; } // WavSendMessage - send a user-defined message to the i/o procedure // (i) handle returned from WavOpen // (i) user-defined message id // (i) parameter for the message // (i) parameter for the message // return value from the i/o procedure (0 if error or unrecognized message) // LRESULT DLLEXPORT WINAPI WavSendMessage(HWAV hWav, UINT wMsg, LPARAM lParam1, LPARAM lParam2) { BOOL fSuccess = TRUE; LPWAV lpWav; LRESULT lResult; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else if (lpWav->hmmio == NULL) fSuccess = TraceFALSE(NULL); else lResult = mmioSendMessage(lpWav->hmmio, wMsg, lParam1, lParam2); return fSuccess ? lResult : 0; } #ifdef TELTHUNK // WavOpenEx - open an audio file, extra special version // (i) must be WAV_VERSION // (i) instance handle of calling module // (i) name of file to open // (i) reserved; must be zero // (i) control flags to pass to WavOpen // (i) control flags // WOX_LOCAL file is on local client // WOX_REMOTE file is on remote server // WOX_WAVFMT file is in Microsoft RIFF/WAV format // WOX_VOXFMT file is in Dialogic OKI ADPCM (vox) format // WOX_WAVDEV file will be played on wav output device // WOX_TELDEV file will be played on telephone device // return handle (NULL if error) // HWAV DLLEXPORT WINAPI WavOpenEx(DWORD dwVersion, HINSTANCE hInst, LPTSTR lpszFileName, DWORD dwReserved, DWORD dwFlagsOpen, DWORD dwFlagsEx) { BOOL fSuccess = TRUE; HWAV hWav = NULL; if ((dwFlagsEx & WOX_TELDEV) || (dwFlagsEx & WOX_REMOTE)) { hWav = TelWavOpenEx(TELWAV_VERSION, hInst, lpszFileName, 0, dwFlagsOpen, dwFlagsEx); } else { WAVEFORMATEX wfx; LPWAVEFORMATEX lpwfx = NULL; if (dwFlagsEx & WOX_VOXFMT) { dwFlagsOpen |= WAV_NORIFF; lpwfx = VoxFormat(&wfx, -1); } hWav = WavOpen(WAV_VERSION, hInst, lpszFileName, lpwfx, NULL, 0, dwFlagsOpen); } return fSuccess ? hWav : NULL; } #endif //// // helper functions //// // WavStopPlay - stop playing // (i) handle returned from WavOpen // return 0 if success // static int WavStopPlay(HWAV hWav) { BOOL fSuccess = TRUE; LPWAV lpWav; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else lpWav->dwState |= WAVSTATE_AUTOSTOP; // close output device if necessary // if (fSuccess && !(lpWav->dwState & WAVSTATE_STOPPLAY) && lpWav->hWavOut != NULL) { long msPositionPlay = 0L; // set a state flag so we never execute this code recursively // lpWav->dwState |= WAVSTATE_STOPPLAY; // get current playback position // if ((msPositionPlay = WavOutGetPosition(lpWav->hWavOut)) == -1) { // let's ignore errors here and assume play position is 0; // msPositionPlay = 0L; fSuccess = TraceTRUE(NULL); } else { // if necessary, adjust position to compensate for non-standard speed // if (lpWav->nSpeedLevel != 100 && ( #ifdef AVTSM !(lpWav->dwFlagsSpeed & WAVSPEED_NOTSM) || #endif !(lpWav->dwFlagsSpeed & WAVSPEED_NOFORMATADJUST))) { msPositionPlay = msPositionPlay * lpWav->nSpeedLevel / 100; } } #if 1 TracePrintf_2(NULL, 6, TEXT("lpWav->msPositionStop=%ld, msPositionPlay=%ld\n"), (long) lpWav->msPositionStop, (long) msPositionPlay); #endif // clear wav handle array entry // if (fSuccess) { int idDev; if ((idDev = WavOutGetId(lpWav->hWavOut)) < HWAVOUT_MIN || idDev >= HWAVOUT_MAX) fSuccess = TraceFALSE(NULL); else ahWavOutCurr[idDev + HWAVOUT_OFFSET] = NULL; } // close output device // if (WavOutClose(lpWav->hWavOut, 0) != 0) fSuccess = TraceFALSE(NULL); else if ((lpWav->hWavOut = NULL), FALSE) fSuccess = TraceFALSE(NULL); // destroy the notification callback window // else if (WavNotifyDestroy(lpWav) != 0) fSuccess = TraceFALSE(NULL); // move read/write pointer (back) to new stop position // else if (WavSetPosition(hWav, min(WavGetLength(hWav), lpWav->msPositionStop + msPositionPlay)) == -1) fSuccess = TraceFALSE(NULL); // close acm conversion engine // else if (AcmConvertTerm(lpWav->hAcm) != 0) fSuccess = TraceFALSE(NULL); // notify user that play is stopped if necessary // if (lpWav->lpfnPlayStopped != NULL) { (*lpWav->lpfnPlayStopped)(hWav, lpWav->hUserPlayStopped, 0); lpWav->lpfnPlayStopped = NULL; lpWav->hUserPlayStopped = NULL; } // clear state flags // lpWav->dwState &= ~WAVSTATE_STOPPLAY; // close wav file if specified // if (lpWav->dwState & WAVSTATE_AUTOCLOSE) { lpWav->dwState &= ~WAVSTATE_AUTOCLOSE; if (WavClose(hWav) != 0) fSuccess = TraceFALSE(NULL); else hWav = NULL; // handle no longer valid } } #ifdef AVTSM // shut down time scale modification engine if necessary // if (fSuccess && lpWav->hTsm != NULL) { if (TsmTerm(lpWav->hTsm) != 0) fSuccess = TraceFALSE(NULL); else lpWav->hTsm = NULL; } #endif return fSuccess ? 0 : -1; } // WavStopRecord - stop recording // (i) handle returned from WavOpen // return 0 if success // static int WavStopRecord(HWAV hWav) { BOOL fSuccess = TRUE; LPWAV lpWav; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else lpWav->dwState |= WAVSTATE_AUTOSTOP; // close input device if necessary // if (fSuccess && !(lpWav->dwState & WAVSTATE_STOPRECORD) && lpWav->hWavIn != NULL) { long msPositionRecord = 0L; // set a state flag so we never execute this code recursively // lpWav->dwState |= WAVSTATE_STOPRECORD; // get current record position // if ((msPositionRecord = WavInGetPosition(lpWav->hWavIn)) == -1) { // let's ignore errors here and assume record position is 0; // msPositionRecord = 0L; fSuccess = TraceTRUE(NULL); } #if 1 TracePrintf_2(NULL, 6, TEXT("lpWav->msPositionStop=%ld, msPositionRecord=%ld\n"), (long) lpWav->msPositionStop, (long) msPositionRecord); #endif // clear wav handle array entry // if (fSuccess) { int idDev; if ((idDev = WavInGetId(lpWav->hWavIn)) < HWAVIN_MIN || idDev >= HWAVIN_MAX) TraceFALSE(NULL); else ahWavInCurr[idDev + HWAVIN_OFFSET] = NULL; } // close input device // if (WavInClose(lpWav->hWavIn, 0) != 0) fSuccess = TraceFALSE(NULL); else if ((lpWav->hWavIn = NULL), FALSE) fSuccess = TraceFALSE(NULL); // destroy the notification callback window // else if (WavNotifyDestroy(lpWav) != 0) fSuccess = TraceFALSE(NULL); // move read/write pointer to new stop position // else if (WavSetPosition(hWav, min(WavGetLength(hWav), lpWav->msPositionStop + msPositionRecord)) == -1) fSuccess = TraceFALSE(NULL); // close acm conversion engine // else if (AcmConvertTerm(lpWav->hAcm) != 0) fSuccess = TraceFALSE(NULL); // truncate file if max file size exceeded // else if (lpWav->msMaxSize > 0 && WavGetLength(hWav) > lpWav->msMaxSize && WavSetLength(hWav, lpWav->msMaxSize) < 0) fSuccess = TraceFALSE(NULL); // clear state flags // lpWav->dwState &= ~WAVSTATE_STOPRECORD; // notify user that record is stopped if necessary // if (lpWav->lpfnRecordStopped != NULL) { (*lpWav->lpfnRecordStopped)(hWav, lpWav->dwUserRecordStopped, 0); lpWav->lpfnRecordStopped = NULL; lpWav->dwUserRecordStopped = 0; } // close wav file if specified // if (lpWav->dwState & WAVSTATE_AUTOCLOSE) { lpWav->dwState &= ~WAVSTATE_AUTOCLOSE; if (WavClose(hWav) != 0) fSuccess = TraceFALSE(NULL); else hWav = NULL; // handle no longer valid } } return fSuccess ? 0 : -1; } // WavStopOutputDevice - stop specified output device // (i) wav output device id // -1 use any suitable output device // (i) control flags // WAV_NOSTOP if device already playing, don't stop it // return 0 if success // static int WavStopOutputDevice(int idDev, DWORD dwFlags) { BOOL fSuccess = TRUE; // stop output device (unless WAV_NOSTOP flag set or device not open) // if (!(dwFlags & WAV_NOSTOP) && WavOutDeviceIsOpen(idDev)) { HWAV hWavCurr; // stop device using WavStopPlay if WavPlay was used // if ((hWavCurr = WavGetOutputHandle(idDev)) != NULL && WavStopPlay(hWavCurr) != 0) fSuccess = TraceFALSE(NULL); // otherwise stop device using sndPlaySound // else if (!sndPlaySound(NULL, 0)) fSuccess = TraceFALSE(NULL); } return fSuccess ? 0 : -1; } // WavStopInputDevice - stop specified input device // (i) wav input device id // -1 use any suitable input device // (i) control flags // WAV_NOSTOP if device already recording, don't stop it // return 0 if success // static int WavStopInputDevice(int idDev, DWORD dwFlags) { BOOL fSuccess = TRUE; // stop input device (unless WAV_NOSTOP flag set or device not open) // if (!(dwFlags & WAV_NOSTOP) && WavInDeviceIsOpen(idDev)) { HWAV hWavCurr; // stop device using WavStopRecord if WavRecord was used // if ((hWavCurr = WavGetInputHandle(idDev)) != NULL && WavStopRecord(hWavCurr) != 0) fSuccess = TraceFALSE(NULL); // otherwise stop device using sndPlaySound // else if (!sndPlaySound(NULL, 0)) fSuccess = TraceFALSE(NULL); } return fSuccess ? 0 : -1; } // WavGetOutputHandle - get wav handle being played on specified device // (i) wav output device id // return wav handle (NULL if error or if output device not in use) // static HWAV WavGetOutputHandle(int idDev) { BOOL fSuccess = TRUE; HWAV hWav; if (idDev < HWAVOUT_MIN || idDev >= HWAVOUT_MAX) fSuccess = TraceFALSE(NULL); else hWav = ahWavOutCurr[idDev + HWAVOUT_OFFSET]; return fSuccess ? hWav : NULL; } // WavGetInputHandle - get wav handle being recorded on specified device // (i) wav input device id // return wav handle (NULL if error or if input device not in use) // static HWAV WavGetInputHandle(int idDev) { BOOL fSuccess = TRUE; HWAV hWav; if (idDev < HWAVIN_MIN || idDev >= HWAVIN_MAX) fSuccess = TraceFALSE(NULL); else hWav = ahWavInCurr[idDev + HWAVIN_OFFSET]; return fSuccess ? hWav : NULL; } // WavPlayNextChunk - fill next chunk and submit it to the output device // (i) handle returned from WavOpen // return 0 if success // static int WavPlayNextChunk(HWAV hWav) { BOOL fSuccess = TRUE; LPWAV lpWav; LPVOID lpBuf = NULL; long sizBuf; long lBytesRead = 0; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // calculate the size of output chunk // // // we have to verify if lpWav is a valid pointer // else if ((sizBuf = WavCalcChunkSize(lpWav->lpwfx[FORMATPLAY], lpWav->msPlayChunkSize, TRUE)) <= 0) { fSuccess = TraceFALSE(NULL); } #ifdef TELOUT // $FIXUP - need to work on this // special case - if we are using the telephone to play audio // from a file that already resides on the server, just pass // the file handle to TelOutPlay rather than a buffer // else if (WavOutGetId(lpWav->hWavOut) == TELOUT_DEVICEID && (lpWav->dwFlags & WAV_TELRFILE)) { long hrfile; long lSize; long lPos; // retrieve handle to remote file from i/o procedure // if ((hrfile = (long) WavSendMessage(hWav, MMIOM_GETINFO, 0, 0)) == (long) -1) fSuccess = TraceFALSE(NULL); // retrieve size of remote file from i/o procedure // else if ((lSize = (long) WavSendMessage(hWav, MMIOM_GETINFO, 1, 0)) == (long) -1) fSuccess = TraceFALSE(NULL); // get current file position // else if ((lPos = mmioSeek(lpWav->hmmio, 0, SEEK_CUR)) == -1) fSuccess = TraceFALSE(NULL); // force the remote file pointer to be at that position // else if (WavOutGetState(lpWav->hWavOut) == WAVOUT_STOPPED) { long lPosActual; if ((lPosActual = mmioSendMessage(lpWav->hmmio, MMIOM_SEEK, lPos, SEEK_SET)) != lPos) { fSuccess = TraceTRUE(NULL); // not an error } } // loop until we read some data or we know we are finished // while (fSuccess && lBytesRead == 0) { // calculate how many bytes would be read // lBytesRead = max(0, min(sizBuf, lSize - lPos)); TracePrintf_4(NULL, 5, TEXT("lBytesRead(%ld) = max(0, min(sizBuf(%ld), lSize(%ld) - lPos(%ld)));\n"), (long) lBytesRead, (long) sizBuf, (long) lSize, (long) lPos); // advance the file position, skipping over the (virtual) chunk // if (lBytesRead > 0 && mmioSeek(lpWav->hmmio, lBytesRead, SEEK_CUR) == -1) fSuccess = TraceFALSE(NULL); // submit the (virtual) chunk to the output device for playback // else if (lBytesRead > 0 && TelOutPlay((HTELOUT) lpWav->hWavOut, lpBuf, lBytesRead, hrfile) != 0) fSuccess = TraceFALSE(NULL); // if no more wav data to play, // else if (lBytesRead == 0) { // if WAV_AUTOSTOP flag set, we are finished // if (lpWav->dwState & WAVSTATE_AUTOSTOP) { // if output device is idle, then close output device // if (lpWav->hWavOut != NULL && WavOutGetState(lpWav->hWavOut) == WAVOUT_STOPPED && WavStopPlay(hWav) != 0) { fSuccess = TraceFALSE(NULL); } break; } // if not finished, yield and then try to read again // else { MSG msg; if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else WaitMessage(); } } } return fSuccess ? 0 : -1; } #endif else if ((lpBuf = (LPVOID) MemAlloc(NULL, sizBuf, 0)) == NULL) fSuccess = TraceFALSE(NULL); // loop until we read some data or we know we are finished // while (fSuccess && lBytesRead == 0) { // fill chunk with wav data // #ifdef AVTSM if ((lBytesRead = WavReadFormatSpeed(hWav, lpBuf, sizBuf)) < 0) #else if ((lBytesRead = WavReadFormatPlay(hWav, lpBuf, sizBuf)) < 0) #endif fSuccess = TraceFALSE(NULL); // submit the chunk to the output device for playback // else if (lBytesRead > 0 && WavOutPlay(lpWav->hWavOut, lpBuf, lBytesRead) != 0) { fSuccess = TraceFALSE(NULL); } // if no more wav data to play, // else if (lBytesRead == 0) { // if WAV_AUTOSTOP flag set, we are finished // if (lpWav->dwState & WAVSTATE_AUTOSTOP) { // if output device is idle, then close output device // if (lpWav->hWavOut != NULL && WavOutGetState(lpWav->hWavOut) == WAVOUT_STOPPED && WavStopPlay(hWav) != 0) { fSuccess = TraceFALSE(NULL); } break; } // if not finished, yield and then try to read again // else { MSG msg; if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else WaitMessage(); } } } // free buffer if it was not sent to output device // if (!fSuccess || lBytesRead == 0) { if (lpBuf != NULL && (lpBuf = MemFree(NULL, lpBuf)) != NULL) fSuccess = TraceFALSE(NULL); } return fSuccess ? 0 : -1; } // WavRecordNextChunk - submit next chunk to the input device // (i) handle returned from WavOpen // return 0 if success // static int WavRecordNextChunk(HWAV hWav) { BOOL fSuccess = TRUE; // // We have to initialize local variable // LPWAV lpWav = NULL; LPVOID lpBuf = NULL; long sizBuf; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); else { // // we should make sure lpWas is not NULL // // calculate the size of input chunk // if ((sizBuf = WavCalcChunkSize(lpWav->lpwfx[FORMATRECORD], lpWav->msRecordChunkSize, FALSE)) <= 0) { fSuccess = TraceFALSE(NULL); } #ifdef TELOUT // $FIXUP - need to work on this // special case - if we are using the telephone to record audio // to a file that already resides on the server, just pass // the file handle to TelOutRecord rather than a buffer // else if (WavInGetId(lpWav->hWavIn) == TELIN_DEVICEID && (lpWav->dwFlags & WAV_TELRFILE)) { long hrfile; // retrieve handle to remote file from i/o procedure // if ((hrfile = (long) WavSendMessage(hWav, MMIOM_GETINFO, 0, 0)) == (long) -1) fSuccess = TraceFALSE(NULL); // submit the (virtual) chunk to the output device for playback // else if (TelInRecord((HTELIN) lpWav->hWavIn, lpBuf, sizBuf, hrfile) != 0) fSuccess = TraceFALSE(NULL); return fSuccess ? 0 : -1; } #endif else if ((lpBuf = (LPVOID) MemAlloc(NULL, sizBuf, 0)) == NULL) fSuccess = TraceFALSE(NULL); // submit the chunk to the input device for recording // else if (WavInRecord(lpWav->hWavIn, lpBuf, sizBuf) != 0) { fSuccess = TraceFALSE(NULL); } } // free buffer if it was not sent to input device // if (!fSuccess) { if (lpBuf != NULL && (lpBuf = MemFree(NULL, lpBuf)) != NULL) fSuccess = TraceFALSE(NULL); } return fSuccess ? 0 : -1; } static int WavNotifyCreate(LPWAV lpWav) { BOOL fSuccess = TRUE; WNDCLASS wc; #ifdef MULTITHREAD // handle WAV_MULTITHREAD flag // if (fSuccess && (lpWav->dwFlags & WAV_MULTITHREAD)) { DWORD dwRet; // we need to know when callback thread begins execution // if ((lpWav->hEventThreadCallbackStarted = CreateEvent( NULL, FALSE, FALSE, NULL)) == NULL) { fSuccess = TraceFALSE(NULL); } // create the callback thread // else if ((lpWav->hThreadCallback = CreateThread( NULL, 0, WavCallbackThread, (LPVOID) lpWav, 0, &lpWav->dwThreadId)) == NULL) { fSuccess = TraceFALSE(NULL); } // wait for the callback thread to begin execution // else if ((dwRet = WaitForSingleObject( lpWav->hEventThreadCallbackStarted, 10000)) != WAIT_OBJECT_0) { fSuccess = TraceFALSE(NULL); } // clean up // if (lpWav->hEventThreadCallbackStarted != NULL) { if (!CloseHandle(lpWav->hEventThreadCallbackStarted)) fSuccess = TraceFALSE(NULL); else lpWav->hEventThreadCallbackStarted = NULL; } } else #endif { // register notify class unless it has been already // if (GetClassInfo(lpWav->hInst, WAVCLASS, &wc) == 0) { wc.hCursor = NULL; wc.hIcon = NULL; wc.lpszMenuName = NULL; wc.hInstance = lpWav->hInst; wc.lpszClassName = WAVCLASS; wc.hbrBackground = NULL; wc.lpfnWndProc = WavNotify; wc.style = 0L; wc.cbWndExtra = sizeof(lpWav); wc.cbClsExtra = 0; if (!RegisterClass(&wc)) fSuccess = TraceFALSE(NULL); } // create the notify window // if (fSuccess && lpWav->hwndNotify == NULL && (lpWav->hwndNotify = CreateWindowEx( 0L, WAVCLASS, NULL, 0L, 0, 0, 0, 0, NULL, NULL, lpWav->hInst, lpWav)) == NULL) { fSuccess = TraceFALSE(NULL); } } return fSuccess ? 0 : -1; } static int WavNotifyDestroy(LPWAV lpWav) { BOOL fSuccess = TRUE; // destroy notify window // if (lpWav->hwndNotify != NULL && !DestroyWindow(lpWav->hwndNotify)) { fSuccess = TraceFALSE(NULL); } else lpWav->hwndNotify = NULL; #ifdef MULTITHREAD if (lpWav->hThreadCallback != NULL) { if (!CloseHandle(lpWav->hThreadCallback)) fSuccess = TraceFALSE(NULL); else lpWav->hThreadCallback = NULL; } #endif return fSuccess ? 0 : -1; } #ifdef MULTITHREAD DWORD WINAPI WavCallbackThread(LPVOID lpvThreadParameter) { BOOL fSuccess = TRUE; MSG msg; LPWAV lpWav = (LPWAV) lpvThreadParameter; // initialize COM // if (lpWav->dwFlags & WAV_COINITIALIZE) { if ((lpWav->hrCoInitialize = CoInitialize(NULL)) != S_OK && lpWav->hrCoInitialize != S_FALSE) { fSuccess = TraceFALSE(NULL); TracePrintf_1(NULL, 5, TEXT("CoInitialize failed (%08X)\n"), (unsigned long) lpWav->hrCoInitialize); } } // 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(lpWav->hEventThreadCallbackStarted)) { fSuccess = TraceFALSE(NULL); } while (fSuccess && GetMessage(&msg, NULL, 0, 0)) { WavNotify((HWND) lpWav, msg.message, msg.wParam, msg.lParam); // exit thread when when have processed last expected message // if (msg.message == WM_WAVOUT_CLOSE || msg.message == WM_WAVIN_CLOSE) { // notify main thread that we are terminating // if (lpWav->hEventStopped != NULL && !SetEvent(lpWav->hEventStopped)) { fSuccess = TraceFALSE(NULL); } break; } } // uninitialize COM // if (lpWav->dwFlags & WAV_COINITIALIZE) { if (lpWav->hrCoInitialize == S_OK || lpWav->hrCoInitialize == S_FALSE) { CoUninitialize(); lpWav->hrCoInitialize = E_UNEXPECTED; } } return 0; } #endif // WavNotify - window procedure for wav notify // LRESULT DLLEXPORT CALLBACK WavNotify(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { BOOL fSuccess = TRUE; LRESULT lResult; LPWAV lpWav; #ifdef MULTITHREAD if (!IsWindow(hwnd)) lpWav = (LPWAV) hwnd; else #endif // retrieve lpWav from window extra bytes // lpWav = (LPWAV) GetWindowLongPtr(hwnd, 0); switch (msg) { case WM_NCCREATE: { LPCREATESTRUCT lpcs = (LPCREATESTRUCT) lParam; LPWAV lpWav = (LPWAV) lpcs->lpCreateParams; // store lpWav in window extra bytes // SetWindowLongPtr(hwnd, 0, (LONG_PTR) lpWav); lResult = DefWindowProc(hwnd, msg, wParam, lParam); } break; case WM_WAVOUT_OPEN: { TracePrintf_0(NULL, 5, TEXT("WM_WAVOUT_OPEN\n")); lResult = 0L; #ifdef MULTITHREAD if ((HANDLE) wParam != NULL) SetEventMessageProcessed(lpWav, (HANDLE) wParam); #endif } break; case WM_WAVOUT_CLOSE: { TracePrintf_0(NULL, 5, TEXT("WM_WAVOUT_CLOSE\n")); // handle no longer valid // lpWav->hWavOut = NULL; lResult = 0L; #ifdef MULTITHREAD if ((HANDLE) wParam != NULL) SetEventMessageProcessed(lpWav, (HANDLE) wParam); #endif } break; case WM_WAVOUT_PLAYDONE: { LPPLAYDONE lpplaydone = (LPPLAYDONE) lParam; TracePrintf_0(NULL, 5, TEXT("WM_WAVOUT_PLAYDONE\n")); if (lpplaydone == NULL) fSuccess = TraceFALSE(NULL); else if (lpplaydone->lpBuf != NULL && (lpplaydone->lpBuf = MemFree(NULL, lpplaydone->lpBuf)) != NULL) fSuccess = TraceFALSE(NULL); else switch (WavOutGetState(lpWav->hWavOut)) { case WAVOUT_STOPPED: // make sure to close output device when play complete // if (lpWav->dwState & WAVSTATE_AUTOSTOP) { #ifdef MULTITHREAD if (lpWav->dwFlags & WAV_MULTITHREAD) PostThreadMessage(lpWav->dwThreadId, WM_WAVOUT_STOPPLAY, 0, 0); else #endif PostMessage(lpWav->hwndNotify, WM_WAVOUT_STOPPLAY, 0, 0); break; } // else fall through case WAVOUT_PLAYING: // load output device queue with next chunk to play // if (WavPlayNextChunk(WavGetHandle(lpWav)) != 0) fSuccess = TraceFALSE(NULL); break; default: break; } lResult = 0L; #ifdef MULTITHREAD if ((HANDLE) wParam != NULL) SetEventMessageProcessed(lpWav, (HANDLE) wParam); #endif } break; case WM_WAVOUT_STOPPLAY: { TracePrintf_0(NULL, 5, TEXT("WM_WAVOUT_STOPPLAY\n")); if (WavStopPlay(WavGetHandle(lpWav)) != 0) fSuccess = TraceFALSE(NULL); lResult = 0L; } break; case WM_WAVIN_OPEN: { TracePrintf_0(NULL, 5, TEXT("WM_WAVIN_OPEN\n")); lResult = 0L; #ifdef MULTITHREAD if ((HANDLE) wParam != NULL) SetEventMessageProcessed(lpWav, (HANDLE) wParam); #endif } break; case WM_WAVIN_CLOSE: { TracePrintf_0(NULL, 5, TEXT("WM_WAVIN_CLOSE\n")); // handle no longer valid // lpWav->hWavIn = NULL; lResult = 0L; #ifdef MULTITHREAD if ((HANDLE) wParam != NULL) SetEventMessageProcessed(lpWav, (HANDLE) wParam); #endif } break; case WM_WAVIN_RECORDDONE: { LPRECORDDONE lprecorddone = (LPRECORDDONE) lParam; long lBytesWritten; TracePrintf_0(NULL, 5, TEXT("WM_WAVIN_RECORDDONE\n")); if (lprecorddone == NULL) fSuccess = TraceFALSE(NULL); // write wav data from chunk to file // else if (lprecorddone->lpBuf != NULL && lprecorddone->lBytesRecorded > 0 && (lBytesWritten = WavWriteFormatRecord(WavGetHandle(lpWav), lprecorddone->lpBuf, lprecorddone->lBytesRecorded)) < 0) { fSuccess = TraceFALSE(NULL); } // free the record buffer, allocated in WavRecordNextChunk() // // // we have to verify if lprecorddone is a valid pointer // if (lprecorddone != NULL && lprecorddone->lpBuf != NULL && (lprecorddone->lpBuf = MemFree(NULL, lprecorddone->lpBuf)) != NULL) { fSuccess = TraceFALSE(NULL); } // stop recording if max size exceeded // else if (lpWav->msMaxSize > 0 && WavGetLength(WavGetHandle(lpWav)) > lpWav->msMaxSize) { #ifdef MULTITHREAD if (lpWav->dwFlags & WAV_MULTITHREAD) PostThreadMessage(lpWav->dwThreadId, WM_WAVIN_STOPRECORD, 0, 0); else #endif PostMessage(lpWav->hwndNotify, WM_WAVIN_STOPRECORD, 0, 0); } else switch (WavInGetState(lpWav->hWavIn)) { case WAVIN_STOPPED: // make sure to close input device when record complete // if (lpWav->dwState & WAVSTATE_AUTOSTOP) { #ifdef MULTITHREAD if (lpWav->dwFlags & WAV_MULTITHREAD) PostThreadMessage(lpWav->dwThreadId, WM_WAVIN_STOPRECORD, 0, 0); else #endif PostMessage(lpWav->hwndNotify, WM_WAVIN_STOPRECORD, 0, 0); break; } // else fall through case WAVIN_RECORDING: // load input device queue with next chunk to record // if (WavRecordNextChunk(WavGetHandle(lpWav)) != 0) fSuccess = TraceFALSE(NULL); break; default: break; } lResult = 0L; #ifdef MULTITHREAD if ((HANDLE) wParam != NULL) SetEventMessageProcessed(lpWav, (HANDLE) wParam); #endif } break; case WM_WAVIN_STOPRECORD: { TracePrintf_0(NULL, 5, TEXT("WM_WAVIN_STOPRECORD\n")); if (WavStopRecord(WavGetHandle(lpWav)) != 0) fSuccess = TraceFALSE(NULL); lResult = 0L; } break; default: lResult = DefWindowProc(hwnd, msg, wParam, lParam); break; } return lResult; } // WavCalcPositionStop - calculate new stop position if stopped // (i) handle returned from WavOpen // (i) new stop position, in bytes // return 0 if success // static int WavCalcPositionStop(HWAV hWav, long cbPosition) { BOOL fSuccess = TRUE; LPWAV lpWav; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // recalculate stop position only if currently stopped // (if playing or recording, defer recalc until stop occurs) // #if 0 else if (WavGetState(hWav) == WAV_STOPPED) #else else if (lpWav->hWavOut == NULL && lpWav->hWavIn == NULL) #endif { // convert byte position to milleseconds // save the new stop position // lpWav->msPositionStop = WavFormatBytesToMilleseconds( lpWav->lpwfx[FORMATFILE], (DWORD) cbPosition); #if 1 TracePrintf_2(NULL, 6, TEXT("lpWav->msPositionStop=%ld, cbPosition=%ld\n"), (long) lpWav->msPositionStop, (long) cbPosition); #endif } return fSuccess ? 0 : -1; } // WavSeekTraceBefore - debug trace output before the seek // (i) pointer to WAV struct // (i) bytes to move pointer // (i) position to move from // return 0 if success // static int WavSeekTraceBefore(LPWAV lpWav, long lOffset, int nOrigin) { BOOL fSuccess = TRUE; TracePrintf_2(NULL, 6, TEXT("WavSeek(..., lOffset=%ld, nOrigin=%d)\n"), (long) lOffset, (int) nOrigin); TracePrintf_4(NULL, 6, TEXT("Before: lpWav->lDataOffset=%ld, lpWav->lDataPos=%ld, lpWav->cbData=%ld, lpWav->msPositionStop=%ld\n"), (long) lpWav->lDataOffset, (long) lpWav->lDataPos, (long) lpWav->cbData, (long) lpWav->msPositionStop); return fSuccess ? 0 : -1; } // WavSeekTraceAfter - debug trace output after the seek // (i) pointer to WAV struct // (i) position returned from mmioSeek // (i) bytes to move pointer // (i) position to move from // return 0 if success // static int WavSeekTraceAfter(LPWAV lpWav, long lPos, long lOffset, int nOrigin) { BOOL fSuccess = TRUE; TracePrintf_3(NULL, 6, TEXT("%ld = mmioSeek(..., lOffset=%ld, nOrigin=%d)\n"), (long) lPos, (long) lOffset, (int) nOrigin); TracePrintf_4(NULL, 6, TEXT("After: lpWav->lDataOffset=%ld, lpWav->lDataPos=%ld, lpWav->cbData=%ld, lpWav->msPositionStop=%ld\n"), (long) lpWav->lDataOffset, (long) lpWav->lDataPos, (long) lpWav->cbData, (long) lpWav->msPositionStop); return fSuccess ? 0 : -1; } // WavGetPtr - verify that wav handle is valid, // (i) handle returned from WavInit // return corresponding wav pointer (NULL if error) // static LPWAV WavGetPtr(HWAV hWav) { BOOL fSuccess = TRUE; LPWAV lpWav; if ((lpWav = (LPWAV) hWav) == NULL) fSuccess = TraceFALSE(NULL); else if (IsBadWritePtr(lpWav, sizeof(WAV))) fSuccess = TraceFALSE(NULL); #ifdef CHECKTASK // make sure current task owns the wav handle // else if (lpWav->hTask != GetCurrentTask()) fSuccess = TraceFALSE(NULL); #endif return fSuccess ? lpWav : NULL; } // WavGetHandle - verify that wav pointer is valid, // (i) pointer to WAV struct // return corresponding wav handle (NULL if error) // static HWAV WavGetHandle(LPWAV lpWav) { BOOL fSuccess = TRUE; HWAV hWav; if ((hWav = (HWAV) lpWav) == NULL) fSuccess = TraceFALSE(NULL); return fSuccess ? hWav : NULL; } // WavInitGetPtr - verify that wavinit handle is valid, // (i) handle returned from WavInitInit // return corresponding wavinit pointer (NULL if error) // static LPWAVINIT WavInitGetPtr(HWAVINIT hWavInit) { BOOL fSuccess = TRUE; LPWAVINIT lpWavInit; if ((lpWavInit = (LPWAVINIT) hWavInit) == NULL) fSuccess = TraceFALSE(NULL); else if (IsBadWritePtr(lpWavInit, sizeof(WAVINIT))) fSuccess = TraceFALSE(NULL); #ifdef CHECKTASK // make sure current task owns the wavinit handle // else if (lpWavInit->hTask != GetCurrentTask()) fSuccess = TraceFALSE(NULL); #endif return fSuccess ? lpWavInit : NULL; } // WavInitGetHandle - verify that wavinit pointer is valid, // (i) pointer to WAVINIT struct // return corresponding wavinit handle (NULL if error) // static HWAVINIT WavInitGetHandle(LPWAVINIT lpWavInit) { BOOL fSuccess = TRUE; HWAVINIT hWavInit; if ((hWavInit = (HWAVINIT) lpWavInit) == NULL) fSuccess = TraceFALSE(NULL); return fSuccess ? hWavInit : NULL; } #ifdef MULTITHREAD static int SetEventMessageProcessed(LPWAV lpWav, HANDLE hEventMessageProcessed) { BOOL fSuccess = TRUE; if (lpWav == NULL) fSuccess = TraceFALSE(NULL); else if (!(lpWav->dwFlags & WAV_MULTITHREAD)) ; // nothing to do else if (hEventMessageProcessed == NULL) fSuccess = TraceFALSE(NULL); // notify SendMessageEx that message has been processed // else if (!SetEvent(hEventMessageProcessed)) fSuccess = TraceFALSE(NULL); return fSuccess ? 0 : -1; } #endif // WavTempStop - stop playback or recording if necessary, save prev state // (i) handle returned from WavInit // (o) return previous state here // (o) return device id here // return 0 if success // static int WavTempStop(HWAV hWav, LPWORD lpwStatePrev, LPINT lpidDevPrev) { BOOL fSuccess = TRUE; LPWAV lpWav; WORD wStatePrev; // // We have to initialize the local variable // int idDevPrev = 0; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // make sure we not playing or recording // $FIXUP - we need to make sure that WAV_PLAYSYNC and // WAV_AUTOCLOSE flags are ignored during this stop // else switch ((wStatePrev = WavGetState(hWav))) { case WAV_PLAYING: if ((idDevPrev = WavOutGetId(lpWav->hWavOut)) < -1) fSuccess = TraceFALSE(NULL); else if (WavStop(hWav) != 0) fSuccess = TraceFALSE(NULL); break; case WAV_RECORDING: if ((idDevPrev = WavInGetId(lpWav->hWavIn)) < -1) fSuccess = TraceFALSE(NULL); else if (WavStop(hWav) != 0) fSuccess = TraceFALSE(NULL); break; default: break; } if (fSuccess) { *lpwStatePrev = wStatePrev; *lpidDevPrev = idDevPrev; } return fSuccess ? 0 : -1; } // WavTempResume - resume playback or recording if necessary, using prev state // (i) handle returned from WavInit // (i) previous state returned from WavTempStop // (i) device id returned from WavTempStop // return 0 if success // static int WavTempResume(HWAV hWav, WORD wStatePrev, int idDevPrev) { BOOL fSuccess = TRUE; LPWAV lpWav; if ((lpWav = WavGetPtr(hWav)) == NULL) fSuccess = TraceFALSE(NULL); // resume playback or recording if necessary // else switch (wStatePrev) { case WAV_PLAYING: if (WavPlay(hWav, idDevPrev, lpWav->dwFlagsPlay) != 0) fSuccess = TraceFALSE(NULL); break; case WAV_RECORDING: if (WavRecord(hWav, idDevPrev, lpWav->dwFlagsRecord) != 0) fSuccess = TraceFALSE(NULL); break; default: break; } return fSuccess ? 0 : -1; }