WAVEFILE.C

/************************************************************************** 
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
* PURPOSE.
*
* Copyright 1992 - 1998 Microsoft Corporation. All Rights Reserved.
*
**************************************************************************/
/****************************************************************************
*
* WAVEFILE.C
*
* An implementation in C of an AVI File Handler to read standard windows
* WAV files as if they were an AVI file with one audio stream.
*
***************************************************************************/

#define INC_OLE2
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <ctype.h>
#include <vfw.h>
#include "MulDiv32.h"
#include "wavefile.h"

#define formtypeWAVEmmioFOURCC('W', 'A', 'V', 'E')
#define ckidWAVEFORMATmmioFOURCC('f', 'm', 't', ' ')
#define ckidWAVEDATAmmioFOURCC('d', 'a', 't', 'a')

LPSTR FAR FileName(LPCSTR lszPath);
//
// Function prototypes and Vtbl for the Unknown interface
//
STDMETHODIMP WaveUnknownQueryInterface(LPUNKNOWN pu, REFIID iid, void FAR* FAR* ppv);
STDMETHODIMP_(ULONG) WaveUnknownAddRef(LPUNKNOWN pu);
STDMETHODIMP_(ULONG) WaveUnknownRelease(LPUNKNOWN pu);

IUnknownVtbl UnknownVtbl = {
WaveUnknownQueryInterface,
WaveUnknownAddRef,
WaveUnknownRelease
};

//
// Function prototypes and Vtbl for the AVIFile interface
//
STDMETHODIMP WaveFileQueryInterface(PAVIFILE pf, REFIID iid, void FAR* FAR* ppv);
STDMETHODIMP_(ULONG) WaveFileAddRef(PAVIFILE pf);
STDMETHODIMP_(ULONG) WaveFileRelease(PAVIFILE pf);
STDMETHODIMP WaveFileInfo(PAVIFILE pf, AVIFILEINFOW FAR * pfi, LONG lSize);
STDMETHODIMP WaveFileGetStream(PAVIFILE pf, PAVISTREAM FAR * ppavi, DWORD fccType, LONG lParam);
STDMETHODIMP WaveFileCreateStream(PAVIFILE pf, PAVISTREAM FAR *ppstream, AVISTREAMINFOW FAR *psi);
STDMETHODIMP WaveFileWriteData(PAVIFILE pf, DWORD ckid, LPVOID lpData, LONG cbData);
STDMETHODIMP WaveFileReadData(PAVIFILE pf, DWORD ckid, LPVOID lpData, LONG FAR *lpcbData);
STDMETHODIMP WaveFileEndRecord(PAVIFILE pf);
STDMETHODIMP WaveFileDeleteStream(PAVIFILE pf, DWORD fccType, LONG lParam);


IAVIFileVtbl FileVtbl = {
WaveFileQueryInterface,
WaveFileAddRef,
WaveFileRelease,
WaveFileInfo,
WaveFileGetStream,
WaveFileCreateStream,
WaveFileWriteData,
WaveFileReadData,
WaveFileEndRecord,
WaveFileDeleteStream
};


STDMETHODIMP WavePersistQueryInterface(LPPERSISTFILE pf, REFIID iid, void FAR* FAR* ppv);
STDMETHODIMP_(ULONG) WavePersistAddRef(LPPERSISTFILE pf);
STDMETHODIMP_(ULONG) WavePersistRelease(LPPERSISTFILE pf);
STDMETHODIMP WavePersistGetClassID (LPPERSISTFILE ppf, LPCLSID lpClassID);
STDMETHODIMP WavePersistIsDirty (LPPERSISTFILE ppf);
STDMETHODIMP WavePersistLoad (LPPERSISTFILE ppf,
LPCOLESTR lpszFileName, DWORD grfMode);
STDMETHODIMP WavePersistSave (LPPERSISTFILE ppf,
LPCOLESTR lpszFileName, BOOL fRemember);
STDMETHODIMP WavePersistSaveCompleted (LPPERSISTFILE ppf,
LPCOLESTR lpszFileName);
STDMETHODIMP WavePersistGetCurFile (LPPERSISTFILE ppf,
LPOLESTR FAR * lplpszFileName);


IPersistFileVtbl PersistVtbl = {
WavePersistQueryInterface,
WavePersistAddRef,
WavePersistRelease,
WavePersistGetClassID,
WavePersistIsDirty,
WavePersistLoad,
WavePersistSave,
WavePersistSaveCompleted,
WavePersistGetCurFile
};

//
// Function prototypes and Vtbl for the AVIStream interface
//
STDMETHODIMP WaveStreamQueryInterface(PAVISTREAM ps, REFIID riid, LPVOID FAR* ppvObj);
STDMETHODIMP WaveStreamCreate(PAVISTREAM ps, LONG lParam1, LONG lParam2);
STDMETHODIMP_(ULONG) WaveStreamAddRef(PAVISTREAM ps);
STDMETHODIMP_(ULONG) WaveStreamRelease(PAVISTREAM ps);
STDMETHODIMP WaveStreamInfo(PAVISTREAM ps, AVISTREAMINFOW FAR * psi, LONG lSize);
STDMETHODIMP_(LONG) WaveStreamFindSample(PAVISTREAM ps, LONG lPos, LONG lFlags);
STDMETHODIMP WaveStreamReadFormat(PAVISTREAM ps, LONG lPos, LPVOID lpFormat, LONG FAR *lpcbFormat);
STDMETHODIMP WaveStreamSetFormat(PAVISTREAM ps, LONG lPos, LPVOID lpFormat, LONG cbFormat);
STDMETHODIMP WaveStreamRead(PAVISTREAM ps, LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG FAR * plBytes,LONG FAR * plSamples);
STDMETHODIMP WaveStreamWrite(PAVISTREAM ps, LONG lStart, LONG lSamples, LPVOID lpData, LONG cbData, DWORD dwFlags, LONG FAR *plSampWritten, LONG FAR *plBytesWritten);
STDMETHODIMP WaveStreamDelete(PAVISTREAM ps, LONG lStart, LONG lSamples);
STDMETHODIMP WaveStreamReadData(PAVISTREAM ps, DWORD fcc, LPVOID lp,LONG FAR *lpcb);
STDMETHODIMP WaveStreamWriteData(PAVISTREAM ps, DWORD fcc, LPVOID lp,LONG cb);
STDMETHODIMP WaveStreamSetInfo(PAVISTREAM ps, AVISTREAMINFOW FAR * psi, LONG lSize);

IAVIStreamVtbl StreamVtbl = {
WaveStreamQueryInterface,
WaveStreamAddRef,
WaveStreamRelease,
WaveStreamCreate,
WaveStreamInfo,
WaveStreamFindSample,
WaveStreamReadFormat,
WaveStreamSetFormat,
WaveStreamRead,
WaveStreamWrite,
WaveStreamDelete,
WaveStreamReadData,
WaveStreamWriteData,
WaveStreamSetInfo
};


#if !defined UNICODE

int LoadUnicodeString(HINSTANCE hinst, UINT wID, LPWSTR lpBuffer, int cchBuffer)
{
char ach[128];
int i;

i = LoadString(hinst, wID, ach, sizeof(ach));

if (i > 0)
MultiByteToWideChar(CP_ACP, 0, ach, -1, lpBuffer, cchBuffer);

return i;
}

#else
#define LoadUnicodeString LoadString
#endif


///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

/*--------*/

UINTuUseCount;// the reference count for our objects
UINTuLockCount;// our lock count for LockServer

/*--------*/

//
// Create a new instance. Since this is a C implementation we have to
// allocate space for our structure ourselves.
//
HRESULT WaveFileCreate(
IUnknown FAR*pUnknownOuter,
REFIIDriid,
void FAR* FAR*ppv)
{
IUnknown FAR*pUnknown;
LPWAVESTUFFpWaveStuff;
HRESULThresult;

// Allocate space for our structure
pWaveStuff = (LPWAVESTUFF)GlobalAllocPtr(GMEM_MOVEABLE,
sizeof(WAVESTUFF));
if (!pWaveStuff)
return ResultFromScode(E_OUTOFMEMORY);

// Initialize the Vtbls
pWaveStuff->AVIFile = &FileVtbl;
pWaveStuff->AVIStream = &StreamVtbl;
pWaveStuff->Unknown = &UnknownVtbl;
pWaveStuff->Persist = &PersistVtbl;

// Set up our controlling object
pUnknown = (IUnknown FAR *)&pWaveStuff->Unknown;
if (pUnknownOuter)
pWaveStuff->pUnknownOuter = pUnknownOuter;
else
pWaveStuff->pUnknownOuter =(IUnknown FAR *)&pWaveStuff->Unknown;

// Initial the things in our structure
pWaveStuff->refs = 0;
pWaveStuff->hmmio = NULL;
pWaveStuff->lpFormat = NULL;
pWaveStuff->cbFormat = 0L;
pWaveStuff->fDirty = FALSE;
pWaveStuff->extra.lp = NULL;
pWaveStuff->extra.cb = 0L;

// Call our Query interface to increment our ref count and get a
// pointer to our interface to return.
hresult = pUnknown->lpVtbl->QueryInterface(pUnknown, riid, ppv);

if (FAILED(GetScode(hresult)))
GlobalFreePtr(pWaveStuff);
return hresult;
}

/*--------*/

//
// Query interface from all three interfaces comes here. We support the
// Unknown interface, AVIStream and AVIFile.
//
STDMETHODIMP WaveUnknownQueryInterface(
LPUNKNOWNpu,
REFIIDiid,
void FAR* FAR*ppv)
{
// Get a pointer to our structure
LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_UNKNOWN(pu);

if (IsEqualIID(iid, &IID_IUnknown))
*ppv = (LPVOID)&pWaveStuff->Unknown;
else if (IsEqualIID(iid, &IID_IAVIFile))
*ppv = (LPVOID)&pWaveStuff->AVIFile;
else if (IsEqualIID(iid, &IID_IAVIStream))
*ppv = (LPVOID)&pWaveStuff->AVIStream;
else if (IsEqualIID(iid, &IID_IPersistFile))
*ppv = (LPVOID)&pWaveStuff->Persist;
else
return ResultFromScode(E_NOINTERFACE);
pu->lpVtbl->AddRef(pu);
return NOERROR;
}

/*--------*/

//
// Increase our reference count. AddRef for all three interfaces comes here.
//
STDMETHODIMP_(ULONG) WaveUnknownAddRef(
LPUNKNOWNpu)
{
// Get a pointer to our structure
LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_UNKNOWN(pu);

uUseCount++;
return ++pWaveStuff->refs;
}

/*--------*/

//
// Decrease our reference count. Release for all three interfaces comes here.
//
STDMETHODIMP_(ULONG) WaveUnknownRelease(
LPUNKNOWN pu)
{
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_UNKNOWN(pu);

uUseCount--;

//
// Ref count is zero. Close the file. If we've been writing to it, it's
// clean-up time!
//
if (!--p->refs) {
LONG lRet = AVIERR_OK;

if (p->fDirty) {
MMCKINFO ckRIFF;
MMCKINFO ck;

mmioSeek(p->hmmio, 0, SEEK_SET);

/* create the output file RIFF chunk of form type 'WAVE' */
ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
ckRIFF.cksize = 0L;// let MMIO figure out ck. size
if (mmioCreateChunk(p->hmmio, &ckRIFF, MMIO_CREATERIFF) != 0)
goto ERROR_CANNOT_WRITE;// cannot write file, probably

ck.ckid = mmioFOURCC('f', 'm', 't', ' ');
ck.cksize = p->cbFormat;// we know the size of this ck.
if (mmioCreateChunk(p->hmmio, &ck, 0) != 0)
goto ERROR_CANNOT_WRITE;// cannot write file, probably

if (mmioWrite(p->hmmio, (HPSTR) p->lpFormat, p->cbFormat) != p->cbFormat)
goto ERROR_CANNOT_WRITE;// cannot write file, probably

/* ascend out of the 'fmt' chunk, back into 'RIFF' chunk */
if (mmioAscend(p->hmmio, &ck, 0) != 0)
goto ERROR_CANNOT_WRITE;// cannot write file, probably

// If there was extra stuff here, we need to fill it!
if (mmioSeek(p->hmmio, 0, SEEK_CUR)
+ 2 * (LRESULT)sizeof(DWORD)
!= (LRESULT) p->ckData.dwDataOffset) {
/* create the 'data' chunk that holds the waveform samples */
ck.ckid = mmioFOURCC('J', 'U', 'N', 'K');
ck.cksize = 0;
if (mmioCreateChunk(p->hmmio, &ck, 0) != 0)
goto ERROR_CANNOT_WRITE;// cannot write file, probably

mmioSeek(p->hmmio,
p->ckData.dwDataOffset - 2 * sizeof(DWORD),
SEEK_SET);

if (mmioAscend(p->hmmio, &ck, 0) != 0)
goto ERROR_CANNOT_WRITE;// cannot write file, probably
}

/* create the 'data' chunk that holds the waveform samples */
ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
ck.cksize = p->ckData.cksize;
if (mmioCreateChunk(p->hmmio, &ck, 0) != 0)
goto ERROR_CANNOT_WRITE;// cannot write file, probably

mmioSeek(p->hmmio, p->ckData.cksize, SEEK_CUR);

mmioAscend(p->hmmio, &ck, 0);

if (p->extra.cb) {
if (mmioWrite(p->hmmio, (HPSTR) p->extra.lp, p->extra.cb) != p->extra.cb)
goto ERROR_CANNOT_WRITE;
}

if (mmioAscend(p->hmmio, &ckRIFF, 0) != 0)
goto ERROR_CANNOT_WRITE;

if (mmioFlush(p->hmmio, 0) != 0)
goto ERROR_CANNOT_WRITE;
}


goto success;

ERROR_CANNOT_WRITE:
lRet = AVIERR_FILEWRITE;

success:
if (p->hmmio)
mmioClose(p->hmmio, 0);

if (p->lpFormat)
GlobalFreePtr(p->lpFormat);

// Free the memory for our structure.
GlobalFreePtr(p);
return 0;
}
return p->refs;
}


//
// Use our controlling object to call QueryInterface on Unknown
//
STDMETHODIMP WaveFileQueryInterface(
PAVIFILEpf,
REFIIDiid,
void FAR* FAR*ppv)
{
// Get a pointer to our structure
LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_FILE(pf);

return pWaveStuff->pUnknownOuter->lpVtbl->QueryInterface(
pWaveStuff->pUnknownOuter, iid, ppv);
}

/*--------*/

//
// Use our controlling object to call AddRef on Unknown
//
STDMETHODIMP_(ULONG) WaveFileAddRef(
PAVIFILEpf)
{
// Get a pointer to our structure
LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_FILE(pf);

return pWaveStuff->pUnknownOuter->lpVtbl->AddRef(
pWaveStuff->pUnknownOuter);
}

/*--------*/

//
// Use our controlling object to call Release on Unknown
//
STDMETHODIMP_(ULONG) WaveFileRelease(
PAVIFILEpf)
{
// Get a pointer to our structure
LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_FILE(pf);

return pWaveStuff->pUnknownOuter->lpVtbl->Release(
pWaveStuff->pUnknownOuter);
}

/*--------*/


//
// Use our controlling object to call QueryInterface on Unknown
//
STDMETHODIMP WavePersistQueryInterface(
LPPERSISTFILEppf,
REFIIDiid,
void FAR* FAR*ppv)
{
// Get a pointer to our structure
LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_PERSIST(ppf);

return pWaveStuff->pUnknownOuter->lpVtbl->QueryInterface(
pWaveStuff->pUnknownOuter, iid, ppv);
}

/*--------*/

//
// Use our controlling object to call AddRef on Unknown
//
STDMETHODIMP_(ULONG) WavePersistAddRef(
LPPERSISTFILEppf)
{
// Get a pointer to our structure
LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_PERSIST(ppf);

return pWaveStuff->pUnknownOuter->lpVtbl->AddRef(
pWaveStuff->pUnknownOuter);
}

/*--------*/

//
// Use our controlling object to call Release on Unknown
//
STDMETHODIMP_(ULONG) WavePersistRelease(
LPPERSISTFILEppf)
{
// Get a pointer to our structure
LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_PERSIST(ppf);

return pWaveStuff->pUnknownOuter->lpVtbl->Release(
pWaveStuff->pUnknownOuter);
}

/*--------*/



//
// Use our controlling object to call QueryInterface on Unknown
//
STDMETHODIMP WaveStreamQueryInterface(
PAVISTREAMps,
REFIIDiid,
void FAR* FAR*ppv)
{
// Get a pointer to our structure
LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_STREAM(ps);

return pWaveStuff->pUnknownOuter->lpVtbl->QueryInterface(
pWaveStuff->pUnknownOuter, iid, ppv);
}

/*--------*/

//
// Use our controlling object to call AddRef on Unknown
//
STDMETHODIMP_(ULONG) WaveStreamAddRef(
PAVISTREAMps)
{
// Get a pointer to our structure
LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_STREAM(ps);

return pWaveStuff->pUnknownOuter->lpVtbl->AddRef(
pWaveStuff->pUnknownOuter);
}

/*--------*/

//
// Use our controlling object to call Release on Unknown
//
STDMETHODIMP_(ULONG) WaveStreamRelease(
PAVISTREAMps)
{
// Get a pointer to our structure
LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_STREAM(ps);

return pWaveStuff->pUnknownOuter->lpVtbl->Release(
pWaveStuff->pUnknownOuter);
}

/*--------*/

#define SLASH(c)((c) == '/' || (c) == '\\')

/*--------------------------------------------------------------+
| FileName - return a pointer to the filename part of szPath |
| with no preceding path. |
+--------------------------------------------------------------*/
LPSTR FAR FileName(
LPCSTR lszPath)
{
LPCSTR lszCur;

for (lszCur = lszPath + lstrlen(lszPath); lszCur > lszPath && !SLASH(*lszCur) && *lszCur != ':';)
lszCur = AnsiPrev(lszPath, lszCur);
if (lszCur == lszPath)
return (LPSTR)lszCur;
else
return (LPSTR)(lszCur + 1);
}

/*--------*/

//
// The Open Method for our File interface - Open a WAVE file
//
STDMETHODIMP WaveFileOpen(
PAVIFILE pf,
LPCSTR szFile,
UINT mode)
{
LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
UINTui;
charach[80];

// !!! Assumptions about the AVIFILE.DLL (which calls us):
// We will only see READWRITE mode, never only WRITE mode.

//
// try to open the actual file, first with share, then without.
// You may need to use specific flags in order to open a file
// that's already open by somebody else.
//

// If the first attempt fails, no system error box, please.
ui = SetErrorMode(SEM_NOOPENFILEERRORBOX);
p->hmmio = mmioOpen((LPSTR) szFile, NULL, MMIO_ALLOCBUF | mode);
if (!p->hmmio && ((mode & MMIO_RWMODE) == OF_READ)) {
// if the open fails, try again without the share flags.
mode &= ~(MMIO_SHAREMODE);
p->hmmio = mmioOpen((LPSTR) szFile, NULL, MMIO_ALLOCBUF | mode);
}
SetErrorMode(ui);

//
// Now set up our structure
//

p->mode = mode;

if (!p->hmmio)
goto error;

_fmemset(&p->avistream, 0, sizeof(p->avistream));

// If this is defined, we pretend that the data is at FPSHACK "frames"
// per second in the main header, otherwise we use the sample
// rate of the audio, which looks somewhat strange in MPlayer.
#define FPSHACK1000

_fmemset(&p->avihdr, 0, sizeof(p->avihdr));

#ifdef FPSHACK
//
// Initialize our AVIFILEHEADER
//
p->avihdr.dwRate = FPSHACK;
p->avihdr.dwScale = 1;
#endif

p->avihdr.dwStreams = 1;
LoadUnicodeString(ghModule, IDS_FILETYPE, p->avihdr.szFileType,
sizeof(p->avihdr.szFileType));

//
// Initialize our AVISTREAMHEADER
//
LoadString(ghModule, IDS_STREAMNAME, ach, sizeof(ach));
#if !defined UNICODE
{
char achTemp[64];

wsprintf(achTemp, ach, FileName(szFile));

MultiByteToWideChar(CP_ACP, 0, achTemp, -1,
p->avistream.szName, 64);
}
#else
wsprintf(p->avistream.szName, ach, FileName(szFile));
#endif

if (mode & OF_CREATE) {// Brand new file
p->avistream.fccType = streamtypeAUDIO;
p->avistream.fccHandler = 0;
p->avistream.dwFlags = 0;
p->avistream.wPriority = 0;
p->avistream.wLanguage = 0;
p->avistream.dwInitialFrames = 0;
p->avistream.dwScale = 0;
p->avistream.dwRate = 0;
p->avistream.dwStart = 0;
p->avistream.dwLength = 0;
p->avistream.dwSuggestedBufferSize = 0;
p->avistream.dwSampleSize = 0;

p->fDirty = TRUE;

} else {// read the existing file to get info

MMCKINFOck;
MMCKINFOckRIFF;
/* Read RIFF chunk */
if (mmioDescend(p->hmmio, &ckRIFF, NULL, 0) != 0)
goto error;

if (ckRIFF.ckid != FOURCC_RIFF)
goto error;

if (ckRIFF.fccType != formtypeWAVE)
goto error;

/* Read WAVE format chunk */
ck.ckid = ckidWAVEFORMAT;
if (FindChunkAndKeepExtras(&p->extra, p->hmmio, &ck, &ckRIFF, MMIO_FINDCHUNK))
goto error;

p->cbFormat = ck.cksize;
p->lpFormat = (LPWAVEFORMAT) GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize);

if (p->lpFormat == NULL)
goto error;

if (mmioRead(p->hmmio,
(HPSTR) p->lpFormat,
(LONG)ck.cksize) != (LONG)ck.cksize)
goto error;


/* Ascend out of stream header */
if (mmioAscend(p->hmmio, &ck, 0) != 0)
goto error;

/* Find big data chunk */
p->ckData.ckid = ckidWAVEDATA;
if (FindChunkAndKeepExtras(&p->extra, p->hmmio, &p->ckData, &ckRIFF, MMIO_FINDCHUNK))
goto error;

p->fDirty = FALSE;

p->avistream.fccType = streamtypeAUDIO;
p->avistream.fccHandler = 0;
p->avistream.dwFlags = 0;
p->avistream.wPriority = 0;
p->avistream.wLanguage = 0;
p->avistream.dwInitialFrames = 0;
p->avistream.dwScale = p->lpFormat->nBlockAlign;
p->avistream.dwRate = p->lpFormat->nAvgBytesPerSec;
p->avistream.dwStart = 0;
p->avistream.dwLength = p->ckData.cksize / p->lpFormat->nBlockAlign;
p->avistream.dwSuggestedBufferSize = 0;
p->avistream.dwSampleSize = p->lpFormat->nBlockAlign;

#ifdef FPSHACK
p->avihdr.dwLength = MulDiv32(p->avistream.dwLength,
p->avistream.dwScale * FPSHACK,
p->avistream.dwRate);
#else
p->avihdr.dwScale = 1;
p->avihdr.dwRate = p->lpFormat->nSamplesPerSec;
p->avihdr.dwLength = MulDiv32(p->ckData.cksize,
p->lpFormat->nSamplesPerSec,
p->lpFormat->nAvgBytesPerSec);
#endif


mmioAscend(p->hmmio, &p->ckData, 0);

// Read extra data at end of file....
if (FindChunkAndKeepExtras(&p->extra, p->hmmio, &ckRIFF, &ck, 0)
!= AVIERR_OK)
goto error;
}

//
// all done return success.
//
return ResultFromScode(0); // success

error:
return ResultFromScode(AVIERR_FILEREAD);
}


//
// Get a stream from the file... Each WAVE file has exactly 1 audio stream.
//
STDMETHODIMP WaveFileGetStream(
PAVIFILE pf,
PAVISTREAM FAR * ppavi,
DWORD fccType,
LONG lParam)
{
int iStreamWant;
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);

iStreamWant = (int)lParam;

if (p->lpFormat == NULL)
return ResultFromScode(AVIERR_BADPARAM);

// We only support one stream
if (iStreamWant != 0)
return ResultFromScode(AVIERR_BADPARAM);

// We only support audio streams
if (fccType && fccType != streamtypeAUDIO)
return ResultFromScode(AVIERR_BADPARAM);

// increase the reference count
p->AVIStream->AddRef((PAVISTREAM)&p->AVIStream);

// Return a pointer to our stream Vtbl
*ppavi = (PAVISTREAM) &(p->AVIStream);
return ResultFromScode(AVIERR_OK);
}


STDMETHODIMP WaveFileDeleteStream(PAVIFILE pf, DWORD fccType, LONG lParam)
{
int iStreamWant;
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);

iStreamWant = (int)lParam;

if (p->lpFormat == NULL)
return ResultFromScode(AVIERR_BADPARAM);

// We only support one stream
if (iStreamWant != 0)
return ResultFromScode(AVIERR_BADPARAM);

// We only support audio streams
if (fccType && fccType != streamtypeAUDIO)
return ResultFromScode(AVIERR_BADPARAM);


GlobalFreePtr(p->lpFormat);
p->lpFormat = NULL;

return NOERROR;
}

//
// We don't support the Save Method of the File Interface (We don't save)
//
STDMETHODIMP WaveFileSave(
PAVIFILE pf,
LPCSTR szFile,
AVICOMPRESSOPTIONS FAR *lpOptions,
AVISAVECALLBACK lpfnCallback)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}

//
// Method to create a stream in a WAVE file. We only support this for blank
// WAVE files.
//
STDMETHODIMP WaveFileCreateStream(
PAVIFILE pf,
PAVISTREAM FAR *ppstream,
AVISTREAMINFOW FAR *psi)
{
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);

// We can't add a second stream to a file
if (p->lpFormat)
return ResultFromScode(AVIERR_UNSUPPORTED);

// We only like audio....
if (psi->fccType != streamtypeAUDIO)
return ResultFromScode(AVIERR_UNSUPPORTED);

// Increase our reference count.
p->AVIStream->AddRef((PAVISTREAM)&p->AVIStream);

p->cbFormat = 0;
p->lpFormat = NULL;

// Return a pointer to our stream Vtbl.
*ppstream = (PAVISTREAM) &(p->AVIStream);

return ResultFromScode(AVIERR_OK);
}

//
// The WriteData Method of the File interface
//
STDMETHODIMP WaveFileWriteData(
PAVIFILE pf,
DWORD ckid,
LPVOID lpData,
LONG cbData)
{
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);

// Write the data in the Wave File.
return ResultFromScode(WriteExtra(&p->extra, ckid, lpData, cbData));
}

//
// The ReadData Method of the File interface
//
STDMETHODIMP WaveFileReadData(
PAVIFILE pf,
DWORD ckid,
LPVOID lpData,
LONG FAR *lpcbData)
{
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);

// Read the data from the file
return ResultFromScode(ReadExtra(&p->extra, ckid, lpData, lpcbData));
}

//
// The EndRecord Method of the File interface.. this doesn't need to do
// anything.. (no concept of interleaving or packaging streams)
//
STDMETHODIMP WaveFileEndRecord(
PAVIFILE pf)
{
return ResultFromScode(AVIERR_OK);
}


//
// The Info Method of the File interface
//
STDMETHODIMP WaveFileInfo(
PAVIFILE pf,
AVIFILEINFOW FAR * pfi,
LONG lSize)
{
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);

// Return an AVIFILEHEADER.
hmemcpy(pfi, &p->avihdr, min(lSize, sizeof(p->avihdr)));
return 0;
}



//
// The Create Method of the Stream interface. We can't create streams that
// aren't attached to the file.
//
STDMETHODIMP WaveStreamCreate(
PAVISTREAMps,
LONG lParam1,
LONG lParam2)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}


//
// The FindSample Method of the Stream interface
//
STDMETHODIMP_(LONG) WaveStreamFindSample(
PAVISTREAMps,
LONG lPos, LONG lFlags)
{
if (lFlags & FIND_FORMAT) {
if ((lFlags & FIND_NEXT) && lPos > 0)
return -1;
else
return 0;
}

return lPos;
}


//
// The ReadFormat Method of the Stream interface
//
STDMETHODIMP WaveStreamReadFormat(
PAVISTREAMps,
LONG lPos,
LPVOID lpFormat,
LONG FAR *lpcbFormat)
{
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);

// No buffer to fill in, this means return the size needed.
if (lpFormat == NULL || *lpcbFormat == 0) {
*lpcbFormat = p->cbFormat;
return 0;
}

// Give them the WAVE format.
hmemcpy(lpFormat, p->lpFormat, min(*lpcbFormat, p->cbFormat));

// Our buffer is too small
if (*lpcbFormat < p->cbFormat)
return ResultFromScode(AVIERR_BUFFERTOOSMALL);

*lpcbFormat = p->cbFormat;

return 0;
}

//
// The Info Method of the Stream interface
//
STDMETHODIMP WaveStreamInfo(
PAVISTREAMps,
AVISTREAMINFOW FAR * psi,
LONG lSize)
{
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);

// give them an AVISTREAMINFO
hmemcpy(psi, &p->avistream, min(lSize, sizeof(p->avistream)));
return 0;
}


STDMETHODIMP WaveStreamSetInfo(PAVISTREAM ps, AVISTREAMINFOW FAR * psi, LONG lSize)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}

///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

/*
invalid lPos return error

if lPos + lSamples is invalid trim lSamples to fit.

lpBuffer == NULL

cbBuffer == 0 && lSamples > 0
return size of lSamples sample.
else
return the exactly the number of bytes and sample
you would have read if lpBuffer was not zero.

NOTE return means fill in *plBytes and *plSamples.

lpBuffer != NULL

lSamples == -1 read convenient amount (just fill buffer)
lSamples == 0 fill buffer with as many samples that will fit.
lSamples > 0 read lSamples (or as much will fit in cbBuffer)

fill in *plBytes with bytes actualy read
fill in *plSamples with samples actualy read

*/

//
// The Read Method for the Stream Interface - Read some wave data
STDMETHODIMP WaveStreamRead(
PAVISTREAMps,
LONGlStart,
LONGlSamples,
LPVOIDlpBuffer,
LONGcbBuffer,
LONG FAR *plBytes,
LONG FAR *plSamples)
{
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);

LONGlSampleSize;
LONGlSeek;
LONGlRead;

// Invalid position
if (lStart < 0 || lStart > (LONG) p->avistream.dwLength) {
ack:
if (plBytes)
*plBytes = 0;
if (plSamples)
*plSamples = 0;
return 0;
}

// Can't read quite this much data
if (lSamples + lStart > (LONG) p->avistream.dwLength)
lSamples = p->avistream.dwLength - lStart;

lSampleSize = p->avistream.dwSampleSize;

// We have fixed-length samples

if (lpBuffer == NULL) {
if (cbBuffer > 0 && lSamples > 0)
// Trim how many samples we'd really be able to read
lSamples = min(lSamples, cbBuffer / lSampleSize);
else if (lSamples <= 0)
// Use as many as will fit
lSamples = cbBuffer / lSampleSize;
} else {
if (lSamples > 0)
// Trim how many samples we'd really be able to read
lSamples = min(lSamples, cbBuffer / lSampleSize);
else
// Use as many as will fit
lSamples = cbBuffer / lSampleSize;
}

//
// a NULL buffer means return the size buffer needed to read
// the given sample.
//
if (lpBuffer == NULL || cbBuffer == 0) {
if (plBytes)
*plBytes = lSamples * lSampleSize;;
if (plSamples)
*plSamples = lSamples;
return 0;
}

// Buffer too small!
if (cbBuffer < lSampleSize)
goto ack;

// Seek and read

cbBuffer = lSamples * lSampleSize;

lSeek = p->ckData.dwDataOffset + lSampleSize * lStart;
lRead = lSamples * lSampleSize;

if (mmioSeek(p->hmmio, lSeek, SEEK_SET) != lSeek)
goto ack;

if (mmioRead(p->hmmio, (HPSTR) lpBuffer, lRead) != lRead)
goto ack;

//
// success return number of bytes and number of samples read
//
if (plBytes)
*plBytes = lRead;

if (plSamples)
*plSamples = lSamples;

return ResultFromScode(AVIERR_OK);
}


//
// The SetFormat Method of the Stream interface- called on an empty WAVE file
// before writing data to it.
//
STDMETHODIMP WaveStreamSetFormat(
PAVISTREAM ps,
LONG lPos,
LPVOID lpFormat,
LONG cbFormat)
{
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);


// We can only do this to an empty wave file
if (p->lpFormat) {
if (cbFormat != p->cbFormat ||
_fmemcmp(lpFormat, p->lpFormat, (int) cbFormat))
return ResultFromScode(AVIERR_UNSUPPORTED);

return NOERROR;
}

// Go ahead and set the format!

p->cbFormat = cbFormat;
p->lpFormat = (LPWAVEFORMAT) GlobalAllocPtr(GMEM_MOVEABLE, cbFormat);

if (p->lpFormat == NULL)
return ResultFromScode(AVIERR_MEMORY);

hmemcpy(p->lpFormat, lpFormat, cbFormat);

p->ckData.dwDataOffset = cbFormat + 7 * sizeof(DWORD);
p->ckData.cksize = 0;
p->avistream.dwScale = p->lpFormat->nBlockAlign;
p->avistream.dwRate = p->lpFormat->nAvgBytesPerSec;
p->avistream.dwLength = 0;
p->avistream.dwSampleSize = p->lpFormat->nBlockAlign;

#ifndef FPSHACK
p->avihdr.dwScale = 1;
p->avihdr.dwRate = p->lpFormat->nSamplesPerSec;
#endif
return ResultFromScode(AVIERR_OK);
}

//
// The Write Method of the Stream interface - write some wave data
//
STDMETHODIMP WaveStreamWrite(
PAVISTREAM ps,
LONG lStart,
LONG lSamples,
LPVOID lpData,
LONG cbData,
DWORD dwFlags,
LONG FAR *plSampWritten,
LONG FAR *plBytesWritten)
{
// Get a pointer to our structure
LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);

if ((p->mode & (OF_WRITE | OF_READWRITE)) == 0)
return ResultFromScode(AVIERR_READONLY);

// < 0 means "at end"
if (lStart < 0)
// !!!
lStart = p->avistream.dwStart + p->avistream.dwLength;

p->fDirty = TRUE;

mmioSeek(p->hmmio,
p->ckData.dwDataOffset +
lStart * p->avistream.dwSampleSize,
SEEK_SET);

if (mmioWrite(p->hmmio, (HPSTR) lpData, cbData) != cbData)
return ResultFromScode(AVIERR_FILEWRITE);

p->avistream.dwLength = max((LONG) p->avistream.dwLength,
lStart + lSamples);

p->ckData.cksize = max(p->ckData.cksize,
lStart * p->avistream.dwSampleSize + cbData);

#ifdef FPSHACK
p->avihdr.dwLength = MulDiv32(p->avistream.dwLength * FPSHACK,
p->avistream.dwScale,
p->avistream.dwRate);
#else
p->avihdr.dwLength = MulDiv32(p->ckData.cksize,
p->lpFormat->nSamplesPerSec,
p->lpFormat->nAvgBytesPerSec);
#endif


if (plSampWritten)
*plSampWritten = lSamples;

if (plBytesWritten)
*plBytesWritten = cbData;

return ResultFromScode(AVIERR_OK);
}

//
// The Delete Method of the Stream interface - we don't cut from wave files
//
STDMETHODIMP WaveStreamDelete(
PAVISTREAM ps,
LONG lStart,
LONG lSamples)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}


//
// We also don't support ReadData and WriteData for the Stream Interface
//

STDMETHODIMP WaveStreamReadData(
PAVISTREAM ps,
DWORD fcc,
LPVOID lp,
LONG FAR *lpcb)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}

STDMETHODIMP WaveStreamWriteData(
PAVISTREAM ps,
DWORD fcc,
LPVOID lp,
LONG cb)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}


STDMETHODIMP WaveFileReserved(
PAVIFILE pf)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}

STDMETHODIMP WaveStreamReserved(
PAVISTREAM ps)
{
return ResultFromScode(AVIERR_UNSUPPORTED);
}

/* - - - - - - - - */

// *** IPersist methods ***
STDMETHODIMP WavePersistGetClassID (LPPERSISTFILE ppf, LPCLSID lpClassID)
{
// Get a pointer to our structure
LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);

hmemcpy(lpClassID, &CLSID_AVIWaveFileReader, sizeof(CLSID));
return NOERROR;
}

// *** IPersistFile methods ***
STDMETHODIMP WavePersistIsDirty (LPPERSISTFILE ppf)
{
// Get a pointer to our structure
LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);

return pfile->fDirty ? NOERROR : ResultFromScode(S_FALSE);
}

STDMETHODIMP WavePersistLoad (LPPERSISTFILE ppf,
LPCOLESTR lpszFileName, DWORD grfMode)
{
// Get a pointer to our structure
LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);


#if !defined UNICODE
char achTemp[256];

// Internally, we're using ANSI, but this interface is defined
// to always accept UNICODE under WIN32, so we have to convert.
WideCharToMultiByte(CP_ACP, 0, lpszFileName, -1,
achTemp, sizeof(achTemp), NULL, NULL);
#else
#define achTemplpszFileName
#endif

return WaveFileOpen((PAVIFILE) &pfile->AVIFile, achTemp, (UINT) grfMode);
}

STDMETHODIMP WavePersistSave (LPPERSISTFILE ppf,
LPCOLESTR lpszFileName, BOOL fRemember)
{
// Get a pointer to our structure
LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);


return ResultFromScode(E_FAIL);
}

STDMETHODIMP WavePersistSaveCompleted (LPPERSISTFILE ppf,
LPCOLESTR lpszFileName)
{
// Get a pointer to our structure
LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);


return NOERROR;
}

STDMETHODIMP WavePersistGetCurFile (LPPERSISTFILE ppf,
LPOLESTR FAR * lplpszFileName)
{
// Get a pointer to our structure
LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);

return ResultFromScode(E_FAIL);
}