home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / graphics / audio / wavefile / wavefile.c < prev    next >
C/C++ Source or Header  |  1997-10-05  |  34KB  |  1,311 lines

  1. /**************************************************************************
  2.  *
  3.  *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  4.  *  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  5.  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  6.  *  PURPOSE.
  7.  *
  8.  *  Copyright (C) 1992 - 1997 Microsoft Corporation.  All Rights Reserved.
  9.  *
  10.  **************************************************************************/
  11. /****************************************************************************
  12.  *
  13.  *  WAVEFILE.C
  14.  *
  15.  *  An implementation in C of an AVI File Handler to read standard windows
  16.  *  WAV files as if they were an AVI file with one audio stream.
  17.  *
  18.  ***************************************************************************/
  19.  
  20. #define INC_OLE2
  21. #include <windows.h>
  22. #include <windowsx.h>
  23. #include <mmsystem.h>
  24. #include <string.h>
  25. #include <stdlib.h>
  26. #include <malloc.h>
  27. #include <ctype.h>
  28. #include <vfw.h>
  29. #include "MulDiv32.h"
  30. #include "wavefile.h"
  31.  
  32. #define formtypeWAVE    mmioFOURCC('W', 'A', 'V', 'E')
  33. #define ckidWAVEFORMAT    mmioFOURCC('f', 'm', 't', ' ')
  34. #define ckidWAVEDATA    mmioFOURCC('d', 'a', 't', 'a')
  35.  
  36. LPSTR FAR FileName(LPCSTR lszPath);
  37. //
  38. // Function prototypes and Vtbl for the Unknown interface
  39. //
  40. STDMETHODIMP WaveUnknownQueryInterface(LPUNKNOWN pu, REFIID iid, void FAR* FAR* ppv);
  41. STDMETHODIMP_(ULONG) WaveUnknownAddRef(LPUNKNOWN pu);
  42. STDMETHODIMP_(ULONG) WaveUnknownRelease(LPUNKNOWN pu);
  43.  
  44. IUnknownVtbl UnknownVtbl = {
  45.     WaveUnknownQueryInterface,
  46.     WaveUnknownAddRef,
  47.     WaveUnknownRelease
  48. };
  49.  
  50. //
  51. // Function prototypes and Vtbl for the AVIFile interface
  52. //
  53. STDMETHODIMP WaveFileQueryInterface(PAVIFILE pf, REFIID iid, void FAR* FAR* ppv);
  54. STDMETHODIMP_(ULONG) WaveFileAddRef(PAVIFILE pf);
  55. STDMETHODIMP_(ULONG) WaveFileRelease(PAVIFILE pf);
  56. STDMETHODIMP WaveFileInfo(PAVIFILE pf, AVIFILEINFOW FAR * pfi, LONG lSize);
  57. STDMETHODIMP WaveFileGetStream(PAVIFILE pf, PAVISTREAM FAR * ppavi, DWORD fccType, LONG lParam);
  58. STDMETHODIMP WaveFileCreateStream(PAVIFILE pf, PAVISTREAM FAR *ppstream, AVISTREAMINFOW FAR *psi);
  59. STDMETHODIMP WaveFileWriteData(PAVIFILE pf, DWORD ckid, LPVOID lpData, LONG cbData);
  60. STDMETHODIMP WaveFileReadData(PAVIFILE pf, DWORD ckid, LPVOID lpData, LONG FAR *lpcbData);
  61. STDMETHODIMP WaveFileEndRecord(PAVIFILE pf);
  62. STDMETHODIMP WaveFileDeleteStream(PAVIFILE pf, DWORD fccType, LONG lParam);
  63.  
  64.  
  65. IAVIFileVtbl FileVtbl = {
  66.     WaveFileQueryInterface,
  67.     WaveFileAddRef,
  68.     WaveFileRelease,
  69.     WaveFileInfo,
  70.     WaveFileGetStream,
  71.     WaveFileCreateStream,
  72.     WaveFileWriteData,
  73.     WaveFileReadData,
  74.     WaveFileEndRecord,
  75.     WaveFileDeleteStream
  76. };
  77.  
  78.  
  79. STDMETHODIMP WavePersistQueryInterface(LPPERSISTFILE pf, REFIID iid, void FAR* FAR* ppv);
  80. STDMETHODIMP_(ULONG) WavePersistAddRef(LPPERSISTFILE pf);
  81. STDMETHODIMP_(ULONG) WavePersistRelease(LPPERSISTFILE pf);
  82. STDMETHODIMP WavePersistGetClassID (LPPERSISTFILE ppf, LPCLSID lpClassID);
  83. STDMETHODIMP WavePersistIsDirty (LPPERSISTFILE ppf);
  84. STDMETHODIMP WavePersistLoad (LPPERSISTFILE ppf,
  85.                   LPCOLESTR lpszFileName, DWORD grfMode);
  86. STDMETHODIMP WavePersistSave (LPPERSISTFILE ppf,
  87.                   LPCOLESTR lpszFileName, BOOL fRemember);
  88. STDMETHODIMP WavePersistSaveCompleted (LPPERSISTFILE ppf,
  89.                        LPCOLESTR lpszFileName);
  90. STDMETHODIMP WavePersistGetCurFile (LPPERSISTFILE ppf,
  91.                     LPOLESTR FAR * lplpszFileName);
  92.  
  93.  
  94. IPersistFileVtbl PersistVtbl = {
  95.     WavePersistQueryInterface,
  96.     WavePersistAddRef,
  97.     WavePersistRelease,
  98.     WavePersistGetClassID,
  99.     WavePersistIsDirty,
  100.     WavePersistLoad,
  101.     WavePersistSave,
  102.     WavePersistSaveCompleted,
  103.     WavePersistGetCurFile
  104. };
  105.  
  106. //
  107. // Function prototypes and Vtbl for the AVIStream interface
  108. //
  109. STDMETHODIMP WaveStreamQueryInterface(PAVISTREAM ps, REFIID riid, LPVOID FAR* ppvObj);
  110. STDMETHODIMP WaveStreamCreate(PAVISTREAM ps, LONG lParam1, LONG lParam2);
  111. STDMETHODIMP_(ULONG) WaveStreamAddRef(PAVISTREAM ps);
  112. STDMETHODIMP_(ULONG) WaveStreamRelease(PAVISTREAM ps);
  113. STDMETHODIMP WaveStreamInfo(PAVISTREAM ps, AVISTREAMINFOW FAR * psi, LONG lSize);
  114. STDMETHODIMP_(LONG) WaveStreamFindSample(PAVISTREAM ps, LONG lPos, LONG lFlags);
  115. STDMETHODIMP WaveStreamReadFormat(PAVISTREAM ps, LONG lPos, LPVOID lpFormat, LONG FAR *lpcbFormat);
  116. STDMETHODIMP WaveStreamSetFormat(PAVISTREAM ps, LONG lPos, LPVOID lpFormat, LONG cbFormat);
  117. STDMETHODIMP WaveStreamRead(PAVISTREAM ps, LONG lStart, LONG lSamples, LPVOID lpBuffer, LONG cbBuffer, LONG FAR * plBytes,LONG FAR * plSamples);
  118. STDMETHODIMP WaveStreamWrite(PAVISTREAM ps, LONG lStart, LONG lSamples, LPVOID lpData, LONG cbData, DWORD dwFlags, LONG FAR *plSampWritten, LONG FAR *plBytesWritten);
  119. STDMETHODIMP WaveStreamDelete(PAVISTREAM ps, LONG lStart, LONG lSamples);
  120. STDMETHODIMP WaveStreamReadData(PAVISTREAM ps, DWORD fcc, LPVOID lp,LONG FAR *lpcb);
  121. STDMETHODIMP WaveStreamWriteData(PAVISTREAM ps, DWORD fcc, LPVOID lp,LONG cb);
  122. STDMETHODIMP WaveStreamSetInfo(PAVISTREAM ps, AVISTREAMINFOW FAR * psi, LONG lSize);
  123.  
  124. IAVIStreamVtbl StreamVtbl = {
  125.     WaveStreamQueryInterface,
  126.     WaveStreamAddRef,
  127.     WaveStreamRelease,
  128.     WaveStreamCreate,
  129.     WaveStreamInfo,
  130.     WaveStreamFindSample,
  131.     WaveStreamReadFormat,
  132.     WaveStreamSetFormat,
  133.     WaveStreamRead,
  134.     WaveStreamWrite,
  135.     WaveStreamDelete,
  136.     WaveStreamReadData,
  137.     WaveStreamWriteData,
  138.     WaveStreamSetInfo
  139. };
  140.  
  141.  
  142. #if !defined UNICODE
  143.  
  144. int LoadUnicodeString(HINSTANCE hinst, UINT wID, LPWSTR lpBuffer, int cchBuffer)
  145. {
  146.     char    ach[128];
  147.     int        i;
  148.  
  149.     i = LoadString(hinst, wID, ach, sizeof(ach));
  150.  
  151.     if (i > 0)
  152.     MultiByteToWideChar(CP_ACP, 0, ach, -1, lpBuffer, cchBuffer);
  153.  
  154.     return i;
  155. }
  156.  
  157. #else
  158. #define LoadUnicodeString   LoadString
  159. #endif
  160.  
  161.  
  162. ///////////////////////////////////////////////////////////////////////////
  163. ///////////////////////////////////////////////////////////////////////////
  164. ///////////////////////////////////////////////////////////////////////////
  165.  
  166. /*    -    -    -    -    -    -    -    -    */
  167.  
  168. UINT    uUseCount;    // the reference count for our objects
  169. UINT    uLockCount;    // our lock count for LockServer
  170.  
  171. /*    -    -    -    -    -    -    -    -    */
  172.  
  173. //
  174. // Create a new instance.  Since this is a C implementation we have to
  175. // allocate space for our structure ourselves.
  176. //
  177. HRESULT WaveFileCreate(
  178.     IUnknown FAR*    pUnknownOuter,
  179.     REFIID        riid,
  180.     void FAR* FAR*    ppv)
  181. {
  182.     IUnknown FAR*    pUnknown;
  183.     LPWAVESTUFF    pWaveStuff;
  184.     HRESULT    hresult;
  185.  
  186.     // Allocate space for our structure
  187.     pWaveStuff = (LPWAVESTUFF)GlobalAllocPtr(GMEM_MOVEABLE,
  188.         sizeof(WAVESTUFF));
  189.     if (!pWaveStuff)
  190.         return ResultFromScode(E_OUTOFMEMORY);
  191.  
  192.     // Initialize the Vtbls
  193.     pWaveStuff->AVIFile = &FileVtbl;
  194.     pWaveStuff->AVIStream = &StreamVtbl;
  195.     pWaveStuff->Unknown = &UnknownVtbl;
  196.     pWaveStuff->Persist = &PersistVtbl;
  197.  
  198.     // Set up our controlling object
  199.     pUnknown = (IUnknown FAR *)&pWaveStuff->Unknown;
  200.     if (pUnknownOuter)
  201.         pWaveStuff->pUnknownOuter = pUnknownOuter;
  202.     else
  203.         pWaveStuff->pUnknownOuter =(IUnknown FAR *)&pWaveStuff->Unknown;
  204.  
  205.     // Initial the things in our structure
  206.     pWaveStuff->refs = 0;
  207.     pWaveStuff->hmmio = NULL;
  208.     pWaveStuff->lpFormat = NULL;
  209.     pWaveStuff->cbFormat = 0L;
  210.     pWaveStuff->fDirty = FALSE;
  211.     pWaveStuff->extra.lp = NULL;
  212.     pWaveStuff->extra.cb = 0L;
  213.  
  214.     // Call our Query interface to increment our ref count and get a
  215.     // pointer to our interface to return.
  216.     hresult = pUnknown->lpVtbl->QueryInterface(pUnknown, riid, ppv);
  217.  
  218.     if (FAILED(GetScode(hresult)))
  219.         GlobalFreePtr(pWaveStuff);
  220.     return hresult;
  221. }
  222.  
  223. /*    -    -    -    -    -    -    -    -    */
  224.  
  225. //
  226. // Query interface from all three interfaces comes here.  We support the
  227. // Unknown interface, AVIStream and AVIFile.
  228. //
  229. STDMETHODIMP WaveUnknownQueryInterface(
  230.     LPUNKNOWN    pu,
  231.     REFIID        iid,
  232.     void FAR* FAR*    ppv)
  233. {
  234.     // Get a pointer to our structure
  235.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_UNKNOWN(pu);
  236.  
  237.     if (IsEqualIID(iid, &IID_IUnknown))
  238.         *ppv = (LPVOID)&pWaveStuff->Unknown;
  239.     else if (IsEqualIID(iid, &IID_IAVIFile))
  240.         *ppv = (LPVOID)&pWaveStuff->AVIFile;
  241.     else if (IsEqualIID(iid, &IID_IAVIStream))
  242.         *ppv = (LPVOID)&pWaveStuff->AVIStream;
  243.     else if (IsEqualIID(iid, &IID_IPersistFile))
  244.         *ppv = (LPVOID)&pWaveStuff->Persist;
  245.     else
  246.         return ResultFromScode(E_NOINTERFACE);
  247.     pu->lpVtbl->AddRef(pu);
  248.     return NOERROR;
  249. }
  250.  
  251. /*    -    -    -    -    -    -    -    -    */
  252.  
  253. //
  254. // Increase our reference count.  AddRef for all three interfaces comes here.
  255. //
  256. STDMETHODIMP_(ULONG) WaveUnknownAddRef(
  257.     LPUNKNOWN    pu)
  258. {
  259.     // Get a pointer to our structure
  260.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_UNKNOWN(pu);
  261.  
  262.     uUseCount++;
  263.     return ++pWaveStuff->refs;
  264. }
  265.  
  266. /*    -    -    -    -    -    -    -    -    */
  267.  
  268. //
  269. // Decrease our reference count.  Release for all three interfaces comes here.
  270. //
  271. STDMETHODIMP_(ULONG) WaveUnknownRelease(
  272.     LPUNKNOWN pu)
  273. {
  274.     // Get a pointer to our structure
  275.     LPWAVESTUFF p = WAVESTUFF_FROM_UNKNOWN(pu);
  276.  
  277.     uUseCount--;
  278.  
  279.     //
  280.     // Ref count is zero.  Close the file.  If we've been writing to it, it's
  281.     // clean-up time!
  282.     //
  283.     if (!--p->refs) {
  284.     LONG lRet = AVIERR_OK;
  285.     
  286.     if (p->fDirty) {
  287.         MMCKINFO ckRIFF;
  288.         MMCKINFO ck;
  289.  
  290.         mmioSeek(p->hmmio, 0, SEEK_SET);
  291.  
  292.         /* create the output file RIFF chunk of form type 'WAVE' */
  293.         ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
  294.         ckRIFF.cksize = 0L;    // let MMIO figure out ck. size
  295.         if (mmioCreateChunk(p->hmmio, &ckRIFF, MMIO_CREATERIFF) != 0)
  296.             goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  297.  
  298.         ck.ckid = mmioFOURCC('f', 'm', 't', ' ');
  299.         ck.cksize = p->cbFormat;        // we know the size of this ck.
  300.         if (mmioCreateChunk(p->hmmio, &ck, 0) != 0)
  301.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  302.  
  303.         if (mmioWrite(p->hmmio, (HPSTR) p->lpFormat, p->cbFormat) != p->cbFormat)
  304.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  305.  
  306.         /* ascend out of the 'fmt' chunk, back into 'RIFF' chunk */
  307.         if (mmioAscend(p->hmmio, &ck, 0) != 0)
  308.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  309.  
  310.         // If there was extra stuff here, we need to fill it!
  311.         if (mmioSeek(p->hmmio, 0, SEEK_CUR)
  312.             + 2 * (LRESULT)sizeof(DWORD)
  313.             != (LRESULT) p->ckData.dwDataOffset) {
  314.             /* create the 'data' chunk that holds the waveform samples */
  315.             ck.ckid = mmioFOURCC('J', 'U', 'N', 'K');
  316.             ck.cksize = 0;
  317.             if (mmioCreateChunk(p->hmmio, &ck, 0) != 0)
  318.                 goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  319.  
  320.             mmioSeek(p->hmmio,
  321.                 p->ckData.dwDataOffset - 2 * sizeof(DWORD),
  322.                 SEEK_SET);
  323.  
  324.             if (mmioAscend(p->hmmio, &ck, 0) != 0)
  325.                 goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  326.         }
  327.  
  328.         /* create the 'data' chunk that holds the waveform samples */
  329.         ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
  330.         ck.cksize = p->ckData.cksize;
  331.         if (mmioCreateChunk(p->hmmio, &ck, 0) != 0)
  332.         goto ERROR_CANNOT_WRITE;    // cannot write file, probably
  333.  
  334.         mmioSeek(p->hmmio, p->ckData.cksize, SEEK_CUR);
  335.  
  336.         mmioAscend(p->hmmio, &ck, 0);
  337.  
  338.         if (p->extra.cb) {
  339.         if (mmioWrite(p->hmmio, (HPSTR) p->extra.lp, p->extra.cb) != p->extra.cb)
  340.             goto ERROR_CANNOT_WRITE;
  341.         }
  342.  
  343.         if (mmioAscend(p->hmmio, &ckRIFF, 0) != 0)
  344.         goto ERROR_CANNOT_WRITE;
  345.  
  346.         if (mmioFlush(p->hmmio, 0) != 0)
  347.         goto ERROR_CANNOT_WRITE;
  348.     }
  349.  
  350.  
  351.     goto success;
  352.  
  353.     ERROR_CANNOT_WRITE:
  354.     lRet = AVIERR_FILEWRITE;
  355.  
  356.     success:
  357.     if (p->hmmio)
  358.         mmioClose(p->hmmio, 0);
  359.  
  360.     if (p->lpFormat)
  361.         GlobalFreePtr(p->lpFormat);
  362.  
  363.     // Free the memory for our structure.
  364.     GlobalFreePtr(p);
  365.     return 0;
  366.     }
  367.     return p->refs;
  368. }
  369.  
  370.  
  371. //
  372. // Use our controlling object to call QueryInterface on Unknown
  373. //
  374. STDMETHODIMP WaveFileQueryInterface(
  375.     PAVIFILE    pf,
  376.     REFIID        iid,
  377.     void FAR* FAR*    ppv)
  378. {
  379.     // Get a pointer to our structure
  380.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_FILE(pf);
  381.  
  382.     return pWaveStuff->pUnknownOuter->lpVtbl->QueryInterface(
  383.         pWaveStuff->pUnknownOuter, iid, ppv);
  384. }
  385.  
  386. /*    -    -    -    -    -    -    -    -    */
  387.  
  388. //
  389. // Use our controlling object to call AddRef on Unknown
  390. //
  391. STDMETHODIMP_(ULONG) WaveFileAddRef(
  392.     PAVIFILE    pf)
  393. {
  394.     // Get a pointer to our structure
  395.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_FILE(pf);
  396.  
  397.     return pWaveStuff->pUnknownOuter->lpVtbl->AddRef(
  398.         pWaveStuff->pUnknownOuter);
  399. }
  400.  
  401. /*    -    -    -    -    -    -    -    -    */
  402.  
  403. //
  404. // Use our controlling object to call Release on Unknown
  405. //
  406. STDMETHODIMP_(ULONG) WaveFileRelease(
  407.     PAVIFILE    pf)
  408. {
  409.     // Get a pointer to our structure
  410.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_FILE(pf);
  411.  
  412.     return pWaveStuff->pUnknownOuter->lpVtbl->Release(
  413.         pWaveStuff->pUnknownOuter);
  414. }
  415.  
  416. /*    -    -    -    -    -    -    -    -    */
  417.  
  418.  
  419. //
  420. // Use our controlling object to call QueryInterface on Unknown
  421. //
  422. STDMETHODIMP WavePersistQueryInterface(
  423.     LPPERSISTFILE    ppf,
  424.     REFIID        iid,
  425.     void FAR* FAR*    ppv)
  426. {
  427.     // Get a pointer to our structure
  428.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_PERSIST(ppf);
  429.  
  430.     return pWaveStuff->pUnknownOuter->lpVtbl->QueryInterface(
  431.         pWaveStuff->pUnknownOuter, iid, ppv);
  432. }
  433.  
  434. /*    -    -    -    -    -    -    -    -    */
  435.  
  436. //
  437. // Use our controlling object to call AddRef on Unknown
  438. //
  439. STDMETHODIMP_(ULONG) WavePersistAddRef(
  440.     LPPERSISTFILE    ppf)
  441. {
  442.     // Get a pointer to our structure
  443.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_PERSIST(ppf);
  444.  
  445.     return pWaveStuff->pUnknownOuter->lpVtbl->AddRef(
  446.         pWaveStuff->pUnknownOuter);
  447. }
  448.  
  449. /*    -    -    -    -    -    -    -    -    */
  450.  
  451. //
  452. // Use our controlling object to call Release on Unknown
  453. //
  454. STDMETHODIMP_(ULONG) WavePersistRelease(
  455.     LPPERSISTFILE    ppf)
  456. {
  457.     // Get a pointer to our structure
  458.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_PERSIST(ppf);
  459.  
  460.     return pWaveStuff->pUnknownOuter->lpVtbl->Release(
  461.         pWaveStuff->pUnknownOuter);
  462. }
  463.  
  464. /*    -    -    -    -    -    -    -    -    */
  465.  
  466.  
  467.  
  468. //
  469. // Use our controlling object to call QueryInterface on Unknown
  470. //
  471. STDMETHODIMP WaveStreamQueryInterface(
  472.     PAVISTREAM    ps,
  473.     REFIID        iid,
  474.     void FAR* FAR*    ppv)
  475. {
  476.     // Get a pointer to our structure
  477.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_STREAM(ps);
  478.  
  479.     return pWaveStuff->pUnknownOuter->lpVtbl->QueryInterface(
  480.         pWaveStuff->pUnknownOuter, iid, ppv);
  481. }
  482.  
  483. /*    -    -    -    -    -    -    -    -    */
  484.  
  485. //
  486. // Use our controlling object to call AddRef on Unknown
  487. //
  488. STDMETHODIMP_(ULONG) WaveStreamAddRef(
  489.     PAVISTREAM    ps)
  490. {
  491.     // Get a pointer to our structure
  492.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_STREAM(ps);
  493.  
  494.     return pWaveStuff->pUnknownOuter->lpVtbl->AddRef(
  495.         pWaveStuff->pUnknownOuter);
  496. }
  497.  
  498. /*    -    -    -    -    -    -    -    -    */
  499.  
  500. //
  501. // Use our controlling object to call Release on Unknown
  502. //
  503. STDMETHODIMP_(ULONG) WaveStreamRelease(
  504.     PAVISTREAM    ps)
  505. {
  506.     // Get a pointer to our structure
  507.     LPWAVESTUFF pWaveStuff = WAVESTUFF_FROM_STREAM(ps);
  508.  
  509.     return pWaveStuff->pUnknownOuter->lpVtbl->Release(
  510.         pWaveStuff->pUnknownOuter);
  511. }
  512.  
  513. /*    -    -    -    -    -    -    -    -    */
  514.  
  515. #define SLASH(c)    ((c) == '/' || (c) == '\\')
  516.  
  517. /*--------------------------------------------------------------+
  518. | FileName  - return a pointer to the filename part of szPath   |
  519. |             with no preceding path.                           |
  520. +--------------------------------------------------------------*/
  521. LPSTR FAR FileName(
  522.     LPCSTR lszPath)
  523. {
  524.     LPCSTR lszCur;
  525.  
  526.     for (lszCur = lszPath + lstrlen(lszPath); lszCur > lszPath && !SLASH(*lszCur) && *lszCur != ':';)
  527.     lszCur = AnsiPrev(lszPath, lszCur);
  528.     if (lszCur == lszPath)
  529.     return (LPSTR)lszCur;
  530.     else
  531.     return (LPSTR)(lszCur + 1);
  532. }
  533.  
  534. /*    -    -    -    -    -    -    -    -    */
  535.  
  536. //
  537. // The Open Method for our File interface - Open a WAVE file
  538. //
  539. STDMETHODIMP WaveFileOpen(
  540.     PAVIFILE pf,
  541.     LPCSTR szFile,
  542.     UINT mode)
  543. {
  544.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  545.     UINT    ui;
  546.     char    ach[80];
  547.  
  548.     // !!! Assumptions about the AVIFILE.DLL (which calls us):
  549.     // We will only see READWRITE mode, never only WRITE mode.
  550.  
  551.     //
  552.     // try to open the actual file, first with share, then without.
  553.     // You may need to use specific flags in order to open a file
  554.     // that's already open by somebody else.
  555.     //
  556.  
  557.     // If the first attempt fails, no system error box, please.
  558.     ui = SetErrorMode(SEM_NOOPENFILEERRORBOX);
  559.     p->hmmio = mmioOpen((LPSTR) szFile, NULL, MMIO_ALLOCBUF | mode);
  560.     if (!p->hmmio && ((mode & MMIO_RWMODE) == OF_READ)) {
  561.     // if the open fails, try again without the share flags.
  562.         mode &= ~(MMIO_SHAREMODE);
  563.         p->hmmio = mmioOpen((LPSTR) szFile, NULL, MMIO_ALLOCBUF | mode);
  564.     }
  565.     SetErrorMode(ui);
  566.     
  567.     //
  568.     // Now set up our structure
  569.     //
  570.  
  571.     p->mode = mode;
  572.     
  573.     if (!p->hmmio)
  574.         goto error;
  575.  
  576.     _fmemset(&p->avistream, 0, sizeof(p->avistream));
  577.  
  578. // If this is defined, we pretend that the data is at FPSHACK "frames"
  579. // per second in the main header, otherwise we use the sample
  580. // rate of the audio, which looks somewhat strange in MPlayer.
  581. #define FPSHACK    1000
  582.     
  583.     _fmemset(&p->avihdr, 0, sizeof(p->avihdr));
  584.  
  585. #ifdef FPSHACK
  586.     //
  587.     // Initialize our AVIFILEHEADER
  588.     //
  589.     p->avihdr.dwRate = FPSHACK;
  590.     p->avihdr.dwScale = 1;
  591. #endif
  592.     
  593.     p->avihdr.dwStreams = 1;
  594.     LoadUnicodeString(ghModule, IDS_FILETYPE, p->avihdr.szFileType,
  595.     sizeof(p->avihdr.szFileType));
  596.     
  597.     //
  598.     // Initialize our AVISTREAMHEADER
  599.     //
  600.     LoadString(ghModule, IDS_STREAMNAME, ach, sizeof(ach));
  601. #if !defined UNICODE
  602.     {
  603.         char    achTemp[64];
  604.  
  605.         wsprintf(achTemp, ach, FileName(szFile));
  606.  
  607.         MultiByteToWideChar(CP_ACP, 0, achTemp, -1,
  608.             p->avistream.szName, 64);
  609.     }
  610. #else
  611.     wsprintf(p->avistream.szName, ach, FileName(szFile));
  612. #endif
  613.  
  614.     if (mode & OF_CREATE) {    // Brand new file
  615.         p->avistream.fccType = streamtypeAUDIO;
  616.         p->avistream.fccHandler = 0;
  617.         p->avistream.dwFlags = 0;
  618.         p->avistream.wPriority = 0;
  619.         p->avistream.wLanguage = 0;
  620.         p->avistream.dwInitialFrames = 0;
  621.         p->avistream.dwScale = 0;
  622.         p->avistream.dwRate = 0;
  623.         p->avistream.dwStart = 0;
  624.         p->avistream.dwLength = 0;
  625.         p->avistream.dwSuggestedBufferSize = 0;
  626.         p->avistream.dwSampleSize = 0;
  627.     
  628.         p->fDirty = TRUE;
  629.     
  630.     } else {        // read the existing file to get info
  631.  
  632.         MMCKINFO    ck;
  633.         MMCKINFO    ckRIFF;
  634.         /* Read RIFF chunk */
  635.         if (mmioDescend(p->hmmio, &ckRIFF, NULL, 0) != 0)
  636.             goto error;
  637.  
  638.         if (ckRIFF.ckid != FOURCC_RIFF)
  639.             goto error;
  640.  
  641.         if (ckRIFF.fccType != formtypeWAVE)
  642.             goto error;
  643.  
  644.         /* Read WAVE format chunk */
  645.         ck.ckid = ckidWAVEFORMAT;
  646.         if (FindChunkAndKeepExtras(&p->extra, p->hmmio, &ck, &ckRIFF, MMIO_FINDCHUNK))
  647.             goto error;
  648.  
  649.         p->cbFormat = ck.cksize;
  650.         p->lpFormat = (LPWAVEFORMAT) GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize);
  651.  
  652.         if (p->lpFormat == NULL)
  653.             goto error;
  654.  
  655.         if (mmioRead(p->hmmio,
  656.             (HPSTR) p->lpFormat,
  657.             (LONG)ck.cksize) != (LONG)ck.cksize)
  658.             goto error;
  659.  
  660.  
  661.         /* Ascend out of stream header */
  662.         if (mmioAscend(p->hmmio, &ck, 0) != 0)
  663.             goto error;
  664.  
  665.         /* Find big data chunk */
  666.         p->ckData.ckid = ckidWAVEDATA;
  667.         if (FindChunkAndKeepExtras(&p->extra, p->hmmio, &p->ckData, &ckRIFF, MMIO_FINDCHUNK))
  668.             goto error;
  669.  
  670.         p->fDirty = FALSE;
  671.     
  672.         p->avistream.fccType = streamtypeAUDIO;
  673.         p->avistream.fccHandler = 0;
  674.         p->avistream.dwFlags = 0;
  675.         p->avistream.wPriority = 0;
  676.         p->avistream.wLanguage = 0;
  677.         p->avistream.dwInitialFrames = 0;
  678.         p->avistream.dwScale = p->lpFormat->nBlockAlign;
  679.         p->avistream.dwRate = p->lpFormat->nAvgBytesPerSec;
  680.         p->avistream.dwStart = 0;
  681.         p->avistream.dwLength = p->ckData.cksize / p->lpFormat->nBlockAlign;
  682.         p->avistream.dwSuggestedBufferSize = 0;
  683.         p->avistream.dwSampleSize = p->lpFormat->nBlockAlign;
  684.     
  685. #ifdef FPSHACK
  686.         p->avihdr.dwLength = MulDiv32(p->avistream.dwLength,
  687.                     p->avistream.dwScale * FPSHACK,
  688.                     p->avistream.dwRate);
  689. #else
  690.         p->avihdr.dwScale = 1;
  691.         p->avihdr.dwRate = p->lpFormat->nSamplesPerSec;
  692.         p->avihdr.dwLength = MulDiv32(p->ckData.cksize,
  693.                     p->lpFormat->nSamplesPerSec,
  694.                     p->lpFormat->nAvgBytesPerSec);
  695. #endif
  696.  
  697.  
  698.         mmioAscend(p->hmmio, &p->ckData, 0);
  699.     
  700.         // Read extra data at end of file....
  701.         if (FindChunkAndKeepExtras(&p->extra, p->hmmio, &ckRIFF, &ck, 0)
  702.             != AVIERR_OK)
  703.             goto error;
  704.     }
  705.     
  706.     //
  707.     // all done return success.
  708.     //
  709.     return ResultFromScode(0); // success
  710.     
  711. error:
  712.     return ResultFromScode(AVIERR_FILEREAD);
  713. }
  714.  
  715.  
  716. //
  717. // Get a stream from the file... Each WAVE file has exactly 1 audio stream.
  718. //
  719. STDMETHODIMP WaveFileGetStream(
  720.     PAVIFILE pf,
  721.     PAVISTREAM FAR * ppavi,
  722.     DWORD fccType,
  723.     LONG lParam)
  724. {
  725.     int iStreamWant;
  726.     // Get a pointer to our structure
  727.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  728.  
  729.     iStreamWant = (int)lParam;
  730.  
  731.     if (p->lpFormat == NULL)
  732.         return ResultFromScode(AVIERR_BADPARAM);
  733.     
  734.     // We only support one stream
  735.     if (iStreamWant != 0)
  736.         return ResultFromScode(AVIERR_BADPARAM);
  737.  
  738.     // We only support audio streams
  739.     if (fccType && fccType != streamtypeAUDIO)
  740.         return ResultFromScode(AVIERR_BADPARAM);
  741.  
  742.     // increase the reference count
  743.     p->AVIStream->AddRef((PAVISTREAM)&p->AVIStream);
  744.     
  745.     // Return a pointer to our stream Vtbl
  746.     *ppavi = (PAVISTREAM) &(p->AVIStream);
  747.     return ResultFromScode(AVIERR_OK);
  748. }
  749.  
  750.  
  751. STDMETHODIMP WaveFileDeleteStream(PAVIFILE pf, DWORD fccType, LONG lParam)
  752. {
  753.     int iStreamWant;
  754.     // Get a pointer to our structure
  755.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  756.  
  757.     iStreamWant = (int)lParam;
  758.  
  759.     if (p->lpFormat == NULL)
  760.         return ResultFromScode(AVIERR_BADPARAM);
  761.     
  762.     // We only support one stream
  763.     if (iStreamWant != 0)
  764.         return ResultFromScode(AVIERR_BADPARAM);
  765.  
  766.     // We only support audio streams
  767.     if (fccType && fccType != streamtypeAUDIO)
  768.         return ResultFromScode(AVIERR_BADPARAM);
  769.  
  770.  
  771.     GlobalFreePtr(p->lpFormat);
  772.     p->lpFormat = NULL;
  773.  
  774.     return NOERROR;
  775. }
  776.  
  777. //
  778. // We don't support the Save Method of the File Interface (We don't save)
  779. //
  780. STDMETHODIMP WaveFileSave(
  781.     PAVIFILE pf,
  782.     LPCSTR szFile,
  783.     AVICOMPRESSOPTIONS FAR *lpOptions,
  784.     AVISAVECALLBACK lpfnCallback)
  785. {
  786.     return ResultFromScode(AVIERR_UNSUPPORTED);
  787. }
  788.  
  789. //
  790. // Method to create a stream in a WAVE file.  We only support this for blank
  791. // WAVE files.
  792. //
  793. STDMETHODIMP WaveFileCreateStream(
  794.     PAVIFILE pf,
  795.     PAVISTREAM FAR *ppstream,
  796.     AVISTREAMINFOW FAR *psi)
  797. {
  798.     // Get a pointer to our structure
  799.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  800.  
  801.     // We can't add a second stream to a file
  802.     if (p->lpFormat)
  803.         return ResultFromScode(AVIERR_UNSUPPORTED);
  804.  
  805.     // We only like audio....
  806.     if (psi->fccType != streamtypeAUDIO)
  807.         return ResultFromScode(AVIERR_UNSUPPORTED);
  808.     
  809.     // Increase our reference count.
  810.     p->AVIStream->AddRef((PAVISTREAM)&p->AVIStream);
  811.  
  812.     p->cbFormat = 0;
  813.     p->lpFormat = NULL;
  814.  
  815.     // Return a pointer to our stream Vtbl.
  816.     *ppstream = (PAVISTREAM) &(p->AVIStream);
  817.     
  818.     return ResultFromScode(AVIERR_OK);
  819. }
  820.  
  821. //
  822. // The WriteData Method of the File interface
  823. //
  824. STDMETHODIMP WaveFileWriteData(
  825.     PAVIFILE pf,
  826.     DWORD ckid,
  827.     LPVOID lpData,
  828.     LONG cbData)
  829. {
  830.     // Get a pointer to our structure
  831.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  832.  
  833.     // Write the data in the Wave File.
  834.     return ResultFromScode(WriteExtra(&p->extra, ckid, lpData, cbData));
  835. }
  836.  
  837. //
  838. // The ReadData Method of the File interface
  839. //
  840. STDMETHODIMP WaveFileReadData(
  841.     PAVIFILE pf,
  842.     DWORD ckid,
  843.     LPVOID lpData,
  844.     LONG FAR *lpcbData)
  845. {
  846.     // Get a pointer to our structure
  847.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  848.  
  849.     // Read the data from the file
  850.     return ResultFromScode(ReadExtra(&p->extra, ckid, lpData, lpcbData));
  851. }
  852.  
  853. //
  854. // The EndRecord Method of the File interface.. this doesn't need to do
  855. // anything.. (no concept of interleaving or packaging streams)
  856. //
  857. STDMETHODIMP WaveFileEndRecord(
  858.     PAVIFILE pf)
  859. {
  860.     return ResultFromScode(AVIERR_OK);
  861. }
  862.  
  863.  
  864. //
  865. // The Info Method of the File interface
  866. //
  867. STDMETHODIMP WaveFileInfo(
  868.     PAVIFILE pf,
  869.     AVIFILEINFOW FAR * pfi,
  870.     LONG lSize)
  871. {
  872.     // Get a pointer to our structure
  873.     LPWAVESTUFF p = WAVESTUFF_FROM_FILE(pf);
  874.  
  875.     // Return an AVIFILEHEADER.
  876.     hmemcpy(pfi, &p->avihdr, min(lSize, sizeof(p->avihdr)));
  877.     return 0;
  878. }
  879.  
  880.  
  881.  
  882. //
  883. // The Create Method of the Stream interface. We can't create streams that
  884. // aren't attached to the file.
  885. //
  886. STDMETHODIMP WaveStreamCreate(
  887.     PAVISTREAM    ps,
  888.     LONG lParam1,
  889.     LONG lParam2)
  890. {
  891.     return ResultFromScode(AVIERR_UNSUPPORTED);
  892. }
  893.  
  894.  
  895. //
  896. // The FindSample Method of the Stream interface
  897. //
  898. STDMETHODIMP_(LONG) WaveStreamFindSample(
  899.     PAVISTREAM    ps,
  900.     LONG lPos, LONG lFlags)
  901. {
  902.     if (lFlags & FIND_FORMAT) {
  903.         if ((lFlags & FIND_NEXT) && lPos > 0)
  904.             return -1;
  905.         else
  906.             return 0;
  907.     }
  908.  
  909.     return lPos;
  910. }
  911.  
  912.  
  913. //
  914. // The ReadFormat Method of the Stream interface
  915. //
  916. STDMETHODIMP WaveStreamReadFormat(
  917.     PAVISTREAM    ps,
  918.     LONG lPos,
  919.     LPVOID lpFormat,
  920.     LONG FAR *lpcbFormat)
  921. {
  922.     // Get a pointer to our structure
  923.     LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);
  924.  
  925.     // No buffer to fill in, this means return the size needed.
  926.     if (lpFormat == NULL || *lpcbFormat == 0) {
  927.         *lpcbFormat = p->cbFormat;
  928.         return 0;
  929.     }
  930.  
  931.     // Give them the WAVE format.
  932.     hmemcpy(lpFormat, p->lpFormat, min(*lpcbFormat, p->cbFormat));
  933.  
  934.     // Our buffer is too small
  935.     if (*lpcbFormat < p->cbFormat)
  936.         return ResultFromScode(AVIERR_BUFFERTOOSMALL);
  937.  
  938.     *lpcbFormat = p->cbFormat;
  939.  
  940.     return 0;
  941. }
  942.  
  943. //
  944. // The Info Method of the Stream interface
  945. //
  946. STDMETHODIMP WaveStreamInfo(
  947.     PAVISTREAM    ps,
  948.     AVISTREAMINFOW FAR * psi,
  949.     LONG lSize)
  950. {
  951.     // Get a pointer to our structure
  952.     LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);
  953.  
  954.     // give them an AVISTREAMINFO
  955.     hmemcpy(psi, &p->avistream, min(lSize, sizeof(p->avistream)));
  956.     return 0;
  957. }
  958.  
  959.  
  960. STDMETHODIMP WaveStreamSetInfo(PAVISTREAM ps, AVISTREAMINFOW FAR * psi, LONG lSize)
  961. {
  962.     return ResultFromScode(AVIERR_UNSUPPORTED);
  963. }
  964.  
  965. ///////////////////////////////////////////////////////////////////////////
  966. ///////////////////////////////////////////////////////////////////////////
  967.  
  968. /*
  969.         invalid lPos return error
  970.  
  971.         if lPos + lSamples is invalid trim lSamples to fit.
  972.  
  973.         lpBuffer == NULL
  974.  
  975.             cbBuffer == 0 && lSamples > 0
  976.                 return size of lSamples sample.
  977.             else
  978.                 return the exactly the number of bytes and sample
  979.                 you would have read if lpBuffer was not zero.
  980.  
  981.             NOTE return means fill in *plBytes and *plSamples.
  982.  
  983.         lpBuffer != NULL
  984.  
  985.             lSamples == -1      read convenient amount (just fill buffer)
  986.             lSamples == 0       fill buffer with as many samples that will fit.
  987.             lSamples >  0       read lSamples (or as much will fit in cbBuffer)
  988.  
  989.             fill in *plBytes   with bytes actualy read
  990.             fill in *plSamples with samples actualy read
  991.  
  992. */
  993.  
  994. //
  995. // The Read Method for the Stream Interface - Read some wave data
  996. STDMETHODIMP WaveStreamRead(
  997.     PAVISTREAM    ps,
  998.     LONG        lStart,
  999.     LONG        lSamples,
  1000.     LPVOID        lpBuffer,
  1001.     LONG        cbBuffer,
  1002.     LONG FAR *    plBytes,
  1003.     LONG FAR *    plSamples)
  1004. {
  1005.     // Get a pointer to our structure
  1006.     LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);
  1007.  
  1008.     LONG    lSampleSize;
  1009.     LONG    lSeek;
  1010.     LONG    lRead;
  1011.  
  1012.     // Invalid position
  1013.     if (lStart < 0 || lStart > (LONG) p->avistream.dwLength) {
  1014. ack:
  1015.         if (plBytes)
  1016.             *plBytes = 0;
  1017.         if (plSamples)
  1018.             *plSamples = 0;
  1019.         return 0;
  1020.     }
  1021.     
  1022.     // Can't read quite this much data
  1023.     if (lSamples + lStart > (LONG) p->avistream.dwLength)
  1024.         lSamples = p->avistream.dwLength - lStart;
  1025.     
  1026.     lSampleSize = p->avistream.dwSampleSize;
  1027.  
  1028.     // We have fixed-length samples
  1029.  
  1030.     if (lpBuffer == NULL) {
  1031.         if (cbBuffer > 0 && lSamples > 0)
  1032.             // Trim how many samples we'd really be able to read
  1033.             lSamples = min(lSamples, cbBuffer / lSampleSize);
  1034.         else if (lSamples <= 0)
  1035.                 // Use as many as will fit
  1036.             lSamples = cbBuffer / lSampleSize;
  1037.     } else {
  1038.         if (lSamples > 0)
  1039.             // Trim how many samples we'd really be able to read
  1040.             lSamples = min(lSamples, cbBuffer / lSampleSize);
  1041.         else
  1042.             // Use as many as will fit
  1043.             lSamples = cbBuffer / lSampleSize;
  1044.     }
  1045.  
  1046.     //
  1047.     // a NULL buffer means return the size buffer needed to read
  1048.     // the given sample.
  1049.     //
  1050.     if (lpBuffer == NULL || cbBuffer == 0) {
  1051.         if (plBytes)
  1052.             *plBytes = lSamples * lSampleSize;;
  1053.         if (plSamples)
  1054.             *plSamples = lSamples;
  1055.         return 0;
  1056.     }
  1057.  
  1058.     // Buffer too small!
  1059.     if (cbBuffer < lSampleSize)
  1060.         goto ack;
  1061.  
  1062.     // Seek and read
  1063.  
  1064.     cbBuffer = lSamples * lSampleSize;
  1065.  
  1066.     lSeek = p->ckData.dwDataOffset + lSampleSize * lStart;
  1067.     lRead = lSamples * lSampleSize;
  1068.     
  1069.     if (mmioSeek(p->hmmio, lSeek, SEEK_SET) != lSeek)
  1070.         goto ack;
  1071.  
  1072.     if (mmioRead(p->hmmio, (HPSTR) lpBuffer, lRead) != lRead)
  1073.         goto ack;
  1074.     
  1075.     //
  1076.     // success return number of bytes and number of samples read
  1077.     //
  1078.     if (plBytes)
  1079.         *plBytes = lRead;
  1080.  
  1081.     if (plSamples)
  1082.         *plSamples = lSamples;
  1083.  
  1084.     return ResultFromScode(AVIERR_OK);
  1085. }
  1086.  
  1087.  
  1088. //
  1089. // The SetFormat Method of the Stream interface    - called on an empty WAVE file
  1090. // before writing data to it.
  1091. //
  1092. STDMETHODIMP WaveStreamSetFormat(
  1093.     PAVISTREAM ps,
  1094.     LONG lPos,
  1095.     LPVOID lpFormat,
  1096.     LONG cbFormat)
  1097. {
  1098.     // Get a pointer to our structure
  1099.     LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);
  1100.  
  1101.     // We can only do this to an empty wave file
  1102.     if (p->lpFormat) {
  1103.         if (cbFormat != p->cbFormat ||
  1104.             _fmemcmp(lpFormat, p->lpFormat, (int) cbFormat))
  1105.             return ResultFromScode(AVIERR_UNSUPPORTED);
  1106.     
  1107.         return NOERROR;
  1108.     }
  1109.     
  1110.     // Go ahead and set the format!
  1111.  
  1112.     p->cbFormat = cbFormat;
  1113.     p->lpFormat = (LPWAVEFORMAT) GlobalAllocPtr(GMEM_MOVEABLE, cbFormat);
  1114.  
  1115.     if (p->lpFormat == NULL)
  1116.         return ResultFromScode(AVIERR_MEMORY);
  1117.  
  1118.     hmemcpy(p->lpFormat, lpFormat, cbFormat);
  1119.  
  1120.     p->ckData.dwDataOffset = cbFormat + 7 * sizeof(DWORD);
  1121.     p->ckData.cksize = 0;
  1122.     p->avistream.dwScale = p->lpFormat->nBlockAlign;
  1123.     p->avistream.dwRate = p->lpFormat->nAvgBytesPerSec;
  1124.     p->avistream.dwLength = 0;
  1125.     p->avistream.dwSampleSize = p->lpFormat->nBlockAlign;
  1126.  
  1127. #ifndef FPSHACK
  1128.     p->avihdr.dwScale = 1;
  1129.     p->avihdr.dwRate = p->lpFormat->nSamplesPerSec;
  1130. #endif
  1131.     return ResultFromScode(AVIERR_OK);
  1132. }
  1133.  
  1134. //
  1135. // The Write Method of the Stream interface - write some wave data
  1136. //
  1137. STDMETHODIMP WaveStreamWrite(
  1138.     PAVISTREAM ps,
  1139.     LONG lStart,
  1140.     LONG lSamples,
  1141.     LPVOID lpData,
  1142.     LONG cbData,
  1143.     DWORD dwFlags,
  1144.     LONG FAR *plSampWritten,
  1145.     LONG FAR *plBytesWritten)
  1146. {
  1147.     // Get a pointer to our structure
  1148.     LPWAVESTUFF p = WAVESTUFF_FROM_STREAM(ps);
  1149.  
  1150.     if ((p->mode & (OF_WRITE | OF_READWRITE)) == 0)
  1151.         return ResultFromScode(AVIERR_READONLY);
  1152.  
  1153.     // < 0 means "at end"
  1154.     if (lStart < 0)
  1155.         // !!!
  1156.         lStart = p->avistream.dwStart + p->avistream.dwLength;
  1157.  
  1158.     p->fDirty = TRUE;
  1159.  
  1160.     mmioSeek(p->hmmio,
  1161.         p->ckData.dwDataOffset +
  1162.         lStart * p->avistream.dwSampleSize,
  1163.         SEEK_SET);
  1164.  
  1165.     if (mmioWrite(p->hmmio, (HPSTR) lpData, cbData) != cbData)
  1166.         return ResultFromScode(AVIERR_FILEWRITE);
  1167.  
  1168.     p->avistream.dwLength = max((LONG) p->avistream.dwLength,
  1169.                     lStart + lSamples);
  1170.  
  1171.     p->ckData.cksize = max(p->ckData.cksize,
  1172.                 lStart * p->avistream.dwSampleSize + cbData);
  1173.  
  1174. #ifdef FPSHACK
  1175.     p->avihdr.dwLength = MulDiv32(p->avistream.dwLength * FPSHACK,
  1176.                 p->avistream.dwScale,
  1177.                 p->avistream.dwRate);
  1178. #else
  1179.     p->avihdr.dwLength = MulDiv32(p->ckData.cksize,
  1180.                 p->lpFormat->nSamplesPerSec,
  1181.                 p->lpFormat->nAvgBytesPerSec);
  1182. #endif
  1183.  
  1184.     
  1185.     if (plSampWritten)
  1186.         *plSampWritten = lSamples;
  1187.  
  1188.     if (plBytesWritten)
  1189.         *plBytesWritten = cbData;
  1190.     
  1191.     return ResultFromScode(AVIERR_OK);
  1192. }
  1193.  
  1194. //
  1195. // The Delete Method of the Stream interface - we don't cut from wave files
  1196. //
  1197. STDMETHODIMP WaveStreamDelete(
  1198.     PAVISTREAM ps,
  1199.     LONG lStart,
  1200.     LONG lSamples)
  1201. {
  1202.     return ResultFromScode(AVIERR_UNSUPPORTED);
  1203. }
  1204.  
  1205.  
  1206. //
  1207. // We also don't support ReadData and WriteData for the Stream Interface
  1208. //
  1209.  
  1210. STDMETHODIMP WaveStreamReadData(
  1211.     PAVISTREAM ps,
  1212.     DWORD fcc,
  1213.     LPVOID lp,
  1214.     LONG FAR *lpcb)
  1215. {
  1216.     return ResultFromScode(AVIERR_UNSUPPORTED);
  1217. }
  1218.  
  1219. STDMETHODIMP WaveStreamWriteData(
  1220.     PAVISTREAM ps,
  1221.     DWORD fcc,
  1222.     LPVOID lp,
  1223.     LONG cb)
  1224. {
  1225.     return ResultFromScode(AVIERR_UNSUPPORTED);
  1226. }
  1227.  
  1228.  
  1229. STDMETHODIMP WaveFileReserved(
  1230.     PAVIFILE pf)
  1231. {
  1232.     return ResultFromScode(AVIERR_UNSUPPORTED);
  1233. }
  1234.  
  1235. STDMETHODIMP WaveStreamReserved(
  1236.     PAVISTREAM ps)
  1237. {
  1238.     return ResultFromScode(AVIERR_UNSUPPORTED);
  1239. }
  1240.  
  1241. /*      -       -       -       -       -       -       -       -       */
  1242.  
  1243. // *** IPersist methods ***
  1244. STDMETHODIMP WavePersistGetClassID (LPPERSISTFILE ppf, LPCLSID lpClassID)
  1245. {
  1246.     // Get a pointer to our structure
  1247.     LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);
  1248.  
  1249.     hmemcpy(lpClassID, &CLSID_AVIWaveFileReader, sizeof(CLSID));
  1250.     return NOERROR;
  1251. }
  1252.  
  1253. // *** IPersistFile methods ***
  1254. STDMETHODIMP WavePersistIsDirty (LPPERSISTFILE ppf)
  1255. {
  1256.     // Get a pointer to our structure
  1257.     LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);
  1258.  
  1259.     return pfile->fDirty ? NOERROR : ResultFromScode(S_FALSE);
  1260. }
  1261.  
  1262. STDMETHODIMP WavePersistLoad (LPPERSISTFILE ppf,
  1263.                   LPCOLESTR lpszFileName, DWORD grfMode)
  1264. {
  1265.     // Get a pointer to our structure
  1266.     LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);
  1267.  
  1268.  
  1269. #if !defined UNICODE
  1270.     char    achTemp[256];
  1271.  
  1272.     // Internally, we're using ANSI, but this interface is defined
  1273.     // to always accept UNICODE under WIN32, so we have to convert.
  1274.     WideCharToMultiByte(CP_ACP, 0, lpszFileName, -1,
  1275.             achTemp, sizeof(achTemp), NULL, NULL);
  1276. #else
  1277.     #define achTemp    lpszFileName
  1278. #endif
  1279.  
  1280.     return WaveFileOpen((PAVIFILE) &pfile->AVIFile, achTemp, (UINT) grfMode);
  1281. }
  1282.  
  1283. STDMETHODIMP WavePersistSave (LPPERSISTFILE ppf,
  1284.                   LPCOLESTR lpszFileName, BOOL fRemember)
  1285. {
  1286.     // Get a pointer to our structure
  1287.     LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);
  1288.  
  1289.  
  1290.     return ResultFromScode(E_FAIL);
  1291. }
  1292.  
  1293. STDMETHODIMP WavePersistSaveCompleted (LPPERSISTFILE ppf,
  1294.                        LPCOLESTR lpszFileName)
  1295. {
  1296.     // Get a pointer to our structure
  1297.     LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);
  1298.  
  1299.  
  1300.     return NOERROR;
  1301. }
  1302.  
  1303. STDMETHODIMP WavePersistGetCurFile (LPPERSISTFILE ppf,
  1304.                     LPOLESTR FAR * lplpszFileName)
  1305. {
  1306.     // Get a pointer to our structure
  1307.     LPWAVESTUFF pfile = WAVESTUFF_FROM_PERSIST(ppf);
  1308.  
  1309.     return ResultFromScode(E_FAIL);
  1310. }
  1311.