home *** CD-ROM | disk | FTP | other *** search
Wrap
/************************************************************************** * * 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 (C) 1992 - 1997 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 formtypeWAVE mmioFOURCC('W', 'A', 'V', 'E') #define ckidWAVEFORMAT mmioFOURCC('f', 'm', 't', ' ') #define ckidWAVEDATA mmioFOURCC('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 /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// /* - - - - - - - - */ UINT uUseCount; // the reference count for our objects UINT uLockCount; // 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, REFIID riid, void FAR* FAR* ppv) { IUnknown FAR* pUnknown; LPWAVESTUFF pWaveStuff; HRESULT hresult; // 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( LPUNKNOWN pu, REFIID iid, 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( LPUNKNOWN pu) { // 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( PAVIFILE pf, REFIID iid, 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( PAVIFILE pf) { // 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( PAVIFILE pf) { // 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( LPPERSISTFILE ppf, REFIID iid, 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( LPPERSISTFILE ppf) { // 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( LPPERSISTFILE ppf) { // 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( PAVISTREAM ps, REFIID iid, 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( PAVISTREAM ps) { // 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( PAVISTREAM ps) { // 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); UINT ui; char ach[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 FPSHACK 1000 _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 MMCKINFO ck; MMCKINFO ckRIFF; /* 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( PAVISTREAM ps, LONG lParam1, LONG lParam2) { return ResultFromScode(AVIERR_UNSUPPORTED); } // // The FindSample Method of the Stream interface // STDMETHODIMP_(LONG) WaveStreamFindSample( PAVISTREAM ps, 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( PAVISTREAM ps, 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( PAVISTREAM ps, 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( PAVISTREAM ps, LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG FAR * plBytes, LONG FAR * plSamples) { // Get a pointer to our structure LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps); LONG lSampleSize; LONG lSeek; LONG lRead; // 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 achTemp lpszFileName #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); }