Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5308 lines
135 KiB

/////////////////////////////////////////////////////////////////////////////////////////
//
// 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 <stdlib.h>
#include "wav.h"
#ifdef MULTITHREAD
#include <objbase.h>
#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
// <dwVersion> (i) must be WAV_VERSION
// <hInst> (i) instance handle of calling module
// <dwFlags> (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
// <hWavInit> (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
// <dwVersion> (i) must be WAV_VERSION
// <hInst> (i) instance handle of calling module
// <lpszFileName> (i) name of file to open or create
// <lpwfx> (i) wave format
// NULL use format from header or default
// <lpIOProc> (i) address of i/o procedure to use
// NULL use default i/o procedure
// <lpadwInfo> (i) data to pass to i/o procedure during open
// NULL no data to pass
// <dwFlags> (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 <lpszFileName> points to memory block
// WAV_RESOURCE <lpszFileName> 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 <dwFlags>, then the
// <lpwfx> parameter must be specified. If <lpwfx> 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 <dwFlags>, then <lpszFileName>
// must point to a WAVE resource in the module specified by <hInst>.
// 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 <dwFlags>, then <lpszFileName>
// must be a pointer to a memory block obtained by calling MemAlloc().
//
// NOTE: if <lpIOProc> is not NULL, this i/o procedure will be called
// for opening, closing, reading, writing, and seeking the wav file.
// If <lpadwInfo> 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 <lpIOProc> 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
{
// <lpszFileName> 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
// <hWav> (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
// <hWav> (i) handle returned from WavOpen
// <idDev> (i) wav output device id
// -1 use any suitable output device
// <lpfnPlayStopped> (i) function to call when play is stopped
// NULL do not notify
// <hUserPlayStopped> (i) param to pass to lpfnPlayStopped
// <dwReserved> (i) reserved; must be zero
// <dwFlags> (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 <dwFlags>, and the device specified
// by <idDev> 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 <dwFlags>, 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 <dwFlags>, 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 <hWav> 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
// <hWav> (i) handle returned from WavOpen
// <idDev> (i) wav input device id
// -1 use any suitable input device
// <lpfnRecordStopped> (i) function to call when record is stopped
// NULL do not notify
// <dwUserRecordStopped> (i) param to pass to lpfnRecordStopped
// <msMaxSize> (i) stop recording if file reaches this size
// 0 no maximum size
// <dwFlags> (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 <dwFlags>, and the device specified
// by <idDev> 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
// <hWav> (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
// <hWav> (i) handle returned from WavOpen
// <hpBuf> (o) buffer to contain bytes read
// <sizBuf> (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 <sizBuf> if data
// decompression is performed by the wav file's I/O procedure. See the
// <lpIOProc> 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
// <hWav> (i) handle returned from WavOpen
// <hpBuf> (i) buffer containing bytes to write
// <sizBuf> (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 <sizBuf> if data
// compression is performed by the wav file's I/O procedure. See the
// <lpIOProc> 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
// <hWav> (i) handle returned from WavOpen
// <lOffset> (i) bytes to move pointer
// <nOrigin> (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
// <hWav> (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
// <hWav> (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
// <hWav> (i) handle returned from WavOpen
// <msLength> (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 <msLength>, 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 <msLength> to byte offset in file
//
lpWav->cbData = WavFormatMillesecondsToBytes(
lpWav->lpwfx[FORMATFILE], (DWORD) msLength);
// $FIXUP - add <nRound> 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
// <hWav> (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
// <hWav> (i) handle returned from WavOpen
// <msPosition> (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 <msPosition> 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
// <hWav> (i) handle returned from WavOpen
// <dwFlags> (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
// <hWav> (i) handle returned from WavOpen
// <lpwfx> (i) wav format
// <dwFlags> (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
// <hWav> (i) handle returned from WavOpen
// <hwndOwner> (i) owner of dialog box
// NULL no owner
// <lpszTitle> (i) title of the dialog box
// NULL use default title ("Sound Selection")
// <dwFlags> (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
// <hWav> (i) handle returned from WavOpen
// <idDev> (i) wav output device id
// -1 use any suitable output device
// <dwFlags> (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
// <hWav> (i) handle returned from WavOpen
// <idDev> (i) wav output device id
// -1 use any suitable output device
// <nLevel> (i) volume level
// 0 minimum volume
// 100 maximum volume
// <dwFlags> (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
// <hWav> (i) handle returned from WavOpen
// <idDev> (i) wav output device id
// -1 any suitable output device
// <nLevel> (i) volume level
// 0 minimum volume
// 100 maximum volume
// <dwFlags> (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
// <hWav> (i) handle returned from WavOpen
// <idDev> (i) wav output device id
// -1 use any suitable output device
// <dwFlags> (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
// <hWav> (i) handle returned from WavOpen
// <idDev> (i) wav output device id
// -1 use any suitable output device
// <nLevel> (i) speed level
// 50 half speed
// 100 normal speed
// 200 double speed, etc.
// <dwFlags> (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
// <hWav> (i) handle returned from WavOpen
// <idDev> (i) wav output device id
// -1 any suitable output device
// <nLevel> (i) speed level
// 50 half speed
// 100 normal speed
// 200 double speed, etc.
// <dwFlags> (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
// <hWav> (i) handle returned from WavOpen
// NULL get default chunk count and size
// <lpcChunks> (o) buffer to hold chunk count
// NULL do not get chunk count
// <lpmsChunkSize> (o) buffer to hold chunk size
// NULL do not get chunk size
// <fWavOut> (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
// <hWav> (i) handle returned from WavOpen
// NULL set default chunk count and size
// <cChunks> (i) number of chunks in device queue
// -1 do not set chunk count
// <msChunkSize> (i) chunk size in milleseconds
// -1 do not set chunk size
// <fWavOut> (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
// <lpwfx> (i) wav format
// <msPlayChunkSize> (i) chunk size in milleseconds
// -1 default chunk size
// <fWavOut> (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
// <hWavSrc> (i) source handle returned from WavOpen
// <hWavDst> (i) destination handle returned from WavOpen
// <hpBuf> (o) pointer to copy buffer
// NULL allocate buffer internally
// <sizBuf> (i) size of copy buffer
// -1 default buffer size (16K)
// <lpfnUserAbort> (i) function that returns TRUE if user aborts
// NULL don't check for user abort
// <dwUser> (i) parameter passed to <lpfnUserAbort>
// <dwFlags> (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
// <hWav> (i) handle returned from WavOpen
// <hpBufSpeed> (o) buffer to contain bytes read
// <sizBufSpeed> (i) size of buffer in bytes
// return bytes formatted for speed in <hpBuf> (-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
// <hWav> (i) handle returned from WavOpen
// <hpBufPlay> (o) buffer to contain bytes read
// <sizBufPlay> (i) size of buffer in bytes
// return bytes formatted for playback in <hpBuf> (-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
// <hWav> (i) handle returned from WavOpen
// <hpBufRecord> (i) buffer containing bytes in record format
// <sizBufRecord> (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
// <hWav> (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
// <hWav> (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
// <dwVersion> (i) must be WAV_VERSION
// <hInst> (i) instance handle of calling module
// <idDev> (i) wav output device id
// -1 use any suitable output device
// <lpszFileName> (i) name of file to play
// NULL stop playing current sound, if any
// <lpwfx> (i) wave format
// NULL use format from header or default
// <lpIOProc> (i) address of i/o procedure to use
// NULL use default i/o procedure
// <lpadwInfo> (i) data to pass to i/o procedure during open
// NULL no data to pass
// <dwFlags> (i) control flags
// WAV_ASYNC return when playback starts (default)
// WAV_SYNC return after playback completes
// WAV_FILENAME <lpszFileName> points to a filename
// WAV_RESOURCE <lpszFileName> points to a resource
// WAV_MEMORY <lpszFileName> 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 <dwFlags>, then the
// <lpwfx> parameter must be specified. If <lpwfx> 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 <dwFlags>, then <lpszFileName>
// must point to a file name.
//
// NOTE: if WAV_RESOURCE is specified in <dwFlags>, then <lpszFileName>
// must point to a WAVE resource in the module specified by <hInst>.
// 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 <dwFlags>, then <lpszFileName>
// 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 <dwFlags>, the [sounds] section of win.ini or the registry is
// searched for an entry matching <lpszFileName>. If no matching entry
// is found, <lpszFileName> is assumed to be a file name.
//
// NOTE: if WAV_NODEFAULT is specified in <dwFlags>, 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
// <lpszFileName> is not found.
//
// NOTE: if WAV_LOOP is specified in <dwFlags>, the sound specified in
// <lpszFileName> 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 <dwFlags>, and the device specified
// by <idDev> 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 <lpIOProc> is not NULL, this i/o procedure will be called
// for opening, closing, reading, writing, and seeking the wav file.
// If <lpadwInfo> 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 <lpIOProc> 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
// <hWav> (i) handle returned from WavOpen
// <wMsg> (i) user-defined message id
// <lParam1> (i) parameter for the message
// <lParam2> (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
// <dwVersion> (i) must be WAV_VERSION
// <hInst> (i) instance handle of calling module
// <lpszFileName> (i) name of file to open
// <dwReserved> (i) reserved; must be zero
// <dwFlagsOpen> (i) control flags to pass to WavOpen
// <dwFlagsEx> (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
// <hWav> (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
// <hWav> (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
// <idDev> (i) wav output device id
// -1 use any suitable output device
// <dwFlags> (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
// <idDev> (i) wav input device id
// -1 use any suitable input device
// <dwFlags> (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
// <idDev> (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
// <idDev> (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
// <hWav> (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
// <hWav> (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
// <hWav> (i) handle returned from WavOpen
// <cbPosition> (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
// <lpWav> (i) pointer to WAV struct
// <lOffset> (i) bytes to move pointer
// <nOrigin> (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
// <lpWav> (i) pointer to WAV struct
// <lPos> (i) position returned from mmioSeek
// <lOffset> (i) bytes to move pointer
// <nOrigin> (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,
// <hWav> (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,
// <lpWav> (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,
// <hWavInit> (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,
// <lpWavInit> (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
// <hWav> (i) handle returned from WavInit
// <lpwStatePrev> (o) return previous state here
// <lpidDevPrev> (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
// <hWav> (i) handle returned from WavInit
// <wStatePrev> (i) previous state returned from WavTempStop
// <idDevPrev> (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;
}