| /* | |
| * Copyright 2002 Michael Günnewig | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #define COM_NO_WINDOWS_H | |
| #include <assert.h> | |
| #include <stdarg.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "wingdi.h" | |
| #include "winuser.h" | |
| #include "winnls.h" | |
| #include "winerror.h" | |
| #include "windowsx.h" | |
| #include "mmsystem.h" | |
| #include "vfw.h" | |
| #include "msacm.h" | |
| #include "avifile_private.h" | |
| #include "extrachunk.h" | |
| #include "wine/unicode.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(avifile); | |
| /***********************************************************************/ | |
| #define formtypeWAVE mmioFOURCC('W','A','V','E') | |
| #define ckidWAVEFORMAT mmioFOURCC('f','m','t',' ') | |
| #define ckidWAVEFACT mmioFOURCC('f','a','c','t') | |
| #define ckidWAVEDATA mmioFOURCC('d','a','t','a') | |
| /***********************************************************************/ | |
| #define ENDIAN_SWAPWORD(x) ((((x) >> 8) & 0xFF) | (((x) & 0xFF) << 8)) | |
| #define ENDIAN_SWAPDWORD(x) (ENDIAN_SWAPWORD((x >> 16) & 0xFFFF) | \ | |
| ENDIAN_SWAPWORD(x & 0xFFFF) << 16) | |
| #ifdef WORDS_BIGENDIAN | |
| #define BE2H_WORD(x) (x) | |
| #define BE2H_DWORD(x) (x) | |
| #define LE2H_WORD(x) ENDIAN_SWAPWORD(x) | |
| #define LE2H_DWORD(x) ENDIAN_SWAPDWORD(x) | |
| #else | |
| #define BE2H_WORD(x) ENDIAN_SWAPWORD(x) | |
| #define BE2H_DWORD(x) ENDIAN_SWAPDWORD(x) | |
| #define LE2H_WORD(x) (x) | |
| #define LE2H_DWORD(x) (x) | |
| #endif | |
| typedef struct { | |
| FOURCC fccType; | |
| DWORD offset; | |
| DWORD size; | |
| INT encoding; | |
| DWORD sampleRate; | |
| DWORD channels; | |
| } SUNAUDIOHEADER; | |
| #define AU_ENCODING_ULAW_8 1 | |
| #define AU_ENCODING_PCM_8 2 | |
| #define AU_ENCODING_PCM_16 3 | |
| #define AU_ENCODING_PCM_24 4 | |
| #define AU_ENCODING_PCM_32 5 | |
| #define AU_ENCODING_FLOAT 6 | |
| #define AU_ENCODING_DOUBLE 7 | |
| #define AU_ENCODING_ADPCM_G721_32 23 | |
| #define AU_ENCODING_ADPCM_G722 24 | |
| #define AU_ENCODING_ADPCM_G723_24 25 | |
| #define AU_ENCODING_ADPCM_G723_5 26 | |
| #define AU_ENCODING_ALAW_8 27 | |
| /***********************************************************************/ | |
| static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj); | |
| static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface); | |
| static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface); | |
| static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size); | |
| static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam); | |
| static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi); | |
| static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size); | |
| static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size); | |
| static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface); | |
| static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam); | |
| struct IAVIFileVtbl iwavft = { | |
| IAVIFile_fnQueryInterface, | |
| IAVIFile_fnAddRef, | |
| IAVIFile_fnRelease, | |
| IAVIFile_fnInfo, | |
| IAVIFile_fnGetStream, | |
| IAVIFile_fnCreateStream, | |
| IAVIFile_fnWriteData, | |
| IAVIFile_fnReadData, | |
| IAVIFile_fnEndRecord, | |
| IAVIFile_fnDeleteStream | |
| }; | |
| static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj); | |
| static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile*iface); | |
| static ULONG WINAPI IPersistFile_fnRelease(IPersistFile*iface); | |
| static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID); | |
| static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface); | |
| static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode); | |
| static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember); | |
| static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName); | |
| static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName); | |
| struct IPersistFileVtbl iwavpft = { | |
| IPersistFile_fnQueryInterface, | |
| IPersistFile_fnAddRef, | |
| IPersistFile_fnRelease, | |
| IPersistFile_fnGetClassID, | |
| IPersistFile_fnIsDirty, | |
| IPersistFile_fnLoad, | |
| IPersistFile_fnSave, | |
| IPersistFile_fnSaveCompleted, | |
| IPersistFile_fnGetCurFile | |
| }; | |
| static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj); | |
| static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream*iface); | |
| static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface); | |
| static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2); | |
| static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size); | |
| static LONG WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags); | |
| static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize); | |
| static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize); | |
| static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread); | |
| static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten); | |
| static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples); | |
| static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread); | |
| static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size); | |
| static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen); | |
| struct IAVIStreamVtbl iwavst = { | |
| IAVIStream_fnQueryInterface, | |
| IAVIStream_fnAddRef, | |
| IAVIStream_fnRelease, | |
| IAVIStream_fnCreate, | |
| IAVIStream_fnInfo, | |
| IAVIStream_fnFindSample, | |
| IAVIStream_fnReadFormat, | |
| IAVIStream_fnSetFormat, | |
| IAVIStream_fnRead, | |
| IAVIStream_fnWrite, | |
| IAVIStream_fnDelete, | |
| IAVIStream_fnReadData, | |
| IAVIStream_fnWriteData, | |
| IAVIStream_fnSetInfo | |
| }; | |
| typedef struct _IAVIFileImpl IAVIFileImpl; | |
| typedef struct _IPersistFileImpl { | |
| /* IUnknown stuff */ | |
| IPersistFileVtbl *lpVtbl; | |
| /* IPersistFile stuff */ | |
| IAVIFileImpl *paf; | |
| } IPersistFileImpl; | |
| typedef struct _IAVIStreamImpl { | |
| /* IUnknown stuff */ | |
| IAVIStreamVtbl *lpVtbl; | |
| /* IAVIStream stuff */ | |
| IAVIFileImpl *paf; | |
| } IAVIStreamImpl; | |
| struct _IAVIFileImpl { | |
| /* IUnknown stuff */ | |
| IAVIFileVtbl *lpVtbl; | |
| DWORD ref; | |
| /* IAVIFile, IAVIStream stuff... */ | |
| IPersistFileImpl iPersistFile; | |
| IAVIStreamImpl iAVIStream; | |
| AVIFILEINFOW fInfo; | |
| AVISTREAMINFOW sInfo; | |
| LPWAVEFORMATEX lpFormat; | |
| LONG cbFormat; | |
| MMCKINFO ckData; | |
| EXTRACHUNKS extra; | |
| /* IPersistFile stuff ... */ | |
| HMMIO hmmio; | |
| LPWSTR szFileName; | |
| UINT uMode; | |
| BOOL fDirty; | |
| }; | |
| /***********************************************************************/ | |
| static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This); | |
| static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This); | |
| static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This); | |
| HRESULT AVIFILE_CreateWAVFile(REFIID riid, LPVOID *ppv) | |
| { | |
| IAVIFileImpl *pfile; | |
| HRESULT hr; | |
| assert(riid != NULL && ppv != NULL); | |
| *ppv = NULL; | |
| pfile = (IAVIFileImpl*)LocalAlloc(LPTR, sizeof(IAVIFileImpl)); | |
| if (pfile == NULL) | |
| return AVIERR_MEMORY; | |
| pfile->lpVtbl = &iwavft; | |
| pfile->iPersistFile.lpVtbl = &iwavpft; | |
| pfile->iAVIStream.lpVtbl = &iwavst; | |
| pfile->ref = 0; | |
| pfile->iPersistFile.paf = pfile; | |
| pfile->iAVIStream.paf = pfile; | |
| hr = IAVIFile_QueryInterface((IAVIFile*)pfile, riid, ppv); | |
| if (FAILED(hr)) | |
| LocalFree((HLOCAL)pfile); | |
| return hr; | |
| } | |
| static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid, | |
| LPVOID *obj) | |
| { | |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; | |
| TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj); | |
| if (IsEqualGUID(&IID_IUnknown, refiid) || | |
| IsEqualGUID(&IID_IAVIFile, refiid)) { | |
| *obj = iface; | |
| return S_OK; | |
| } else if (This->fInfo.dwStreams == 1 && | |
| IsEqualGUID(&IID_IAVIStream, refiid)) { | |
| *obj = &This->iAVIStream; | |
| return S_OK; | |
| } else if (IsEqualGUID(&IID_IPersistFile, refiid)) { | |
| *obj = &This->iPersistFile; | |
| return S_OK; | |
| } | |
| return OLE_E_ENUM_NOMORE; | |
| } | |
| static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface) | |
| { | |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; | |
| TRACE("(%p)\n",iface); | |
| return InterlockedIncrement(&This->ref); | |
| } | |
| static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface) | |
| { | |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)\n",iface); | |
| if (!ref) { | |
| if (This->fDirty) { | |
| /* need to write headers to file */ | |
| AVIFILE_SaveFile(This); | |
| } | |
| if (This->lpFormat != NULL) { | |
| GlobalFreePtr(This->lpFormat); | |
| This->lpFormat = NULL; | |
| This->cbFormat = 0; | |
| } | |
| if (This->extra.lp != NULL) { | |
| GlobalFreePtr(This->extra.lp); | |
| This->extra.lp = NULL; | |
| This->extra.cb = 0; | |
| } | |
| if (This->szFileName != NULL) { | |
| LocalFree((HLOCAL)This->szFileName); | |
| This->szFileName = NULL; | |
| } | |
| if (This->hmmio != NULL) { | |
| mmioClose(This->hmmio, 0); | |
| This->hmmio = NULL; | |
| } | |
| LocalFree((HLOCAL)This); | |
| return 0; | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi, | |
| LONG size) | |
| { | |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; | |
| TRACE("(%p,%p,%ld)\n",iface,afi,size); | |
| if (afi == NULL) | |
| return AVIERR_BADPARAM; | |
| if (size < 0) | |
| return AVIERR_BADSIZE; | |
| /* update file info */ | |
| This->fInfo.dwFlags = 0; | |
| This->fInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; | |
| if (This->lpFormat != NULL) { | |
| assert(This->sInfo.dwScale != 0); | |
| This->fInfo.dwStreams = 1; | |
| This->fInfo.dwScale = This->sInfo.dwScale; | |
| This->fInfo.dwRate = This->sInfo.dwRate; | |
| This->fInfo.dwLength = This->sInfo.dwLength; | |
| This->fInfo.dwSuggestedBufferSize = This->ckData.cksize; | |
| This->fInfo.dwMaxBytesPerSec = | |
| MulDiv(This->sInfo.dwSampleSize,This->sInfo.dwRate,This->sInfo.dwScale); | |
| } | |
| memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo))); | |
| if ((DWORD)size < sizeof(This->fInfo)) | |
| return AVIERR_BUFFERTOOSMALL; | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis, | |
| DWORD fccType, LONG lParam) | |
| { | |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; | |
| TRACE("(%p,%p,0x%08lX,%ld)\n", iface, avis, fccType, lParam); | |
| /* check parameter */ | |
| if (avis == NULL) | |
| return AVIERR_BADPARAM; | |
| *avis = NULL; | |
| /* Does our stream exists? */ | |
| if (lParam != 0 || This->fInfo.dwStreams == 0) | |
| return AVIERR_NODATA; | |
| if (fccType != 0 && fccType != streamtypeAUDIO) | |
| return AVIERR_NODATA; | |
| *avis = (PAVISTREAM)&This->iAVIStream; | |
| IAVIFile_AddRef(iface); | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis, | |
| LPAVISTREAMINFOW asi) | |
| { | |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; | |
| TRACE("(%p,%p,%p)\n", iface, avis, asi); | |
| /* check parameters */ | |
| if (avis == NULL || asi == NULL) | |
| return AVIERR_BADPARAM; | |
| *avis = NULL; | |
| /* We only support one audio stream */ | |
| if (This->fInfo.dwStreams != 0 || This->lpFormat != NULL) | |
| return AVIERR_UNSUPPORTED; | |
| if (asi->fccType != streamtypeAUDIO) | |
| return AVIERR_UNSUPPORTED; | |
| /* Does the user have write permission? */ | |
| if ((This->uMode & MMIO_RWMODE) == 0) | |
| return AVIERR_READONLY; | |
| This->cbFormat = 0; | |
| This->lpFormat = NULL; | |
| memcpy(&This->sInfo, asi, sizeof(This->sInfo)); | |
| /* make sure streaminfo if okay for us */ | |
| This->sInfo.fccHandler = 0; | |
| This->sInfo.dwFlags = 0; | |
| This->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE; | |
| This->sInfo.dwStart = 0; | |
| This->sInfo.dwInitialFrames = 0; | |
| This->sInfo.dwFormatChangeCount = 0; | |
| memset(&This->sInfo.rcFrame, 0, sizeof(This->sInfo.rcFrame)); | |
| This->fInfo.dwStreams = 1; | |
| This->fInfo.dwScale = This->sInfo.dwScale; | |
| This->fInfo.dwRate = This->sInfo.dwRate; | |
| This->fInfo.dwLength = This->sInfo.dwLength; | |
| This->ckData.dwDataOffset = 0; | |
| This->ckData.cksize = 0; | |
| *avis = (PAVISTREAM)&This->iAVIStream; | |
| IAVIFile_AddRef(iface); | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, | |
| LPVOID lpData, LONG size) | |
| { | |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; | |
| TRACE("(%p,0x%08lX,%p,%ld)\n", iface, ckid, lpData, size); | |
| /* check parameters */ | |
| if (lpData == NULL) | |
| return AVIERR_BADPARAM; | |
| if (size < 0) | |
| return AVIERR_BADSIZE; | |
| /* Do we have write permission? */ | |
| if ((This->uMode & MMIO_RWMODE) == 0) | |
| return AVIERR_READONLY; | |
| This->fDirty = TRUE; | |
| return WriteExtraChunk(&This->extra, ckid, lpData, size); | |
| } | |
| static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, | |
| LPVOID lpData, LONG *size) | |
| { | |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; | |
| TRACE("(%p,0x%08lX,%p,%p)\n", iface, ckid, lpData, size); | |
| return ReadExtraChunk(&This->extra, ckid, lpData, size); | |
| } | |
| static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface) | |
| { | |
| TRACE("(%p)\n",iface); | |
| /* This is only needed for interleaved files. | |
| * We have only one stream, which can't be interleaved. | |
| */ | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, | |
| LONG lParam) | |
| { | |
| IAVIFileImpl *This = (IAVIFileImpl *)iface; | |
| TRACE("(%p,0x%08lX,%ld)\n", iface, fccType, lParam); | |
| /* check parameter */ | |
| if (lParam < 0) | |
| return AVIERR_BADPARAM; | |
| /* Do we have our audio stream? */ | |
| if (lParam != 0 || This->fInfo.dwStreams == 0 || | |
| (fccType != 0 && fccType != streamtypeAUDIO)) | |
| return AVIERR_NODATA; | |
| /* Have user write permissions? */ | |
| if ((This->uMode & MMIO_RWMODE) == 0) | |
| return AVIERR_READONLY; | |
| GlobalFreePtr(This->lpFormat); | |
| This->lpFormat = NULL; | |
| This->cbFormat = 0; | |
| /* update infos */ | |
| This->ckData.dwDataOffset = 0; | |
| This->ckData.cksize = 0; | |
| This->sInfo.dwScale = 0; | |
| This->sInfo.dwRate = 0; | |
| This->sInfo.dwLength = 0; | |
| This->sInfo.dwSuggestedBufferSize = 0; | |
| This->fInfo.dwStreams = 0; | |
| This->fInfo.dwEditCount++; | |
| This->fDirty = TRUE; | |
| return AVIERR_OK; | |
| } | |
| /***********************************************************************/ | |
| static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, | |
| REFIID refiid, LPVOID *obj) | |
| { | |
| IPersistFileImpl *This = (IPersistFileImpl *)iface; | |
| assert(This->paf != NULL); | |
| return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj); | |
| } | |
| static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface) | |
| { | |
| IPersistFileImpl *This = (IPersistFileImpl *)iface; | |
| assert(This->paf != NULL); | |
| return IAVIFile_AddRef((PAVIFILE)This->paf); | |
| } | |
| static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface) | |
| { | |
| IPersistFileImpl *This = (IPersistFileImpl *)iface; | |
| assert(This->paf != NULL); | |
| return IAVIFile_Release((PAVIFILE)This->paf); | |
| } | |
| static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, | |
| LPCLSID pClassID) | |
| { | |
| TRACE("(%p,%p)\n", iface, pClassID); | |
| if (pClassID == NULL) | |
| return AVIERR_BADPARAM; | |
| memcpy(pClassID, &CLSID_WAVFile, sizeof(CLSID_WAVFile)); | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface) | |
| { | |
| IPersistFileImpl *This = (IPersistFileImpl *)iface; | |
| TRACE("(%p)\n", iface); | |
| assert(This->paf != NULL); | |
| return (This->paf->fDirty ? S_OK : S_FALSE); | |
| } | |
| static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, | |
| LPCOLESTR pszFileName, DWORD dwMode) | |
| { | |
| IAVIFileImpl *This = ((IPersistFileImpl*)iface)->paf; | |
| WCHAR wszStreamFmt[50]; | |
| INT len; | |
| TRACE("(%p,%s,0x%08lX)\n", iface, debugstr_w(pszFileName), dwMode); | |
| /* check parameter */ | |
| if (pszFileName == NULL) | |
| return AVIERR_BADPARAM; | |
| assert(This != NULL); | |
| if (This->hmmio != NULL) | |
| return AVIERR_ERROR; /* No reuse of this object for another file! */ | |
| /* remeber mode and name */ | |
| This->uMode = dwMode; | |
| len = lstrlenW(pszFileName) + 1; | |
| This->szFileName = LocalAlloc(LPTR, len * sizeof(WCHAR)); | |
| if (This->szFileName == NULL) | |
| return AVIERR_MEMORY; | |
| lstrcpyW(This->szFileName, pszFileName); | |
| /* try to open the file */ | |
| This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode); | |
| if (This->hmmio == NULL) { | |
| /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */ | |
| LPSTR szFileName; | |
| len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, | |
| NULL, 0, NULL, NULL); | |
| szFileName = LocalAlloc(LPTR, len * sizeof(CHAR)); | |
| if (szFileName == NULL) | |
| return AVIERR_MEMORY; | |
| WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, | |
| len, NULL, NULL); | |
| This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode); | |
| LocalFree((HLOCAL)szFileName); | |
| if (This->hmmio == NULL) | |
| return AVIERR_FILEOPEN; | |
| } | |
| memset(& This->fInfo, 0, sizeof(This->fInfo)); | |
| memset(& This->sInfo, 0, sizeof(This->sInfo)); | |
| LoadStringW(AVIFILE_hModule, IDS_WAVEFILETYPE, This->fInfo.szFileType, | |
| sizeof(This->fInfo.szFileType)); | |
| if (LoadStringW(AVIFILE_hModule, IDS_WAVESTREAMFORMAT, | |
| wszStreamFmt, sizeof(wszStreamFmt)) > 0) { | |
| wsprintfW(This->sInfo.szName, wszStreamFmt, | |
| AVIFILE_BasenameW(This->szFileName)); | |
| } | |
| /* should we create a new file? */ | |
| if (dwMode & OF_CREATE) { | |
| /* nothing more to do */ | |
| return AVIERR_OK; | |
| } else | |
| return AVIFILE_LoadFile(This); | |
| } | |
| static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, | |
| LPCOLESTR pszFileName,BOOL fRemember) | |
| { | |
| TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember); | |
| /* We write directly to disk, so nothing to do. */ | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, | |
| LPCOLESTR pszFileName) | |
| { | |
| TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName)); | |
| /* We write directly to disk, so nothing to do. */ | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, | |
| LPOLESTR *ppszFileName) | |
| { | |
| IPersistFileImpl *This = (IPersistFileImpl *)iface; | |
| TRACE("(%p,%p)\n", iface, ppszFileName); | |
| if (ppszFileName == NULL) | |
| return AVIERR_BADPARAM; | |
| *ppszFileName = NULL; | |
| assert(This->paf != NULL); | |
| if (This->paf->szFileName != NULL) { | |
| int len = lstrlenW(This->paf->szFileName) + 1; | |
| *ppszFileName = GlobalAllocPtr(GHND, len * sizeof(WCHAR)); | |
| if (*ppszFileName == NULL) | |
| return AVIERR_MEMORY; | |
| strcpyW(*ppszFileName, This->paf->szFileName); | |
| } | |
| return AVIERR_OK; | |
| } | |
| /***********************************************************************/ | |
| static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, | |
| REFIID refiid, LPVOID *obj) | |
| { | |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | |
| assert(This->paf != NULL); | |
| return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj); | |
| } | |
| static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface) | |
| { | |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | |
| assert(This->paf != NULL); | |
| return IAVIFile_AddRef((PAVIFILE)This->paf); | |
| } | |
| static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface) | |
| { | |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | |
| assert(This->paf != NULL); | |
| return IAVIFile_Release((PAVIFILE)This->paf); | |
| } | |
| static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, | |
| LPARAM lParam2) | |
| { | |
| TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2); | |
| /* This IAVIStream interface needs an WAVFile */ | |
| return AVIERR_UNSUPPORTED; | |
| } | |
| static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi, | |
| LONG size) | |
| { | |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | |
| TRACE("(%p,%p,%ld)\n", iface, psi, size); | |
| if (psi == NULL) | |
| return AVIERR_BADPARAM; | |
| if (size < 0) | |
| return AVIERR_BADSIZE; | |
| memcpy(psi, &This->paf->sInfo, min((DWORD)size, sizeof(This->paf->sInfo))); | |
| if ((DWORD)size < sizeof(This->paf->sInfo)) | |
| return AVIERR_BUFFERTOOSMALL; | |
| return AVIERR_OK; | |
| } | |
| static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, | |
| LONG flags) | |
| { | |
| IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; | |
| TRACE("(%p,%ld,0x%08lX)\n",iface,pos,flags); | |
| /* Do we have data? */ | |
| if (This->lpFormat == NULL) | |
| return -1; | |
| /* We don't have an index */ | |
| if (flags & FIND_INDEX) | |
| return -1; | |
| if (flags & FIND_FROM_START) { | |
| pos = This->sInfo.dwStart; | |
| flags &= ~(FIND_FROM_START|FIND_PREV); | |
| flags |= FIND_NEXT; | |
| } | |
| if (flags & FIND_FORMAT) { | |
| if ((flags & FIND_NEXT) && pos > 0) | |
| pos = -1; | |
| else | |
| pos = 0; | |
| } | |
| if ((flags & FIND_RET) == FIND_LENGTH || | |
| (flags & FIND_RET) == FIND_SIZE) | |
| return This->sInfo.dwSampleSize; | |
| if ((flags & FIND_RET) == FIND_OFFSET) | |
| return This->ckData.dwDataOffset + pos * This->sInfo.dwSampleSize; | |
| return pos; | |
| } | |
| static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, | |
| LPVOID format, LONG *formatsize) | |
| { | |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | |
| TRACE("(%p,%ld,%p,%p)\n", iface, pos, format, formatsize); | |
| if (formatsize == NULL) | |
| return AVIERR_BADPARAM; | |
| /* only interested in needed buffersize? */ | |
| if (format == NULL || *formatsize <= 0) { | |
| *formatsize = This->paf->cbFormat; | |
| return AVIERR_OK; | |
| } | |
| /* copy initial format (only as much as will fit) */ | |
| memcpy(format, This->paf->lpFormat, min(*formatsize, This->paf->cbFormat)); | |
| if (*formatsize < This->paf->cbFormat) { | |
| *formatsize = This->paf->cbFormat; | |
| return AVIERR_BUFFERTOOSMALL; | |
| } | |
| *formatsize = This->paf->cbFormat; | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, | |
| LPVOID format, LONG formatsize) | |
| { | |
| IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; | |
| TRACE("(%p,%ld,%p,%ld)\n", iface, pos, format, formatsize); | |
| /* check parameters */ | |
| if (format == NULL || formatsize <= sizeof(PCMWAVEFORMAT)) | |
| return AVIERR_BADPARAM; | |
| /* We can only do this to an empty wave file, but ignore call | |
| * if still same format */ | |
| if (This->lpFormat != NULL) { | |
| if (formatsize != This->cbFormat || | |
| memcmp(format, This->lpFormat, formatsize) != 0) | |
| return AVIERR_UNSUPPORTED; | |
| return AVIERR_OK; | |
| } | |
| /* only support start at position 0 */ | |
| if (pos != 0) | |
| return AVIERR_UNSUPPORTED; | |
| /* Do we have write permission? */ | |
| if ((This->uMode & MMIO_RWMODE) == 0) | |
| return AVIERR_READONLY; | |
| /* get memory for format and copy it */ | |
| This->lpFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, formatsize); | |
| if (This->lpFormat == NULL) | |
| return AVIERR_MEMORY; | |
| This->cbFormat = formatsize; | |
| memcpy(This->lpFormat, format, formatsize); | |
| /* update info's about 'data' chunk */ | |
| This->ckData.dwDataOffset = formatsize + 7 * sizeof(DWORD); | |
| This->ckData.cksize = 0; | |
| /* for non-pcm format we need also a 'fact' chunk */ | |
| if (This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) | |
| This->ckData.dwDataOffset += 3 * sizeof(DWORD); | |
| /* update stream and file info */ | |
| This->sInfo.dwSampleSize = This->lpFormat->nBlockAlign; | |
| This->sInfo.dwScale = This->lpFormat->nBlockAlign; | |
| This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; | |
| This->sInfo.dwLength = 0; | |
| This->sInfo.dwSuggestedBufferSize = 0; | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, | |
| LONG samples, LPVOID buffer, | |
| LONG buffersize, LPLONG bytesread, | |
| LPLONG samplesread) | |
| { | |
| IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; | |
| TRACE("(%p,%ld,%ld,%p,%ld,%p,%p)\n", iface, start, samples, buffer, | |
| buffersize, bytesread, samplesread); | |
| /* clear return parameters if given */ | |
| if (bytesread != NULL) | |
| *bytesread = 0; | |
| if (samplesread != NULL) | |
| *samplesread = 0; | |
| /* positions without data */ | |
| if (start < 0 || (DWORD)start > This->sInfo.dwLength) | |
| return AVIERR_OK; | |
| /* check samples */ | |
| if (samples < 0) | |
| samples = 0; | |
| if (buffersize > 0) { | |
| if (samples > 0) | |
| samples = min((DWORD)samples, buffersize / This->sInfo.dwSampleSize); | |
| else | |
| samples = buffersize / This->sInfo.dwSampleSize; | |
| } | |
| /* limit to end of stream */ | |
| if ((DWORD)(start + samples) > This->sInfo.dwLength) | |
| samples = This->sInfo.dwLength - start; | |
| /* request only the sizes? */ | |
| if (buffer == NULL || buffersize <= 0) { | |
| /* then I need at least one parameter for it */ | |
| if (bytesread == NULL && samplesread == NULL) | |
| return AVIERR_BADPARAM; | |
| if (bytesread != NULL) | |
| *bytesread = samples * This->sInfo.dwSampleSize; | |
| if (samplesread != NULL) | |
| *samplesread = samples; | |
| return AVIERR_OK; | |
| } | |
| /* nothing to read? */ | |
| if (samples == 0) | |
| return AVIERR_OK; | |
| /* Can I read at least one sample? */ | |
| if ((DWORD)buffersize < This->sInfo.dwSampleSize) | |
| return AVIERR_BUFFERTOOSMALL; | |
| buffersize = samples * This->sInfo.dwSampleSize; | |
| if (mmioSeek(This->hmmio, This->ckData.dwDataOffset | |
| + start * This->sInfo.dwSampleSize, SEEK_SET) == -1) | |
| return AVIERR_FILEREAD; | |
| if (mmioRead(This->hmmio, (HPSTR)buffer, buffersize) != buffersize) | |
| return AVIERR_FILEREAD; | |
| /* fill out return parameters if given */ | |
| if (bytesread != NULL) | |
| *bytesread = buffersize; | |
| if (samplesread != NULL) | |
| *samplesread = samples; | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, | |
| LONG samples, LPVOID buffer, | |
| LONG buffersize, DWORD flags, | |
| LPLONG sampwritten, | |
| LPLONG byteswritten) | |
| { | |
| IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; | |
| TRACE("(%p,%ld,%ld,%p,%ld,0x%08lX,%p,%p)\n", iface, start, samples, | |
| buffer, buffersize, flags, sampwritten, byteswritten); | |
| /* clear return parameters if given */ | |
| if (sampwritten != NULL) | |
| *sampwritten = 0; | |
| if (byteswritten != NULL) | |
| *byteswritten = 0; | |
| /* check parameters */ | |
| if (buffer == NULL && (buffersize > 0 || samples > 0)) | |
| return AVIERR_BADPARAM; | |
| /* Do we have write permission? */ | |
| if ((This->uMode & MMIO_RWMODE) == 0) | |
| return AVIERR_READONLY; | |
| /* < 0 means "append" */ | |
| if (start < 0) | |
| start = This->sInfo.dwStart + This->sInfo.dwLength; | |
| /* check buffersize -- must multiple of samplesize */ | |
| if (buffersize & ~(This->sInfo.dwSampleSize - 1)) | |
| return AVIERR_BADSIZE; | |
| /* do we have anything to write? */ | |
| if (buffer != NULL && buffersize > 0) { | |
| This->fDirty = 1; | |
| if (mmioSeek(This->hmmio, This->ckData.dwDataOffset + | |
| start * This->sInfo.dwSampleSize, SEEK_SET) == -1) | |
| return AVIERR_FILEWRITE; | |
| if (mmioWrite(This->hmmio, (HPSTR)buffer, buffersize) != buffersize) | |
| return AVIERR_FILEWRITE; | |
| This->sInfo.dwLength = max(This->sInfo.dwLength, (DWORD)start + samples); | |
| This->ckData.cksize = max(This->ckData.cksize, | |
| start * This->sInfo.dwSampleSize + buffersize); | |
| /* fill out return parameters if given */ | |
| if (sampwritten != NULL) | |
| *sampwritten = samples; | |
| if (byteswritten != NULL) | |
| *byteswritten = buffersize; | |
| } | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, | |
| LONG samples) | |
| { | |
| IAVIFileImpl *This = ((IAVIStreamImpl*)iface)->paf; | |
| TRACE("(%p,%ld,%ld)\n", iface, start, samples); | |
| /* check parameters */ | |
| if (start < 0 || samples < 0) | |
| return AVIERR_BADPARAM; | |
| /* Delete before start of stream? */ | |
| if ((DWORD)(start + samples) < This->sInfo.dwStart) | |
| return AVIERR_OK; | |
| /* Delete after end of stream? */ | |
| if ((DWORD)start > This->sInfo.dwLength) | |
| return AVIERR_OK; | |
| /* For the rest we need write permissions */ | |
| if ((This->uMode & MMIO_RWMODE) == 0) | |
| return AVIERR_READONLY; | |
| if ((DWORD)(start + samples) >= This->sInfo.dwLength) { | |
| /* deletion at end */ | |
| samples = This->sInfo.dwLength - start; | |
| This->sInfo.dwLength -= samples; | |
| This->ckData.cksize -= samples * This->sInfo.dwSampleSize; | |
| } else if ((DWORD)start <= This->sInfo.dwStart) { | |
| /* deletion at start */ | |
| samples = This->sInfo.dwStart - start; | |
| start = This->sInfo.dwStart; | |
| This->ckData.dwDataOffset += samples * This->sInfo.dwSampleSize; | |
| This->ckData.cksize -= samples * This->sInfo.dwSampleSize; | |
| } else { | |
| /* deletion inside stream -- needs playlist and cue's */ | |
| FIXME(": deletion inside of stream not supported!\n"); | |
| return AVIERR_UNSUPPORTED; | |
| } | |
| This->fDirty = 1; | |
| return AVIERR_OK; | |
| } | |
| static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, | |
| LPVOID lp, LPLONG lpread) | |
| { | |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | |
| assert(This->paf != NULL); | |
| return IAVIFile_ReadData((PAVIFILE)This->paf, fcc, lp, lpread); | |
| } | |
| static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, | |
| LPVOID lp, LONG size) | |
| { | |
| IAVIStreamImpl *This = (IAVIStreamImpl *)iface; | |
| return IAVIFile_WriteData((PAVIFILE)This->paf, fcc, lp, size); | |
| } | |
| static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, | |
| LPAVISTREAMINFOW info, LONG infolen) | |
| { | |
| FIXME("(%p,%p,%ld): stub\n", iface, info, infolen); | |
| return E_FAIL; | |
| } | |
| /***********************************************************************/ | |
| static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This) | |
| { | |
| MMCKINFO ckRIFF; | |
| MMCKINFO ck; | |
| This->sInfo.dwLength = 0; /* just to be sure */ | |
| This->fDirty = FALSE; | |
| /* search for RIFF chunk */ | |
| ckRIFF.fccType = 0; /* find any */ | |
| if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) { | |
| return AVIFILE_LoadSunFile(This); | |
| } | |
| if (ckRIFF.fccType != formtypeWAVE) | |
| return AVIERR_BADFORMAT; | |
| /* search WAVE format chunk */ | |
| ck.ckid = ckidWAVEFORMAT; | |
| if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, | |
| &ckRIFF, MMIO_FINDCHUNK) != S_OK) | |
| return AVIERR_FILEREAD; | |
| /* get memory for format and read it */ | |
| This->lpFormat = (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize); | |
| if (This->lpFormat == NULL) | |
| return AVIERR_FILEREAD; | |
| This->cbFormat = ck.cksize; | |
| if (mmioRead(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize) | |
| return AVIERR_FILEREAD; | |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | |
| return AVIERR_FILEREAD; | |
| /* Non-pcm formats have a fact chunk. | |
| * We don't need it, so simply add it to the extra chunks. | |
| */ | |
| /* find the big data chunk */ | |
| This->ckData.ckid = ckidWAVEDATA; | |
| if (FindChunkAndKeepExtras(&This->extra, This->hmmio, &This->ckData, | |
| &ckRIFF, MMIO_FINDCHUNK) != S_OK) | |
| return AVIERR_FILEREAD; | |
| memset(&This->sInfo, 0, sizeof(This->sInfo)); | |
| This->sInfo.fccType = streamtypeAUDIO; | |
| This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; | |
| This->sInfo.dwSampleSize = | |
| This->sInfo.dwScale = This->lpFormat->nBlockAlign; | |
| This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign; | |
| This->sInfo.dwSuggestedBufferSize = This->ckData.cksize; | |
| This->fInfo.dwStreams = 1; | |
| if (mmioAscend(This->hmmio, &This->ckData, 0) != S_OK) { | |
| /* seems to be truncated */ | |
| WARN(": file seems to be truncated!\n"); | |
| This->ckData.cksize = mmioSeek(This->hmmio, 0, SEEK_END) - | |
| This->ckData.dwDataOffset; | |
| This->sInfo.dwLength = This->ckData.cksize / This->lpFormat->nBlockAlign; | |
| This->sInfo.dwSuggestedBufferSize = This->ckData.cksize; | |
| } | |
| /* ignore errors */ | |
| FindChunkAndKeepExtras(&This->extra, This->hmmio, &ck, &ckRIFF, 0); | |
| return AVIERR_OK; | |
| } | |
| static HRESULT AVIFILE_LoadSunFile(IAVIFileImpl *This) | |
| { | |
| SUNAUDIOHEADER auhdr; | |
| mmioSeek(This->hmmio, 0, SEEK_SET); | |
| if (mmioRead(This->hmmio, (HPSTR)&auhdr, sizeof(auhdr)) != sizeof(auhdr)) | |
| return AVIERR_FILEREAD; | |
| if (auhdr.fccType == 0x0064732E) { | |
| /* header in little endian */ | |
| This->ckData.dwDataOffset = LE2H_DWORD(auhdr.offset); | |
| This->ckData.cksize = LE2H_DWORD(auhdr.size); | |
| auhdr.encoding = LE2H_DWORD(auhdr.encoding); | |
| auhdr.sampleRate = LE2H_DWORD(auhdr.sampleRate); | |
| auhdr.channels = LE2H_DWORD(auhdr.channels); | |
| } else if (auhdr.fccType == mmioFOURCC('.','s','n','d')) { | |
| /* header in big endian */ | |
| This->ckData.dwDataOffset = BE2H_DWORD(auhdr.offset); | |
| This->ckData.cksize = BE2H_DWORD(auhdr.size); | |
| auhdr.encoding = BE2H_DWORD(auhdr.encoding); | |
| auhdr.sampleRate = BE2H_DWORD(auhdr.sampleRate); | |
| auhdr.channels = BE2H_DWORD(auhdr.channels); | |
| } else | |
| return AVIERR_FILEREAD; | |
| if (auhdr.channels < 1) | |
| return AVIERR_BADFORMAT; | |
| /* get size of header */ | |
| switch(auhdr.encoding) { | |
| case AU_ENCODING_ADPCM_G721_32: | |
| This->cbFormat = sizeof(G721_ADPCMWAVEFORMAT); break; | |
| case AU_ENCODING_ADPCM_G723_24: | |
| This->cbFormat = sizeof(G723_ADPCMWAVEFORMAT); break; | |
| case AU_ENCODING_ADPCM_G722: | |
| case AU_ENCODING_ADPCM_G723_5: | |
| WARN("unsupported Sun audio format %d\n", auhdr.encoding); | |
| return AVIERR_UNSUPPORTED; /* FIXME */ | |
| default: | |
| This->cbFormat = sizeof(WAVEFORMATEX); break; | |
| }; | |
| This->lpFormat = | |
| (LPWAVEFORMATEX)GlobalAllocPtr(GMEM_MOVEABLE, This->cbFormat); | |
| if (This->lpFormat == NULL) | |
| return AVIERR_MEMORY; | |
| This->lpFormat->nChannels = auhdr.channels; | |
| This->lpFormat->nSamplesPerSec = auhdr.sampleRate; | |
| switch(auhdr.encoding) { | |
| case AU_ENCODING_ULAW_8: | |
| This->lpFormat->wFormatTag = WAVE_FORMAT_MULAW; | |
| This->lpFormat->wBitsPerSample = 8; | |
| break; | |
| case AU_ENCODING_PCM_8: | |
| This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; | |
| This->lpFormat->wBitsPerSample = 8; | |
| break; | |
| case AU_ENCODING_PCM_16: | |
| This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; | |
| This->lpFormat->wBitsPerSample = 16; | |
| break; | |
| case AU_ENCODING_PCM_24: | |
| This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; | |
| This->lpFormat->wBitsPerSample = 24; | |
| break; | |
| case AU_ENCODING_PCM_32: | |
| This->lpFormat->wFormatTag = WAVE_FORMAT_PCM; | |
| This->lpFormat->wBitsPerSample = 32; | |
| break; | |
| case AU_ENCODING_ALAW_8: | |
| This->lpFormat->wFormatTag = WAVE_FORMAT_ALAW; | |
| This->lpFormat->wBitsPerSample = 8; | |
| break; | |
| case AU_ENCODING_ADPCM_G721_32: | |
| This->lpFormat->wFormatTag = WAVE_FORMAT_G721_ADPCM; | |
| This->lpFormat->wBitsPerSample = (3*5*8); | |
| This->lpFormat->nBlockAlign = 15*15*8; | |
| This->lpFormat->cbSize = sizeof(WORD); | |
| ((LPG721_ADPCMWAVEFORMAT)This->lpFormat)->nAuxBlockSize = 0; | |
| break; | |
| case AU_ENCODING_ADPCM_G723_24: | |
| This->lpFormat->wFormatTag = WAVE_FORMAT_G723_ADPCM; | |
| This->lpFormat->wBitsPerSample = (3*5*8); | |
| This->lpFormat->nBlockAlign = 15*15*8; | |
| This->lpFormat->cbSize = 2*sizeof(WORD); | |
| ((LPG723_ADPCMWAVEFORMAT)This->lpFormat)->cbExtraSize = 0; | |
| ((LPG723_ADPCMWAVEFORMAT)This->lpFormat)->nAuxBlockSize = 0; | |
| break; | |
| default: | |
| WARN("unsupported Sun audio format %d\n", auhdr.encoding); | |
| return AVIERR_UNSUPPORTED; | |
| }; | |
| This->lpFormat->nBlockAlign = | |
| (This->lpFormat->nChannels * This->lpFormat->wBitsPerSample) / 8; | |
| if (This->lpFormat->nBlockAlign == 0 && This->lpFormat->wBitsPerSample < 8) | |
| This->lpFormat->nBlockAlign++; | |
| This->lpFormat->nAvgBytesPerSec = | |
| This->lpFormat->nBlockAlign * This->lpFormat->nSamplesPerSec; | |
| This->fDirty = 0; | |
| This->sInfo.fccType = streamtypeAUDIO; | |
| This->sInfo.fccHandler = 0; | |
| This->sInfo.dwFlags = 0; | |
| This->sInfo.wPriority = 0; | |
| This->sInfo.wLanguage = 0; | |
| This->sInfo.dwInitialFrames = 0; | |
| This->sInfo.dwScale = This->lpFormat->nBlockAlign; | |
| This->sInfo.dwRate = This->lpFormat->nAvgBytesPerSec; | |
| This->sInfo.dwStart = 0; | |
| This->sInfo.dwLength = | |
| This->ckData.cksize / This->lpFormat->nBlockAlign; | |
| This->sInfo.dwSuggestedBufferSize = This->sInfo.dwLength; | |
| This->sInfo.dwSampleSize = This->lpFormat->nBlockAlign; | |
| This->fInfo.dwStreams = 1; | |
| This->fInfo.dwScale = 1; | |
| This->fInfo.dwRate = This->lpFormat->nSamplesPerSec; | |
| This->fInfo.dwLength = | |
| MulDiv(This->ckData.cksize, This->lpFormat->nSamplesPerSec, | |
| This->lpFormat->nAvgBytesPerSec); | |
| return AVIERR_OK; | |
| } | |
| static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This) | |
| { | |
| MMCKINFO ckRIFF; | |
| MMCKINFO ck; | |
| mmioSeek(This->hmmio, 0, SEEK_SET); | |
| /* create the RIFF chunk with formtype WAVE */ | |
| ckRIFF.fccType = formtypeWAVE; | |
| ckRIFF.cksize = 0; | |
| if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK) | |
| return AVIERR_FILEWRITE; | |
| /* the next chunk is the format */ | |
| ck.ckid = ckidWAVEFORMAT; | |
| ck.cksize = This->cbFormat; | |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) | |
| return AVIERR_FILEWRITE; | |
| if (This->lpFormat != NULL && This->cbFormat > 0) { | |
| if (mmioWrite(This->hmmio, (HPSTR)This->lpFormat, ck.cksize) != ck.cksize) | |
| return AVIERR_FILEWRITE; | |
| } | |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | |
| return AVIERR_FILEWRITE; | |
| /* fact chunk is needed for non-pcm waveforms */ | |
| if (This->lpFormat != NULL && This->cbFormat > sizeof(PCMWAVEFORMAT) && | |
| This->lpFormat->wFormatTag != WAVE_FORMAT_PCM) { | |
| WAVEFORMATEX wfx; | |
| DWORD dwFactLength; | |
| HACMSTREAM has; | |
| /* try to open an appropriate audio codec to figure out | |
| * data for fact-chunk */ | |
| wfx.wFormatTag = WAVE_FORMAT_PCM; | |
| if (acmFormatSuggest(NULL, This->lpFormat, &wfx, | |
| sizeof(wfx), ACM_FORMATSUGGESTF_WFORMATTAG)) { | |
| acmStreamOpen(&has, NULL, This->lpFormat, &wfx, NULL, | |
| 0, 0, ACM_STREAMOPENF_NONREALTIME); | |
| acmStreamSize(has, This->ckData.cksize, &dwFactLength, | |
| ACM_STREAMSIZEF_SOURCE); | |
| dwFactLength /= wfx.nBlockAlign; | |
| acmStreamClose(has, 0); | |
| /* create the fact chunk */ | |
| ck.ckid = ckidWAVEFACT; | |
| ck.cksize = sizeof(dwFactLength); | |
| /* test for enough space before data chunk */ | |
| if (mmioSeek(This->hmmio, 0, SEEK_CUR) > This->ckData.dwDataOffset | |
| - ck.cksize - 4 * sizeof(DWORD)) | |
| return AVIERR_FILEWRITE; | |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) | |
| return AVIERR_FILEWRITE; | |
| if (mmioWrite(This->hmmio, (HPSTR)&dwFactLength, ck.cksize) != ck.cksize) | |
| return AVIERR_FILEWRITE; | |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | |
| return AVIERR_FILEWRITE; | |
| } else | |
| ERR(": fact chunk is needed for non-pcm files -- currently no codec found, so skipped!\n"); | |
| } | |
| /* if there was extra stuff, we need to fill it with JUNK */ | |
| if (mmioSeek(This->hmmio, 0, SEEK_CUR) + 2 * sizeof(DWORD) < This->ckData.dwDataOffset) { | |
| ck.ckid = ckidAVIPADDING; | |
| ck.cksize = 0; | |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) | |
| return AVIERR_FILEWRITE; | |
| if (mmioSeek(This->hmmio, This->ckData.dwDataOffset | |
| - 2 * sizeof(DWORD), SEEK_SET) == -1) | |
| return AVIERR_FILEWRITE; | |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | |
| return AVIERR_FILEWRITE; | |
| } | |
| /* create the data chunk */ | |
| ck.ckid = ckidWAVEDATA; | |
| ck.cksize = This->ckData.cksize; | |
| if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK) | |
| return AVIERR_FILEWRITE; | |
| if (mmioSeek(This->hmmio, This->ckData.cksize, SEEK_CUR) == -1) | |
| return AVIERR_FILEWRITE; | |
| if (mmioAscend(This->hmmio, &ck, 0) != S_OK) | |
| return AVIERR_FILEWRITE; | |
| /* some optional extra chunks? */ | |
| if (This->extra.lp != NULL && This->extra.cb > 0) { | |
| /* chunk headers are already in structure */ | |
| if (mmioWrite(This->hmmio, This->extra.lp, This->extra.cb) != This->extra.cb) | |
| return AVIERR_FILEWRITE; | |
| } | |
| /* close RIFF chunk */ | |
| if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK) | |
| return AVIERR_FILEWRITE; | |
| if (mmioFlush(This->hmmio, 0) != S_OK) | |
| return AVIERR_FILEWRITE; | |
| return AVIERR_OK; | |
| } |