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.
1548 lines
40 KiB
1548 lines
40 KiB
/****************************************************************************
|
|
*
|
|
* 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.
|
|
*
|
|
***************************************************************************/
|
|
/**************************************************************************
|
|
*
|
|
* 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 - 1995 Microsoft Corporation. All Rights Reserved.
|
|
*
|
|
**************************************************************************/
|
|
|
|
#include <win32.h>
|
|
#ifndef _WIN32
|
|
#include <ole2.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <malloc.h>
|
|
#include <ctype.h>
|
|
#include <vfw.h>
|
|
#include "extra.h"
|
|
#include "wavefile.h"
|
|
// First, override the definition in media\inc\win32.h that causes strsafe to not work on Win64
|
|
#ifndef _X86_
|
|
#undef __inline
|
|
#endif // _X86_
|
|
// Then, include strsafe.h
|
|
#include <strsafe.h>
|
|
|
|
#define formtypeWAVE mmioFOURCC('W', 'A', 'V', 'E')
|
|
#define ckidWAVEFORMAT mmioFOURCC('f', 'm', 't', ' ')
|
|
#define ckidWAVEDATA mmioFOURCC('d', 'a', 't', 'a')
|
|
|
|
#ifndef _WIN32
|
|
#define LPCOLESTR LPCSTR
|
|
#define LPOLESTR LPSTR
|
|
#endif
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
/*
|
|
** This implementation of a file handler is done in C, not C++, so a few
|
|
** things work differently than in C++. Our structure contains Vtbls
|
|
** (pointer to function tables) for three interfaces... Unknown, AVIStream,
|
|
** and AVIFile, as well as our private data we need to implement the
|
|
** handler.
|
|
**
|
|
*/
|
|
|
|
IAVIStreamVtbl FAR *AVIStream;
|
|
IAVIFileVtbl FAR *AVIFile;
|
|
IUnknownVtbl FAR *Unknown;
|
|
IPersistFileVtbl FAR *Persist;
|
|
|
|
// This is our controlling object.
|
|
IUnknown FAR* pUnknownOuter;
|
|
|
|
//
|
|
// WaveFile instance data
|
|
//
|
|
HSHFILE hshfile; // file I/O
|
|
|
|
MMCKINFO ckData;
|
|
|
|
LONG refs; // for UNKNOWN
|
|
AVISTREAMINFOW avistream; // for STREAM
|
|
|
|
LPWAVEFORMATEX lpFormat; // stream format
|
|
LONG cbFormat;
|
|
BOOL fDirty;
|
|
UINT mode;
|
|
EXTRA extra;
|
|
AVIFILEINFOW avihdr;
|
|
} WAVESTUFF, FAR *LPWAVESTUFF;
|
|
|
|
/*
|
|
** Whenever a function is called with a pointer to one of our Vtbls, we need
|
|
** to back up and get a pointer to the beginning of our structure. Depending
|
|
** on which pointer we are passed, we need to back up a different number of
|
|
** bytes. C++ would make this easier, by declaring backpointers.
|
|
*/
|
|
|
|
WAVESTUFF ws;
|
|
#define WAVESTUFF_FROM_UNKNOWN(pu) (LPWAVESTUFF)((LPBYTE)(pu) - ((LPBYTE)&ws.Unknown - (LPBYTE)&ws))
|
|
#define WAVESTUFF_FROM_FILE(pf) (LPWAVESTUFF)((LPBYTE)(pf) - ((LPBYTE)&ws.AVIFile - (LPBYTE)&ws))
|
|
#define WAVESTUFF_FROM_STREAM(ps) (LPWAVESTUFF)((LPBYTE)(ps) - ((LPBYTE)&ws.AVIStream - (LPBYTE)&ws))
|
|
#define WAVESTUFF_FROM_PERSIST(ppf) (LPWAVESTUFF)((LPBYTE)(ppf) - ((LPBYTE)&ws.Persist - (LPBYTE)&ws))
|
|
|
|
|
|
|
|
extern HINSTANCE ghMod;
|
|
LPTSTR FAR FileName( LPCTSTR lszPath);
|
|
extern LPTSTR FAR lstrzcpy (LPTSTR pszTgt, LPCTSTR pszSrc, size_t cch);
|
|
extern LPSTR FAR lstrzcpyA (LPSTR pszTgt, LPCSTR pszSrc, size_t cch);
|
|
extern LPWSTR FAR lstrzcpyW (LPWSTR pszTgt, LPCWSTR pszSrc, size_t cch);
|
|
extern LPWSTR FAR lstrzcpyAtoW (LPWSTR pszTgt, LPCSTR pszSrc, size_t cch);
|
|
extern LPSTR FAR lstrzcpyWtoA (LPSTR pszTgt, LPCWSTR pszSrc, size_t cch);
|
|
|
|
//
|
|
// 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);
|
|
#ifndef _WIN32
|
|
STDMETHODIMP WaveFileOpen(PAVIFILE pf, LPCSTR szFile, UINT mode);
|
|
#endif
|
|
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);
|
|
#ifndef _WIN32
|
|
STDMETHODIMP WaveFileSave(PAVIFILE pf, LPCSTR szFile, AVICOMPRESSOPTIONS FAR *lpOptions, AVISAVECALLBACK lpfnCallback);
|
|
#endif
|
|
|
|
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);
|
|
#ifdef _WIN32
|
|
STDMETHODIMP WaveFileDeleteStream(PAVIFILE pf, DWORD fccType, LONG lParam);
|
|
#else
|
|
STDMETHODIMP WaveFileReserved(PAVIFILE pf);
|
|
#endif
|
|
|
|
|
|
IAVIFileVtbl FileVtbl = {
|
|
WaveFileQueryInterface,
|
|
WaveFileAddRef,
|
|
WaveFileRelease,
|
|
#ifndef _WIN32
|
|
WaveFileOpen,
|
|
#endif
|
|
WaveFileInfo,
|
|
WaveFileGetStream,
|
|
WaveFileCreateStream,
|
|
#ifndef _WIN32
|
|
WaveFileSave,
|
|
#endif
|
|
WaveFileWriteData,
|
|
WaveFileReadData,
|
|
WaveFileEndRecord,
|
|
#ifdef _WIN32
|
|
WaveFileDeleteStream
|
|
#else
|
|
WaveFileReserved,
|
|
WaveFileReserved,
|
|
WaveFileReserved,
|
|
WaveFileReserved,
|
|
WaveFileReserved
|
|
#endif
|
|
};
|
|
|
|
|
|
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, LPARAM lParam1, LPARAM 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);
|
|
#ifdef _WIN32
|
|
STDMETHODIMP WaveStreamSetInfo(PAVISTREAM ps, AVISTREAMINFOW FAR * psi, LONG lSize);
|
|
#else
|
|
STDMETHODIMP WaveStreamReserved(PAVISTREAM ps);
|
|
#endif
|
|
|
|
IAVIStreamVtbl StreamVtbl = {
|
|
WaveStreamQueryInterface,
|
|
WaveStreamAddRef,
|
|
WaveStreamRelease,
|
|
WaveStreamCreate,
|
|
WaveStreamInfo,
|
|
WaveStreamFindSample,
|
|
WaveStreamReadFormat,
|
|
WaveStreamSetFormat,
|
|
WaveStreamRead,
|
|
WaveStreamWrite,
|
|
WaveStreamDelete,
|
|
WaveStreamReadData,
|
|
WaveStreamWriteData,
|
|
#ifdef _WIN32
|
|
WaveStreamSetInfo
|
|
#else
|
|
WaveStreamReserved,
|
|
WaveStreamReserved,
|
|
WaveStreamReserved,
|
|
WaveStreamReserved,
|
|
WaveStreamReserved
|
|
#endif
|
|
};
|
|
|
|
|
|
#if defined _WIN32 && !defined UNICODE
|
|
|
|
int LoadUnicodeString(HINSTANCE hinst, UINT wID, LPWSTR lpBuffer, int cchBuffer)
|
|
{
|
|
char ach[256];
|
|
int i;
|
|
|
|
i = LoadString(hinst, wID, ach, NUMELMS(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->hshfile = 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;
|
|
|
|
shfileSeek(p->hshfile, 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 (shfileCreateChunk(p->hshfile, &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 (shfileCreateChunk(p->hshfile, &ck, 0) != 0)
|
|
goto ERROR_CANNOT_WRITE; // cannot write file, probably
|
|
|
|
if (shfileWrite(p->hshfile, (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 (shfileAscend(p->hshfile, &ck, 0) != 0)
|
|
goto ERROR_CANNOT_WRITE; // cannot write file, probably
|
|
|
|
// If there was extra stuff here, we need to fill it!
|
|
if (shfileSeek(p->hshfile, 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 (shfileCreateChunk(p->hshfile, &ck, 0) != 0)
|
|
goto ERROR_CANNOT_WRITE; // cannot write file, probably
|
|
|
|
shfileSeek(p->hshfile,
|
|
p->ckData.dwDataOffset - 2 * sizeof(DWORD),
|
|
SEEK_SET);
|
|
|
|
if (shfileAscend(p->hshfile, &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 (shfileCreateChunk(p->hshfile, &ck, 0) != 0)
|
|
goto ERROR_CANNOT_WRITE; // cannot write file, probably
|
|
|
|
shfileSeek(p->hshfile, p->ckData.cksize, SEEK_CUR);
|
|
|
|
shfileAscend(p->hshfile, &ck, 0);
|
|
|
|
if (p->extra.cb) {
|
|
if (shfileWrite(p->hshfile, (HPSTR) p->extra.lp, p->extra.cb) != p->extra.cb)
|
|
goto ERROR_CANNOT_WRITE;
|
|
}
|
|
|
|
if (shfileAscend(p->hshfile, &ckRIFF, 0) != 0)
|
|
goto ERROR_CANNOT_WRITE;
|
|
|
|
if (shfileFlush(p->hshfile, 0) != 0)
|
|
goto ERROR_CANNOT_WRITE;
|
|
}
|
|
|
|
|
|
goto success;
|
|
|
|
ERROR_CANNOT_WRITE:
|
|
lRet = AVIERR_FILEWRITE;
|
|
|
|
success:
|
|
if (p->hshfile)
|
|
shfileClose(p->hshfile, 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) == TEXT('/') || (c) == TEXT('\\'))
|
|
|
|
/*--------------------------------------------------------------+
|
|
| FileName - return a pointer to the filename part of szPath |
|
|
| with no preceding path. |
|
|
| note: perhaps we should use GetFullPathName |
|
|
+--------------------------------------------------------------*/
|
|
LPTSTR FAR FileName(
|
|
LPCTSTR lszPath)
|
|
{
|
|
LPCTSTR lszCur;
|
|
|
|
for (lszCur = lszPath + lstrlen(lszPath); lszCur > lszPath && !SLASH(*lszCur) && *lszCur != ':';)
|
|
lszCur = CharPrev(lszPath, lszCur);
|
|
if (lszCur == lszPath)
|
|
return (LPTSTR)lszCur;
|
|
else
|
|
return (LPTSTR)(lszCur + 1);
|
|
}
|
|
|
|
STDMETHODIMP ParseAUFile(LPWAVESTUFF p);
|
|
|
|
|
|
/* - - - - - - - - */
|
|
|
|
STDMETHODIMP ParseWaveFile(LPWAVESTUFF p)
|
|
{
|
|
MMCKINFO ck;
|
|
MMCKINFO ckRIFF;
|
|
/* Read RIFF chunk */
|
|
if (shfileDescend(p->hshfile, &ckRIFF, NULL, 0) != 0)
|
|
goto error;
|
|
|
|
if (ckRIFF.ckid != FOURCC_RIFF || ckRIFF.fccType != formtypeWAVE)
|
|
return ParseAUFile(p);
|
|
|
|
/* Read WAVE format chunk */
|
|
ck.ckid = ckidWAVEFORMAT;
|
|
if (FindChunkAndKeepExtras(&p->extra, p->hshfile, &ck, &ckRIFF, MMIO_FINDCHUNK))
|
|
goto error;
|
|
|
|
p->cbFormat = ck.cksize;
|
|
p->lpFormat = (LPWAVEFORMATEX) GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize);
|
|
|
|
if (p->lpFormat == NULL)
|
|
goto error;
|
|
|
|
if (shfileRead(p->hshfile,
|
|
(HPSTR) p->lpFormat,
|
|
(LONG)ck.cksize) != (LONG)ck.cksize)
|
|
goto error;
|
|
|
|
/* Ascend out of stream header */
|
|
if (shfileAscend(p->hshfile, &ck, 0) != 0)
|
|
goto error;
|
|
|
|
/* Find big data chunk */
|
|
p->ckData.ckid = ckidWAVEDATA;
|
|
if (FindChunkAndKeepExtras(&p->extra, p->hshfile, &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
|
|
|
|
|
|
shfileAscend(p->hshfile, &p->ckData, 0);
|
|
|
|
// Read extra data at end of file....
|
|
if (FindChunkAndKeepExtras(&p->extra, p->hshfile, &ckRIFF, &ck, 0) != AVIERR_OK)
|
|
goto error;
|
|
|
|
return ResultFromScode(0); // success
|
|
|
|
error:
|
|
return ResultFromScode(AVIERR_FILEREAD);
|
|
}
|
|
|
|
//
|
|
// The Open Method for our File interface - Open a WAVE file
|
|
//
|
|
STDMETHODIMP WaveFileOpen(
|
|
PAVIFILE pf,
|
|
LPCTSTR szFile,
|
|
UINT mode)
|
|
{
|
|
LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
|
|
UINT ui;
|
|
TCHAR ach[80];
|
|
HRESULT hr = NOERROR;
|
|
|
|
// !!! Assumptions about the AVIFILE.DLL (which calls us):
|
|
// We will only see READWRITE mode, never only WRITE mode.
|
|
|
|
// if it ain't broke, don't fix it
|
|
#if 0
|
|
// force the share flags to the 'correct' values
|
|
// If we're writing, use Exclusive mode. If we're reading, use DenyWrite.
|
|
if (mode & OF_READWRITE) {
|
|
mode = (mode & ~(MMIO_SHAREMODE)) | OF_SHARE_EXCLUSIVE;
|
|
} else {
|
|
mode = (mode & ~(MMIO_SHAREMODE)) | OF_SHARE_DENY_WRITE;
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// 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->hshfile = shfileOpen((LPTSTR) szFile, NULL, MMIO_ALLOCBUF | mode);
|
|
if (!p->hshfile && ((mode & MMIO_RWMODE) == OF_READ)) {
|
|
// if the open fails, try again without the share flags.
|
|
mode &= ~(MMIO_SHAREMODE);
|
|
p->hshfile = shfileOpen((LPTSTR) szFile, NULL, MMIO_ALLOCBUF | mode);
|
|
}
|
|
SetErrorMode(ui);
|
|
|
|
//
|
|
// Now set up our structure
|
|
//
|
|
|
|
p->mode = mode;
|
|
|
|
if (!p->hshfile)
|
|
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(ghMod, IDS_FILETYPE, p->avihdr.szFileType,
|
|
NUMELMS(p->avihdr.szFileType));
|
|
|
|
//
|
|
// Initialize our AVISTREAMHEADER
|
|
//
|
|
LoadString(ghMod, IDS_STREAMNAME, ach, NUMELMS(ach));
|
|
{
|
|
TCHAR achTemp[MAX_PATH];
|
|
|
|
// Fix: Change from wsprintf to StringCchPrintf so we don't overrun achTemp
|
|
StringCchPrintf(achTemp, NUMELMS(achTemp), ach, FileName(szFile));
|
|
|
|
#ifdef UNICODE
|
|
lstrzcpy (p->avistream.szName,achTemp,NUMELMS(p->avistream.szName));
|
|
#else
|
|
lstrzcpyAtoW (p->avistream.szName,achTemp,NUMELMS(p->avistream.szName));
|
|
#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
|
|
hr = ParseWaveFile(p);
|
|
}
|
|
|
|
return hr;
|
|
|
|
error:
|
|
return ResultFromScode(AVIERR_FILEREAD);
|
|
}
|
|
|
|
typedef struct {
|
|
DWORD magic; /* magic number SND_MAGIC */
|
|
DWORD dataLocation; /* offset or poDWORDer to the data */
|
|
DWORD dataSize; /* number of bytes of data */
|
|
DWORD dataFormat; /* the data format code */
|
|
DWORD samplingRate; /* the sampling rate */
|
|
DWORD channelCount; /* the number of channels */
|
|
DWORD fccInfo; /* optional text information */
|
|
} SNDSoundStruct;
|
|
|
|
#define SND_FORMAT_MULAW_8 1 // 8-bit mu-law samples
|
|
#define SND_FORMAT_LINEAR_8 2 // 8-bit linear samples
|
|
|
|
#define SWAP(x,y) ( (x)^=(y), (y)^=(x), (x)^=(y) )
|
|
|
|
void _inline SwapDWORD( DWORD FAR * pdw )
|
|
{
|
|
SWAP(((BYTE FAR *)pdw)[0],((BYTE FAR *)pdw)[3]);
|
|
SWAP(((BYTE FAR *)pdw)[1],((BYTE FAR *)pdw)[2]);
|
|
}
|
|
|
|
STDMETHODIMP ParseAUFile(LPWAVESTUFF p)
|
|
{
|
|
SNDSoundStruct header;
|
|
|
|
shfileSeek(p->hshfile, 0, SEEK_SET);
|
|
|
|
if (shfileRead(p->hshfile, (HPSTR) &header, sizeof(header)) != sizeof(header))
|
|
goto error;
|
|
|
|
// validate header
|
|
// !!!
|
|
if (header.magic != mmioFOURCC('.', 's', 'n', 'd'))
|
|
goto error;
|
|
|
|
SwapDWORD(&header.dataFormat);
|
|
SwapDWORD(&header.dataLocation);
|
|
SwapDWORD(&header.dataSize);
|
|
SwapDWORD(&header.samplingRate);
|
|
SwapDWORD(&header.channelCount);
|
|
|
|
p->cbFormat = sizeof(WAVEFORMATEX);
|
|
p->lpFormat = (LPWAVEFORMATEX) GlobalAllocPtr(GHND, p->cbFormat);
|
|
|
|
if (p->lpFormat == NULL)
|
|
goto error;
|
|
|
|
p->mode = OF_READ | OF_SHARE_DENY_WRITE;
|
|
|
|
// fill in wave format fields
|
|
if (header.dataFormat == SND_FORMAT_MULAW_8) {
|
|
p->lpFormat->wFormatTag = WAVE_FORMAT_MULAW;
|
|
p->lpFormat->wBitsPerSample = 8;
|
|
|
|
// !!! HACK: if the sampling rate is almost 8KHz, make it be
|
|
// exactly 8KHz, so that more sound cards will play it right.
|
|
if (header.samplingRate > 7980 && header.samplingRate < 8020)
|
|
header.samplingRate = 8000;
|
|
|
|
} else if (header.dataFormat == SND_FORMAT_LINEAR_8) {
|
|
p->lpFormat->wFormatTag = WAVE_FORMAT_PCM;
|
|
p->lpFormat->wBitsPerSample = 8;
|
|
// Could support LINEAR_16, but would have to byte-swap everything....
|
|
} else
|
|
goto error;
|
|
|
|
p->lpFormat->nChannels = (UINT) header.channelCount;
|
|
p->lpFormat->nSamplesPerSec = header.samplingRate;
|
|
p->lpFormat->nAvgBytesPerSec = header.samplingRate * p->lpFormat->nChannels;
|
|
p->lpFormat->nBlockAlign = 1;
|
|
|
|
/* Tell rest of handler where data is */
|
|
p->ckData.dwDataOffset = header.dataLocation;
|
|
p->ckData.cksize = header.dataSize;
|
|
|
|
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
|
|
|
|
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,
|
|
LPARAM lParam1,
|
|
LPARAM 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 (shfileSeek(p->hshfile, lSeek, SEEK_SET) != lSeek)
|
|
goto ack;
|
|
|
|
if (shfileRead(p->hshfile, (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 = (LPWAVEFORMATEX) 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;
|
|
|
|
#if 0 // !!! don't check for too long - why not?
|
|
if (lStart > (LONG) (p->avistream.dwStart + p->avistream.dwLength))
|
|
return ResultFromScode(AVIERR_BADPARAM);
|
|
#endif
|
|
|
|
p->fDirty = TRUE;
|
|
|
|
shfileSeek(p->hshfile,
|
|
p->ckData.dwDataOffset +
|
|
lStart * p->avistream.dwSampleSize,
|
|
SEEK_SET);
|
|
|
|
if (shfileWrite(p->hshfile, (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 _WIN32 && !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.
|
|
lstrzcpyWtoA (achTemp, lpszFileName, NUMELMS(achTemp));
|
|
#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);
|
|
}
|
|
|