ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

mciwave.c
Go to the documentation of this file.
00001 /*
00002  * Wine Driver for MCI wave forms
00003  *
00004  * Copyright    1994 Martin Ayotte
00005  *      1999,2000,2005 Eric Pouech
00006  *              2000 Francois Jacques
00007  *      2009 Jörg Höhle
00008  *
00009  * This library is free software; you can redistribute it and/or
00010  * modify it under the terms of the GNU Lesser General Public
00011  * License as published by the Free Software Foundation; either
00012  * version 2.1 of the License, or (at your option) any later version.
00013  *
00014  * This library is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017  * Lesser General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU Lesser General Public
00020  * License along with this library; if not, write to the Free Software
00021  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00022  */
00023 
00024 #include <assert.h>
00025 #include <stdarg.h>
00026 
00027 #include "windef.h"
00028 #include "winbase.h"
00029 #include "wingdi.h"
00030 #include "winuser.h"
00031 #include "mmddk.h"
00032 #include "wownt32.h"
00033 #include "digitalv.h"
00034 #include "wine/debug.h"
00035 #include "wine/unicode.h"
00036 
00037 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
00038 
00039 typedef struct {
00040     UINT            wDevID;
00041     HANDLE          hWave;
00042     int             nUseCount;  /* Incremented for each shared open */
00043     HMMIO           hFile;      /* mmio file handle open as Element */
00044     MCIDEVICEID         wNotifyDeviceID;    /* MCI device ID with a pending notification */
00045     HANDLE          hCallback;  /* Callback handle for pending notification */
00046     LPWSTR          lpFileName; /* Name of file (if any)                     */
00047     WAVEFORMATEX        wfxRef;
00048     LPWAVEFORMATEX      lpWaveFormat;   /* Points to wfxRef until set by OPEN or RECORD */
00049     BOOL            fInput;     /* FALSE = Output, TRUE = Input */
00050     WORD            wInput;     /* wave input device */
00051     WORD            wOutput;    /* wave output device */
00052     volatile WORD       dwStatus;   /* one from MCI_MODE_xxxx */
00053     DWORD           dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
00054     DWORD           dwPosition; /* position in bytes in chunk */
00055     HANDLE          hEvent;     /* for synchronization */
00056     LONG            dwEventCount;   /* for synchronization */
00057     MMCKINFO                    ckMainRIFF;     /* main RIFF chunk */
00058     MMCKINFO                    ckWaveData;     /* data chunk */
00059 } WINE_MCIWAVE;
00060 
00061 /* ===================================================================
00062  * ===================================================================
00063  * FIXME: should be using the new mmThreadXXXX functions from WINMM
00064  * instead of those
00065  * it would require to add a wine internal flag to mmThreadCreate
00066  * in order to pass a 32 bit function instead of a 16 bit one
00067  * ===================================================================
00068  * =================================================================== */
00069 
00070 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
00071 
00072 struct SCA {
00073     async_cmd   cmd;
00074     HANDLE      evt;
00075     UINT    wDevID;
00076     DWORD_PTR   dwParam1;
00077     DWORD_PTR   dwParam2;
00078 };
00079 
00080 /**************************************************************************
00081  *              MCI_SCAStarter          [internal]
00082  */
00083 static DWORD CALLBACK   MCI_SCAStarter(LPVOID arg)
00084 {
00085     struct SCA* sca = (struct SCA*)arg;
00086     DWORD       ret;
00087 
00088     TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
00089       sca->wDevID, sca->dwParam1, sca->dwParam2);
00090     ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
00091     TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
00092       sca->wDevID, sca->dwParam1, sca->dwParam2);
00093     HeapFree(GetProcessHeap(), 0, sca);
00094     return ret;
00095 }
00096 
00097 /**************************************************************************
00098  *              MCI_SendCommandAsync        [internal]
00099  */
00100 static  DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
00101                    DWORD_PTR dwParam2, UINT size)
00102 {
00103     HANDLE handles[2];
00104     struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
00105 
00106     if (sca == 0)
00107     return MCIERR_OUT_OF_MEMORY;
00108 
00109     sca->wDevID   = wDevID;
00110     sca->cmd      = cmd;
00111     sca->dwParam1 = dwParam1;
00112 
00113     if (size && dwParam2) {
00114     sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
00115     /* copy structure passed by program in dwParam2 to be sure
00116      * we can still use it whatever the program does
00117      */
00118     memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
00119     } else {
00120     sca->dwParam2 = dwParam2;
00121     }
00122 
00123     if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL ||
00124         (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
00125     WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
00126         if (handles[1]) CloseHandle(handles[1]);
00127         sca->evt = NULL;
00128     return MCI_SCAStarter(&sca);
00129     }
00130 
00131     SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL);
00132     /* wait until either:
00133      * - the thread has finished (handles[0], likely an error)
00134      * - init phase of async command is done (handles[1])
00135      */
00136     WaitForMultipleObjects(2, handles, FALSE, INFINITE);
00137     CloseHandle(handles[0]);
00138     CloseHandle(handles[1]);
00139     return 0;
00140 }
00141 
00142 /*======================================================================*
00143  *                          MCI WAVE implementation         *
00144  *======================================================================*/
00145 
00146 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
00147 
00148 /**************************************************************************
00149  *              MCIWAVE_drvOpen         [internal]
00150  */
00151 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
00152 {
00153     WINE_MCIWAVE*   wmw;
00154 
00155     if (modp == NULL) return 0xFFFFFFFF;
00156 
00157     wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
00158 
00159     if (!wmw)
00160     return 0;
00161 
00162     wmw->wDevID = modp->wDeviceID;
00163     mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
00164     modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
00165     modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
00166 
00167     wmw->wfxRef.wFormatTag      = WAVE_FORMAT_PCM;
00168     wmw->wfxRef.nChannels       = 1;      /* MONO */
00169     wmw->wfxRef.nSamplesPerSec  = 11025;
00170     wmw->wfxRef.nAvgBytesPerSec = 11025;
00171     wmw->wfxRef.nBlockAlign     = 1;
00172     wmw->wfxRef.wBitsPerSample  = 8;
00173     wmw->wfxRef.cbSize          = 0;      /* don't care */
00174 
00175     return modp->wDeviceID;
00176 }
00177 
00178 /**************************************************************************
00179  *              MCIWAVE_drvClose        [internal]
00180  */
00181 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
00182 {
00183     WINE_MCIWAVE*  wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
00184 
00185     if (wmw) {
00186     HeapFree(GetProcessHeap(), 0, wmw);
00187     mciSetDriverData(dwDevID, 0);
00188     return 1;
00189     }
00190     return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
00191 }
00192 
00193 /**************************************************************************
00194  *              WAVE_mciGetOpenDev      [internal]
00195  */
00196 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
00197 {
00198     WINE_MCIWAVE*   wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
00199 
00200     if (wmw == NULL || wmw->nUseCount == 0) {
00201     WARN("Invalid wDevID=%u\n", wDevID);
00202     return 0;
00203     }
00204     return wmw;
00205 }
00206 
00207 /**************************************************************************
00208  *              WAVE_mciNotify          [internal]
00209  *
00210  * Notifications in MCI work like a 1-element queue.
00211  * Each new notification request supersedes the previous one.
00212  * This affects Play and Record; other commands are immediate.
00213  */
00214 static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus)
00215 {
00216     /* We simply save one parameter by not passing the wDevID local
00217      * to the command.  They are the same (via mciGetDriverData).
00218      */
00219     MCIDEVICEID wDevID = wmw->wNotifyDeviceID;
00220     HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
00221     if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
00222     mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
00223 }
00224 
00225 /**************************************************************************
00226  *              WAVE_ConvertByteToTimeFormat    [internal]
00227  */
00228 static  DWORD   WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val)
00229 {
00230     DWORD      ret = 0;
00231 
00232     switch (wmw->dwMciTimeFormat) {
00233     case MCI_FORMAT_MILLISECONDS:
00234     ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
00235     break;
00236     case MCI_FORMAT_BYTES:
00237     ret = val;
00238     break;
00239     case MCI_FORMAT_SAMPLES:
00240     ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
00241     break;
00242     default:
00243     WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
00244     }
00245     TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
00246     return ret;
00247 }
00248 
00249 /**************************************************************************
00250  *              WAVE_ConvertTimeFormatToByte    [internal]
00251  */
00252 static  DWORD   WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
00253 {
00254     DWORD   ret = 0;
00255 
00256     switch (wmw->dwMciTimeFormat) {
00257     case MCI_FORMAT_MILLISECONDS:
00258     ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
00259     if (ret > wmw->ckWaveData.cksize &&
00260         val == WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize))
00261         ret = wmw->ckWaveData.cksize;
00262     break;
00263     case MCI_FORMAT_BYTES:
00264     ret = val;
00265     break;
00266     case MCI_FORMAT_SAMPLES:
00267     ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
00268     break;
00269     default:
00270     WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
00271     }
00272     TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
00273     return ret;
00274 }
00275 
00276 /**************************************************************************
00277  *          WAVE_mciReadFmt                         [internal]
00278  */
00279 static  DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
00280 {
00281     MMCKINFO    mmckInfo;
00282     LONG    r;
00283     LPWAVEFORMATEX pwfx;
00284 
00285     mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
00286     if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
00287     return MCIERR_INVALID_FILE;
00288     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
00289       (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
00290 
00291     pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
00292     if (!pwfx) return MCIERR_OUT_OF_MEMORY;
00293 
00294     r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize);
00295     if (r < sizeof(PCMWAVEFORMAT)) {
00296     HeapFree(GetProcessHeap(), 0, pwfx);
00297     return MCIERR_INVALID_FILE;
00298     }
00299     TRACE("wFormatTag=%04X !\n",   pwfx->wFormatTag);
00300     TRACE("nChannels=%d\n",        pwfx->nChannels);
00301     TRACE("nSamplesPerSec=%d\n",   pwfx->nSamplesPerSec);
00302     TRACE("nAvgBytesPerSec=%d\n",  pwfx->nAvgBytesPerSec);
00303     TRACE("nBlockAlign=%d\n",      pwfx->nBlockAlign);
00304     TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample);
00305     if (r >= sizeof(WAVEFORMATEX))
00306     TRACE("cbSize=%u !\n",     pwfx->cbSize);
00307     if ((pwfx->wFormatTag != WAVE_FORMAT_PCM)
00308     && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) {
00309     HeapFree(GetProcessHeap(), 0, pwfx);
00310     return MCIERR_INVALID_FILE;
00311     }
00312     wmw->lpWaveFormat = pwfx;
00313 
00314     mmioAscend(wmw->hFile, &mmckInfo, 0);
00315     wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
00316     if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
00317     TRACE("can't find data chunk\n");
00318     return MCIERR_INVALID_FILE;
00319     }
00320     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
00321       (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
00322     return 0;
00323 }
00324 
00325 /**************************************************************************
00326  *          WAVE_mciDefaultFmt          [internal]
00327  *
00328  * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
00329  * until either Open File or Record.  It becomes immutable afterwards,
00330  * i.e. Set wave format or channels etc. is subsequently refused.
00331  */
00332 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
00333 {
00334     wmw->lpWaveFormat = &wmw->wfxRef;
00335     wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
00336     wmw->lpWaveFormat->nChannels = 1;
00337     wmw->lpWaveFormat->nSamplesPerSec = 11025;
00338     wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
00339     wmw->lpWaveFormat->nBlockAlign = 1;
00340     wmw->lpWaveFormat->wBitsPerSample = 8;
00341     wmw->lpWaveFormat->cbSize = 0;
00342 }
00343 
00344 /**************************************************************************
00345  *          WAVE_mciCreateRIFFSkeleton              [internal]
00346  */
00347 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
00348 {
00349    MMCKINFO     ckWaveFormat;
00350    LPMMCKINFO   lpckRIFF     = &(wmw->ckMainRIFF);
00351    LPMMCKINFO   lpckWaveData = &(wmw->ckWaveData);
00352 
00353    lpckRIFF->ckid    = FOURCC_RIFF;
00354    lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
00355    lpckRIFF->cksize  = 0;
00356 
00357    if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
00358     goto err;
00359 
00360    ckWaveFormat.fccType = 0;
00361    ckWaveFormat.ckid    = mmioFOURCC('f', 'm', 't', ' ');
00362    ckWaveFormat.cksize  = sizeof(PCMWAVEFORMAT);
00363 
00364    /* Set wave format accepts PCM only, however open an
00365     * existing ADPCM file, record into it and the MCI will
00366     * happily save back in that format. */
00367    if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
00368     if (wmw->lpWaveFormat->nBlockAlign !=
00369         wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
00370         WORD size = wmw->lpWaveFormat->nChannels *
00371         wmw->lpWaveFormat->wBitsPerSample/8;
00372         WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
00373         wmw->lpWaveFormat->nBlockAlign, size);
00374         wmw->lpWaveFormat->nBlockAlign = size;
00375     }
00376     if (wmw->lpWaveFormat->nAvgBytesPerSec !=
00377         wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
00378         DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
00379         wmw->lpWaveFormat->nBlockAlign;
00380         WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
00381         wmw->lpWaveFormat->nAvgBytesPerSec, speed);
00382         wmw->lpWaveFormat->nAvgBytesPerSec = speed;
00383     }
00384    }
00385    if (wmw->lpWaveFormat == &wmw->wfxRef) {
00386     LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
00387     if (!pwfx) return MCIERR_OUT_OF_MEMORY;
00388     /* Set wave format accepts PCM only so the size is known. */
00389     assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM);
00390     *pwfx = wmw->wfxRef;
00391     wmw->lpWaveFormat = pwfx;
00392    }
00393 
00394    if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
00395     goto err;
00396 
00397    if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag)
00398     ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize))
00399     goto err;
00400 
00401    if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
00402     goto err;
00403 
00404    lpckWaveData->cksize  = 0;
00405    lpckWaveData->fccType = 0;
00406    lpckWaveData->ckid    = mmioFOURCC('d', 'a', 't', 'a');
00407 
00408    /* create data chunk */
00409    if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
00410     goto err;
00411 
00412    return 0;
00413 
00414 err:
00415    /* mciClose takes care of wmw->lpWaveFormat. */
00416    return MCIERR_INVALID_FILE;
00417 }
00418 
00419 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
00420 {
00421     WCHAR       szTmpPath[MAX_PATH];
00422     WCHAR       szPrefix[4];
00423     DWORD       dwRet = MMSYSERR_NOERROR;
00424 
00425     szPrefix[0] = 'M';
00426     szPrefix[1] = 'C';
00427     szPrefix[2] = 'I';
00428     szPrefix[3] = '\0';
00429 
00430     if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
00431         WARN("can't retrieve temp path!\n");
00432         *pszTmpFileName = NULL;
00433         return MCIERR_FILE_NOT_FOUND;
00434     }
00435 
00436     *pszTmpFileName = HeapAlloc(GetProcessHeap(),
00437                                 HEAP_ZERO_MEMORY,
00438                                 MAX_PATH * sizeof(WCHAR));
00439     if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
00440         WARN("can't retrieve temp file name!\n");
00441         HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
00442         return MCIERR_FILE_NOT_FOUND;
00443     }
00444 
00445     TRACE("%s!\n", debugstr_w(*pszTmpFileName));
00446 
00447     if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
00448 
00449         *hFile = mmioOpenW(*pszTmpFileName, NULL,
00450                            MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
00451 
00452         if (*hFile == 0) {
00453             WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
00454             /* temporary file could not be created. clean filename. */
00455             HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
00456             dwRet = MCIERR_FILE_NOT_FOUND;
00457         }
00458     }
00459     return dwRet;
00460 }
00461 
00462 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename)
00463 {
00464     LRESULT dwRet = MMSYSERR_NOERROR;
00465     LPWSTR fn;
00466 
00467     fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
00468     if (!fn) return MCIERR_OUT_OF_MEMORY;
00469     strcpyW(fn, filename);
00470     HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
00471     wmw->lpFileName = fn;
00472 
00473     if (strlenW(filename) > 0) {
00474         /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
00475         TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
00476 
00477         wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
00478                                MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
00479 
00480         if (wmw->hFile == 0) {
00481             WARN("can't find file=%s!\n", debugstr_w(filename));
00482             dwRet = MCIERR_FILE_NOT_FOUND;
00483         }
00484         else
00485         {
00486             LPMMCKINFO          lpckMainRIFF = &wmw->ckMainRIFF;
00487 
00488             /* make sure we're at the beginning of the file */
00489             mmioSeek(wmw->hFile, 0, SEEK_SET);
00490 
00491             /* first reading of this file. read the waveformat chunk */
00492             if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
00493                 dwRet = MCIERR_INVALID_FILE;
00494             } else {
00495                 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
00496                       (LPSTR)&(lpckMainRIFF->ckid),
00497                       (LPSTR) &(lpckMainRIFF->fccType),
00498                       (lpckMainRIFF->cksize));
00499 
00500                 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
00501                     lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
00502                     dwRet = MCIERR_INVALID_FILE;
00503                 } else {
00504                     dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
00505                 }
00506             }
00507         }
00508     }
00509     return dwRet;
00510 }
00511 
00512 /**************************************************************************
00513  *          WAVE_mciOpen                            [internal]
00514  */
00515 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
00516 {
00517     DWORD       dwRet = 0;
00518     WINE_MCIWAVE*   wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
00519 
00520     TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
00521     if (lpOpenParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
00522     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
00523 
00524     if (dwFlags & MCI_OPEN_SHAREABLE)
00525     return MCIERR_UNSUPPORTED_FUNCTION;
00526 
00527     if (wmw->nUseCount > 0) {
00528     /* The driver is already opened on this channel
00529      * Wave driver cannot be shared
00530      */
00531     return MCIERR_DEVICE_OPEN;
00532     }
00533 
00534     wmw->nUseCount++;
00535 
00536     wmw->wInput = wmw->wOutput = WAVE_MAPPER;
00537     wmw->fInput = FALSE;
00538     wmw->hWave = 0;
00539     wmw->dwStatus = MCI_MODE_NOT_READY;
00540     wmw->hFile = 0;
00541     wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */
00542     wmw->hCallback = NULL;
00543     WAVE_mciDefaultFmt(wmw);
00544 
00545     TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
00546     /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
00547     wmw->wNotifyDeviceID = wDevID;
00548 
00549     if (dwFlags & MCI_OPEN_ELEMENT) {
00550     if (dwFlags & MCI_OPEN_ELEMENT_ID) {
00551         /* could it be that (DWORD)lpOpenParms->lpstrElementName
00552          * contains the hFile value ?
00553          */
00554         dwRet = MCIERR_UNRECOGNIZED_COMMAND;
00555     } else {
00556             dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
00557     }
00558     }
00559     TRACE("hFile=%p\n", wmw->hFile);
00560 
00561     if (dwRet == 0) {
00562     wmw->dwPosition = 0;
00563 
00564     wmw->dwStatus = MCI_MODE_STOP;
00565 
00566     if (dwFlags & MCI_NOTIFY)
00567         WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
00568     } else {
00569     wmw->nUseCount--;
00570     if (wmw->hFile != 0)
00571         mmioClose(wmw->hFile, 0);
00572     wmw->hFile = 0;
00573     HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
00574     wmw->lpFileName = NULL;
00575     }
00576     return dwRet;
00577 }
00578 
00579 /**************************************************************************
00580  *                               WAVE_mciCue             [internal]
00581  */
00582 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
00583 {
00584     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
00585 
00586     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
00587 
00588     /* Tests on systems without sound drivers show that Cue, like
00589      * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
00590      * The first Cue Notify does not immediately return the
00591      * notification, as if a player or recorder thread is started.
00592      * PAUSE mode is reported when successful, but this mode is
00593      * different from the normal Pause, because a) Pause then returns
00594      * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
00595      * still accepted, returning the original notification as ABORTED.
00596      * I.e. Cue allows subsequent format changes, unlike Record or
00597      * Open file, closes winmm if the format changes and stops this
00598      * thread.
00599      * Wine creates one player or recorder thread per async. Play or
00600      * Record command.  Notification behaviour suggests that MS-W*
00601      * reuses a single thread to improve response times.  Having Cue
00602      * start this thread early helps to improve Play/Record's initial
00603      * response time.  In effect, Cue is a performance hint, which
00604      * justifies our almost no-op implementation.
00605      */
00606 
00607     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
00608     if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
00609 
00610     if ((dwFlags & MCI_NOTIFY) && lpParms)
00611     WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
00612 
00613     return MMSYSERR_NOERROR;
00614 }
00615 
00616 /**************************************************************************
00617  *              WAVE_mciStop            [internal]
00618  */
00619 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
00620 {
00621     DWORD       dwRet = 0;
00622     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
00623 
00624     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
00625 
00626     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
00627 
00628     if (wmw->dwStatus != MCI_MODE_STOP) {
00629     HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
00630     if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
00631     }
00632 
00633     /* wait for playback thread (if any) to exit before processing further */
00634     switch (wmw->dwStatus) {
00635     case MCI_MODE_PAUSE:
00636     case MCI_MODE_PLAY:
00637     case MCI_MODE_RECORD:
00638     {
00639         int oldStat = wmw->dwStatus;
00640         wmw->dwStatus = MCI_MODE_NOT_READY;
00641         if (oldStat == MCI_MODE_PAUSE)
00642         dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
00643     }
00644     while (wmw->dwStatus != MCI_MODE_STOP)
00645         Sleep(10);
00646     break;
00647     }
00648 
00649     /* sanity resets */
00650     wmw->dwStatus = MCI_MODE_STOP;
00651 
00652     if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
00653     WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
00654 
00655     return dwRet;
00656 }
00657 
00658 /**************************************************************************
00659  *              WAVE_mciClose       [internal]
00660  */
00661 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
00662 {
00663     DWORD       dwRet = 0;
00664     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
00665 
00666     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
00667 
00668     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
00669 
00670     if (wmw->dwStatus != MCI_MODE_STOP) {
00671         /* mciStop handles MCI_NOTIFY_ABORTED */
00672     dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
00673     }
00674 
00675     wmw->nUseCount--;
00676 
00677     if (wmw->nUseCount == 0) {
00678     if (wmw->hFile != 0) {
00679         mmioClose(wmw->hFile, 0);
00680         wmw->hFile = 0;
00681     }
00682     }
00683 
00684     if (wmw->lpWaveFormat != &wmw->wfxRef)
00685     HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
00686     wmw->lpWaveFormat = &wmw->wfxRef;
00687     HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
00688     wmw->lpFileName = NULL;
00689 
00690     if ((dwFlags & MCI_NOTIFY) && lpParms) {
00691     WAVE_mciNotify(lpParms->dwCallback, wmw,
00692         (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
00693     }
00694 
00695     return 0;
00696 }
00697 
00698 /**************************************************************************
00699  *              WAVE_mciPlayCallback        [internal]
00700  */
00701 static  void    CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
00702                           DWORD_PTR dwInstance,
00703                           LPARAM dwParam1, LPARAM dwParam2)
00704 {
00705     WINE_MCIWAVE*   wmw = (WINE_MCIWAVE*)dwInstance;
00706 
00707     switch (uMsg) {
00708     case WOM_OPEN:
00709     case WOM_CLOSE:
00710     break;
00711     case WOM_DONE:
00712     InterlockedIncrement(&wmw->dwEventCount);
00713     TRACE("Returning waveHdr=%lx\n", dwParam1);
00714     SetEvent(wmw->hEvent);
00715     break;
00716     default:
00717     ERR("Unknown uMsg=%d\n", uMsg);
00718     }
00719 }
00720 
00721 /******************************************************************
00722  *          WAVE_mciPlayWaitDone        [internal]
00723  */
00724 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
00725 {
00726     for (;;) {
00727     ResetEvent(wmw->hEvent);
00728     if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
00729         break;
00730     }
00731     InterlockedIncrement(&wmw->dwEventCount);
00732 
00733     WaitForSingleObject(wmw->hEvent, INFINITE);
00734     }
00735 }
00736 
00737 /**************************************************************************
00738  *              WAVE_mciPlay        [internal]
00739  */
00740 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
00741 {
00742     LPMCI_PLAY_PARMS    lpParms = (void*)pmt;
00743     DWORD       end;
00744     LONG        bufsize, count, left;
00745     DWORD       dwRet;
00746     LPWAVEHDR       waveHdr = NULL;
00747     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
00748     HANDLE      oldcb;
00749     int         whidx;
00750 
00751     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
00752 
00753     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
00754     if (lpParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
00755 
00756     if (wmw->hFile == 0) {
00757     WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
00758     return MCIERR_FILE_NOT_FOUND;
00759     }
00760 
00761     if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput && !(dwFlags & (MCI_FROM | MCI_TO))) {
00762     /* FIXME: notification is different with Resume than Play */
00763     return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
00764     }
00765 
00770     if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
00771     !((wmw->dwStatus == MCI_MODE_PLAY) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
00772     /* FIXME: Check FROM/TO parameters first. */
00773     /* FIXME: Play; Play [notify|wait] must hook into the running player. */
00774     dwRet = WAVE_mciStop(wDevID, MCI_WAIT, NULL);
00775     if (dwRet) return dwRet;
00776     }
00777 
00778     if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
00779         if (wmw->lpWaveFormat->nBlockAlign !=
00780             wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
00781             WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
00782                 wmw->lpWaveFormat->nBlockAlign,
00783                 wmw->lpWaveFormat->nChannels *
00784                  wmw->lpWaveFormat->wBitsPerSample/8);
00785             wmw->lpWaveFormat->nBlockAlign =
00786                 wmw->lpWaveFormat->nChannels *
00787                 wmw->lpWaveFormat->wBitsPerSample/8;
00788         }
00789         if (wmw->lpWaveFormat->nAvgBytesPerSec !=
00790             wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
00791             WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
00792                 wmw->lpWaveFormat->nAvgBytesPerSec,
00793                 wmw->lpWaveFormat->nSamplesPerSec *
00794                  wmw->lpWaveFormat->nBlockAlign);
00795             wmw->lpWaveFormat->nAvgBytesPerSec =
00796                 wmw->lpWaveFormat->nSamplesPerSec *
00797                 wmw->lpWaveFormat->nBlockAlign;
00798         }
00799     }
00800 
00801     end = wmw->ckWaveData.cksize;
00802     if (dwFlags & MCI_TO) {
00803     DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
00804     if (position > end)     return MCIERR_OUTOFRANGE;
00805     end = position;
00806     }
00807     if (dwFlags & MCI_FROM) {
00808     DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
00809     if (position > end)     return MCIERR_OUTOFRANGE;
00810     /* Seek rounds down, so do we. */
00811     position /= wmw->lpWaveFormat->nBlockAlign;
00812     position *= wmw->lpWaveFormat->nBlockAlign;
00813     wmw->dwPosition = position;
00814     }
00815     if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
00816     left = end - wmw->dwPosition;
00817     if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
00818 
00819     wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
00820     wmw->dwStatus = MCI_MODE_PLAY;
00821 
00822     if (!(dwFlags & MCI_WAIT)) {
00823     return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags,
00824                     (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
00825     }
00826 
00827     TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
00828 
00829     oldcb = InterlockedExchangePointer(&wmw->hCallback,
00830     (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
00831     if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
00832     oldcb = NULL;
00833 
00834 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
00835 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
00836 
00837     /* go back to beginning of chunk plus the requested position */
00838     /* FIXME: I'm not sure this is correct, notably because some data linked to
00839      * the decompression state machine will not be correctly initialized.
00840      * try it this way (other way would be to decompress from 0 up to dwPosition
00841      * and to start sending to hWave when dwPosition is reached)
00842      */
00843     mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
00844 
00845     dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, wmw->wOutput, wmw->lpWaveFormat,
00846             (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
00847 
00848     if (dwRet != 0) {
00849     TRACE("Can't open low level audio device %d\n", dwRet);
00850     dwRet = MCIERR_DEVICE_OPEN;
00851     wmw->hWave = 0;
00852     goto cleanUp;
00853     }
00854 
00855     /* make it so that 3 buffers per second are needed */
00856     bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
00857 
00858     waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
00859     waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
00860     waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
00861     waveHdr[0].dwUser         = waveHdr[1].dwUser         = 0L;
00862     waveHdr[0].dwLoops        = waveHdr[1].dwLoops        = 0L;
00863     waveHdr[0].dwFlags        = waveHdr[1].dwFlags        = 0L;
00864     waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
00865     if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
00866     waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
00867     dwRet = MCIERR_INTERNAL;
00868     goto cleanUp;
00869     }
00870 
00871     whidx = 0;
00872     wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
00873     if (!wmw->hEvent) {
00874     dwRet = MCIERR_OUT_OF_MEMORY;
00875     goto cleanUp;
00876     }
00877     wmw->dwEventCount = 1L; /* for first buffer */
00878 
00879     TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
00880     if (hEvent) SetEvent(hEvent);
00881 
00882     /* FIXME: this doesn't work if wmw->dwPosition != 0 */
00883     while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
00884     count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
00885     TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
00886     if (count < 1)
00887         break;
00888     /* count is always <= bufsize, so this is correct regarding the
00889      * waveOutPrepareHeader function
00890      */
00891     waveHdr[whidx].dwBufferLength = count;
00892     waveHdr[whidx].dwFlags &= ~WHDR_DONE;
00893     TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
00894           &waveHdr[whidx], waveHdr[whidx].dwBufferLength);
00895     dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
00896     if (dwRet) {
00897         ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet);
00898         dwRet = MCIERR_HARDWARE;
00899         break;
00900     }
00901     left -= count;
00902     wmw->dwPosition += count;
00903     TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
00904     /* InterlockedDecrement if and only if waveOutWrite is successful */
00905     WAVE_mciPlayWaitDone(wmw);
00906     whidx ^= 1;
00907     }
00908 
00909     WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
00910 
00911     /* just to get rid of some race conditions between play, stop and pause */
00912     waveOutReset(wmw->hWave);
00913 
00914     waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
00915     waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
00916 
00917 cleanUp:
00918     if (dwFlags & MCI_NOTIFY)
00919     oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
00920 
00921     HeapFree(GetProcessHeap(), 0, waveHdr);
00922 
00923     if (wmw->hWave) {
00924     waveOutClose(wmw->hWave);
00925     wmw->hWave = 0;
00926     }
00927     CloseHandle(wmw->hEvent);
00928     wmw->hEvent = NULL;
00929 
00930     wmw->dwStatus = MCI_MODE_STOP;
00931 
00932     /* Let the potentially asynchronous commands support FAILURE notification. */
00933     if (oldcb) mciDriverNotify(oldcb, wDevID,
00934     dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
00935 
00936     return dwRet;
00937 }
00938 
00939 /**************************************************************************
00940  *              WAVE_mciRecordCallback      [internal]
00941  */
00942 static  void    CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
00943                                                 DWORD_PTR dwInstance,
00944                                                 LPARAM dwParam1, LPARAM dwParam2)
00945 {
00946     WINE_MCIWAVE*   wmw = (WINE_MCIWAVE*)dwInstance;
00947     LPWAVEHDR           lpWaveHdr;
00948     LONG                count = 0;
00949 
00950     switch (uMsg) {
00951     case WIM_OPEN:
00952     case WIM_CLOSE:
00953     break;
00954     case WIM_DATA:
00955     lpWaveHdr = (LPWAVEHDR) dwParam1;
00956 
00957     InterlockedIncrement(&wmw->dwEventCount);
00958 
00959     count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
00960 
00961     lpWaveHdr->dwFlags &= ~WHDR_DONE;
00962         if (count > 0)
00963             wmw->dwPosition  += count;
00964         /* else error reporting ?? */
00965         if (wmw->dwStatus == MCI_MODE_RECORD)
00966         {
00967            /* Only queue up another buffer if we are recording.  We could receive this
00968               message also when waveInReset() is called, since it notifies on all wave
00969               buffers that are outstanding.  Queueing up more sometimes causes waveInClose
00970               to fail. */
00971            waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
00972            TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
00973         }
00974 
00975     SetEvent(wmw->hEvent);
00976     break;
00977     default:
00978     ERR("Unknown uMsg=%d\n", uMsg);
00979     }
00980 }
00981 
00982 /******************************************************************
00983  *          WAVE_mciRecordWaitDone      [internal]
00984  */
00985 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
00986 {
00987     for (;;) {
00988     ResetEvent(wmw->hEvent);
00989     if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
00990         break;
00991     }
00992     InterlockedIncrement(&wmw->dwEventCount);
00993 
00994     WaitForSingleObject(wmw->hEvent, INFINITE);
00995     }
00996 }
00997 
00998 /**************************************************************************
00999  *              WAVE_mciRecord          [internal]
01000  */
01001 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
01002 {
01003     LPMCI_RECORD_PARMS  lpParms = (void*)pmt;
01004     DWORD       end;
01005     DWORD       dwRet = MMSYSERR_NOERROR;
01006     LONG        bufsize;
01007     LPWAVEHDR       waveHdr = NULL;
01008     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
01009     HANDLE      oldcb;
01010 
01011     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
01012 
01013     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
01014     if (lpParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
01015 
01016     if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
01017         /* FIXME: parameters (start/end) in lpParams may not be used */
01018         return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
01019     }
01020 
01025     if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
01026     !((wmw->dwStatus == MCI_MODE_RECORD) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
01027     return MCIERR_INTERNAL;
01028     }
01029 
01030     wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
01031     wmw->dwStatus = MCI_MODE_RECORD;
01032 
01033     if (!(dwFlags & MCI_WAIT)) {
01034     return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags,
01035                     (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
01036     }
01037 
01038     /* FIXME: we only re-create the RIFF structure from an existing file (if any)
01039      * we don't modify the wave part of an existing file (ie. we always erase an
01040      * existing content, we don't overwrite)
01041      */
01042     HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
01043     dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
01044     if (dwRet != 0) return dwRet;
01045 
01046     /* new RIFF file, lpWaveFormat now valid */
01047     dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
01048     if (dwRet != 0) return dwRet;
01049 
01050     if (dwFlags & MCI_TO) {
01051     end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
01052     } else end = 0xFFFFFFFF;
01053     if (dwFlags & MCI_FROM) {
01054     DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
01055     if (wmw->ckWaveData.cksize < position)  return MCIERR_OUTOFRANGE;
01056     /* Seek rounds down, so do we. */
01057     position /= wmw->lpWaveFormat->nBlockAlign;
01058     position *= wmw->lpWaveFormat->nBlockAlign;
01059     wmw->dwPosition = position;
01060     }
01061     if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
01062 
01063     TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
01064 
01065     oldcb = InterlockedExchangePointer(&wmw->hCallback,
01066     (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
01067     if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
01068     oldcb = NULL;
01069 
01070 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
01071 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
01072 
01073     wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
01074 
01075     /* Go back to the beginning of the chunk plus the requested position */
01076     /* FIXME: I'm not sure this is correct, notably because some data linked to
01077      * the decompression state machine will not be correctly initialized.
01078      * Try it this way (other way would be to decompress from 0 up to dwPosition
01079      * and to start sending to hWave when dwPosition is reached).
01080      */
01081     mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
01082 
01083     dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, wmw->wInput, wmw->lpWaveFormat,
01084             (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
01085 
01086     if (dwRet != MMSYSERR_NOERROR) {
01087     TRACE("Can't open low level audio device %d\n", dwRet);
01088     dwRet = MCIERR_DEVICE_OPEN;
01089     wmw->hWave = 0;
01090     goto cleanUp;
01091     }
01092 
01093     /* make it so that 3 buffers per second are needed */
01094     bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
01095 
01096     waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
01097     waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
01098     waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
01099     waveHdr[0].dwUser         = waveHdr[1].dwUser         = 0L;
01100     waveHdr[0].dwLoops        = waveHdr[1].dwLoops        = 0L;
01101     waveHdr[0].dwFlags        = waveHdr[1].dwFlags        = 0L;
01102     waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
01103 
01104     if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
01105     waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
01106     dwRet = MCIERR_INTERNAL;
01107     goto cleanUp;
01108     }
01109 
01110     if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
01111     waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
01112     dwRet = MCIERR_INTERNAL;
01113     goto cleanUp;
01114     }
01115 
01116     wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
01117     wmw->dwEventCount = 1L; /* for first buffer */
01118 
01119     TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
01120 
01121     dwRet = waveInStart(wmw->hWave);
01122 
01123     if (hEvent) SetEvent(hEvent);
01124 
01125     while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
01126     WAVE_mciRecordWaitDone(wmw);
01127     }
01128     /* Grab callback before another thread kicks in after we change dwStatus. */
01129     if (dwFlags & MCI_NOTIFY) {
01130     oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
01131     dwFlags &= ~MCI_NOTIFY;
01132     }
01133     /* needed so that the callback above won't add again the buffers returned by the reset */
01134     wmw->dwStatus = MCI_MODE_STOP;
01135 
01136     waveInReset(wmw->hWave);
01137 
01138     waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
01139     waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
01140 
01141     dwRet = 0;
01142 
01143 cleanUp:
01144     if (dwFlags & MCI_NOTIFY)
01145     oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
01146 
01147     HeapFree(GetProcessHeap(), 0, waveHdr);
01148 
01149     if (wmw->hWave) {
01150     waveInClose(wmw->hWave);
01151     wmw->hWave = 0;
01152     }
01153     CloseHandle(wmw->hEvent);
01154 
01155     wmw->dwStatus = MCI_MODE_STOP;
01156 
01157     if (oldcb) mciDriverNotify(oldcb, wDevID,
01158     dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
01159 
01160     return dwRet;
01161 
01162 }
01163 
01164 /**************************************************************************
01165  *              WAVE_mciPause           [internal]
01166  */
01167 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
01168 {
01169     DWORD       dwRet;
01170     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
01171 
01172     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
01173 
01174     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
01175 
01176     switch (wmw->dwStatus) {
01177     case MCI_MODE_PLAY:
01178     dwRet = waveOutPause(wmw->hWave);
01179     if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
01180     else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
01181         ERR("waveOutPause error %d\n",dwRet);
01182         dwRet = MCIERR_INTERNAL;
01183     }
01184     break;
01185     case MCI_MODE_RECORD:
01186     dwRet = waveInStop(wmw->hWave);
01187     if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
01188     else {
01189         ERR("waveInStop error %d\n",dwRet);
01190         dwRet = MCIERR_INTERNAL;
01191     }
01192     break;
01193     case MCI_MODE_PAUSE:
01194     dwRet = MMSYSERR_NOERROR;
01195     break;
01196     default:
01197     dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
01198     }
01199     if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
01200     WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
01201     return dwRet;
01202 }
01203 
01204 /**************************************************************************
01205  *              WAVE_mciResume          [internal]
01206  */
01207 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
01208 {
01209     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
01210     DWORD       dwRet;
01211 
01212     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
01213 
01214     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
01215 
01216     switch (wmw->dwStatus) {
01217     case MCI_MODE_PAUSE:
01218     /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
01219     if (wmw->fInput) {
01220         dwRet = waveInStart(wmw->hWave);
01221         if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
01222         else {
01223         ERR("waveInStart error %d\n",dwRet);
01224         dwRet = MCIERR_INTERNAL;
01225         }
01226     } else {
01227         dwRet = waveOutRestart(wmw->hWave);
01228         if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
01229         else {
01230         ERR("waveOutRestart error %d\n",dwRet);
01231         dwRet = MCIERR_INTERNAL;
01232         }
01233     }
01234     break;
01235     case MCI_MODE_PLAY:
01236     case MCI_MODE_RECORD:
01237     dwRet = MMSYSERR_NOERROR;
01238     break;
01239     default:
01240     dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
01241     }
01242     if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
01243     WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
01244     return dwRet;
01245 }
01246 
01247 /**************************************************************************
01248  *              WAVE_mciSeek            [internal]
01249  */
01250 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
01251 {
01252     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
01253     DWORD       position, dwRet;
01254 
01255     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
01256 
01257     if (lpParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
01258     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
01259 
01260     position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
01261     if (!position)      return MCIERR_MISSING_PARAMETER;
01262     if (position&(position-1))  return MCIERR_FLAGS_NOT_COMPATIBLE;
01263 
01264     /* Stop sends MCI_NOTIFY_ABORTED when needed */
01265     dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
01266     if (dwRet != MMSYSERR_NOERROR) return dwRet;
01267 
01268     if (dwFlags & MCI_TO) {
01269     position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
01270     if (position > wmw->ckWaveData.cksize)
01271         return MCIERR_OUTOFRANGE;
01272     } else if (dwFlags & MCI_SEEK_TO_START) {
01273     position = 0;
01274     } else {
01275     position = wmw->ckWaveData.cksize;
01276     }
01277     /* Seek rounds down, unless at end */
01278     if (position != wmw->ckWaveData.cksize) {
01279     position /= wmw->lpWaveFormat->nBlockAlign;
01280     position *= wmw->lpWaveFormat->nBlockAlign;
01281     }
01282     wmw->dwPosition = position;
01283     TRACE("Seeking to position=%u bytes\n", position);
01284 
01285     if (dwFlags & MCI_NOTIFY)
01286     WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
01287 
01288     return MMSYSERR_NOERROR;
01289 }
01290 
01291 /**************************************************************************
01292  *              WAVE_mciSet         [internal]
01293  */
01294 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_SET_PARMS lpParms)
01295 {
01296     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
01297 
01298     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
01299 
01300     if (lpParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
01301     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
01302 
01303     if (dwFlags & MCI_SET_TIME_FORMAT) {
01304     switch (lpParms->dwTimeFormat) {
01305     case MCI_FORMAT_MILLISECONDS:
01306         TRACE("MCI_FORMAT_MILLISECONDS !\n");
01307         wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
01308         break;
01309     case MCI_FORMAT_BYTES:
01310         TRACE("MCI_FORMAT_BYTES !\n");
01311         wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
01312         break;
01313     case MCI_FORMAT_SAMPLES:
01314         TRACE("MCI_FORMAT_SAMPLES !\n");
01315         wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
01316         break;
01317     default:
01318             WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
01319         return MCIERR_BAD_TIME_FORMAT;
01320     }
01321     }
01322     if (dwFlags & MCI_SET_VIDEO) {
01323     TRACE("No support for video !\n");
01324     return MCIERR_UNSUPPORTED_FUNCTION;
01325     }
01326     if (dwFlags & MCI_SET_DOOR_OPEN) {
01327     TRACE("No support for door open !\n");
01328     return MCIERR_UNSUPPORTED_FUNCTION;
01329     }
01330     if (dwFlags & MCI_SET_DOOR_CLOSED) {
01331     TRACE("No support for door close !\n");
01332     return MCIERR_UNSUPPORTED_FUNCTION;
01333     }
01334     if (dwFlags & MCI_SET_AUDIO) {
01335     if (dwFlags & MCI_SET_ON) {
01336         TRACE("MCI_SET_ON audio !\n");
01337     } else if (dwFlags & MCI_SET_OFF) {
01338         TRACE("MCI_SET_OFF audio !\n");
01339     } else {
01340         WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
01341         return MCIERR_BAD_INTEGER;
01342     }
01343 
01344     switch (lpParms->dwAudio)
01345         {
01346         case MCI_SET_AUDIO_ALL:         TRACE("MCI_SET_AUDIO_ALL !\n"); break;
01347         case MCI_SET_AUDIO_LEFT:        TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
01348         case MCI_SET_AUDIO_RIGHT:       TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
01349         default:                        WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
01350         }
01351     }
01352     if (dwFlags & MCI_WAVE_INPUT) {
01353     TRACE("MCI_WAVE_INPUT = %d\n", lpParms->wInput);
01354     if (lpParms->wInput >= waveInGetNumDevs())
01355         return MCIERR_OUTOFRANGE;
01356     if (wmw->wInput != (WORD)lpParms->wInput)
01357         WAVE_mciStop(wDevID, MCI_WAIT, NULL);
01358     wmw->wInput = lpParms->wInput;
01359     }
01360     if (dwFlags & MCI_WAVE_OUTPUT) {
01361     TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms->wOutput);
01362     if (lpParms->wOutput >= waveOutGetNumDevs())
01363         return MCIERR_OUTOFRANGE;
01364     if (wmw->wOutput != (WORD)lpParms->wOutput)
01365         WAVE_mciStop(wDevID, MCI_WAIT, NULL);
01366     wmw->wOutput = lpParms->wOutput;
01367     }
01368     if (dwFlags & MCI_WAVE_SET_ANYINPUT) {
01369     TRACE("MCI_WAVE_SET_ANYINPUT\n");
01370     if (wmw->wInput != (WORD)lpParms->wInput)
01371         WAVE_mciStop(wDevID, MCI_WAIT, NULL);
01372     wmw->wInput = WAVE_MAPPER;
01373     }
01374     if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) {
01375     TRACE("MCI_WAVE_SET_ANYOUTPUT\n");
01376     if (wmw->wOutput != (WORD)lpParms->wOutput)
01377         WAVE_mciStop(wDevID, MCI_WAIT, NULL);
01378     wmw->wOutput = WAVE_MAPPER;
01379     }
01380     /* Set wave format parameters is refused after Open or Record.*/
01381     if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
01382     TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms->wFormatTag);
01383     if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
01384     if (lpParms->wFormatTag != WAVE_FORMAT_PCM)
01385         return MCIERR_OUTOFRANGE;
01386     }
01387     if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
01388     if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
01389     wmw->wfxRef.nAvgBytesPerSec = lpParms->nAvgBytesPerSec;
01390     TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
01391     }
01392     if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
01393     if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
01394     wmw->wfxRef.wBitsPerSample = lpParms->wBitsPerSample;
01395     TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
01396     }
01397     if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
01398     if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
01399     wmw->wfxRef.nBlockAlign = lpParms->nBlockAlign;
01400     TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
01401     }
01402     if (dwFlags & MCI_WAVE_SET_CHANNELS) {
01403     if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
01404     wmw->wfxRef.nChannels = lpParms->nChannels;
01405     TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
01406     }
01407     if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
01408     if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
01409     wmw->wfxRef.nSamplesPerSec = lpParms->nSamplesPerSec;
01410     TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
01411     }
01412     if (dwFlags & MCI_NOTIFY)
01413     WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
01414     return 0;
01415 }
01416 
01417 /**************************************************************************
01418  *              WAVE_mciSave        [internal]
01419  */
01420 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
01421 {
01422     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
01423     DWORD       ret = MCIERR_FILE_NOT_SAVED, tmpRet;
01424 
01425     TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
01426     if (lpParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
01427     if (wmw     == NULL)    return MCIERR_INVALID_DEVICE_ID;
01428 
01429     if (dwFlags & MCI_WAIT)
01430     {
01431         FIXME("MCI_WAIT not implemented\n");
01432     }
01433     WAVE_mciStop(wDevID, 0, NULL);
01434 
01435     ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
01436     ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
01437 
01438     ret = mmioClose(wmw->hFile, 0);
01439     wmw->hFile = 0;
01440 
01441     /*
01442       If the destination file already exists, it has to be overwritten.  (Behaviour
01443       verified in Windows (2000)).  If it doesn't overwrite, it is breaking one of
01444       my applications.  We are making use of mmioRename, which WILL NOT overwrite
01445       the destination file (which is what Windows does, also verified in Win2K)
01446       So, lets delete the destination file before calling mmioRename.  If the
01447       destination file DOESN'T exist, the delete will fail silently.  Let's also be
01448       careful not to lose our previous error code.
01449     */
01450     tmpRet = GetLastError();
01451     DeleteFileW (lpParms->lpfilename);
01452     SetLastError(tmpRet);
01453 
01454     /* FIXME: Open file.wav; Save; must not rename the original file.
01455      * Nor must Save a.wav; Save b.wav rename a. */
01456     if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
01457     ret = MMSYSERR_NOERROR;
01458     }
01459 
01460     if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
01461     WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
01462 
01463     if (ret == MMSYSERR_NOERROR)
01464         ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
01465 
01466     return ret;
01467 }
01468 
01469 /**************************************************************************
01470  *              WAVE_mciStatus      [internal]
01471  */
01472 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
01473 {
01474     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
01475     DWORD       ret = 0;
01476 
01477     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
01478     if (lpParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
01479     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
01480     if (!(dwFlags & MCI_STATUS_ITEM))   return MCIERR_MISSING_PARAMETER;
01481 
01482     if (dwFlags & MCI_STATUS_ITEM) {
01483     switch (lpParms->dwItem) {
01484     case MCI_STATUS_CURRENT_TRACK:
01485         lpParms->dwReturn = 1;
01486             TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
01487         break;
01488     case MCI_STATUS_LENGTH:
01489         if (!wmw->hFile) {
01490         lpParms->dwReturn = 0;
01491         return MCIERR_UNSUPPORTED_FUNCTION;
01492         }
01493         /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
01494         lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize);
01495             TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
01496         break;
01497     case MCI_STATUS_MODE:
01498         TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
01499         lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
01500         ret = MCI_RESOURCE_RETURNED;
01501         break;
01502     case MCI_STATUS_MEDIA_PRESENT:
01503         TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
01504         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01505         ret = MCI_RESOURCE_RETURNED;
01506         break;
01507     case MCI_STATUS_NUMBER_OF_TRACKS:
01508         /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
01509         lpParms->dwReturn = 1;
01510             TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
01511         break;
01512     case MCI_STATUS_POSITION:
01513         if (!wmw->hFile) {
01514         lpParms->dwReturn = 0;
01515         return MCIERR_UNSUPPORTED_FUNCTION;
01516         }
01517         /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
01518         lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
01519                                  (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
01520             TRACE("MCI_STATUS_POSITION %s => %lu\n",
01521           (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
01522         break;
01523     case MCI_STATUS_READY:
01524         lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
01525         MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01526         TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
01527         ret = MCI_RESOURCE_RETURNED;
01528         break;
01529     case MCI_STATUS_TIME_FORMAT:
01530         lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
01531             TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
01532         ret = MCI_RESOURCE_RETURNED;
01533         break;
01534     case MCI_WAVE_INPUT:
01535         if (wmw->wInput != (WORD)WAVE_MAPPER)
01536         lpParms->dwReturn = wmw->wInput;
01537         else {
01538         lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
01539         ret = MCI_RESOURCE_RETURNED;
01540         }
01541         TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw->wInput);
01542         break;
01543     case MCI_WAVE_OUTPUT:
01544         if (wmw->wOutput != (WORD)WAVE_MAPPER)
01545         lpParms->dwReturn = wmw->wOutput;
01546         else {
01547         lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
01548         ret = MCI_RESOURCE_RETURNED;
01549         }
01550         TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw->wOutput);
01551         break;
01552     /* It is always ok to query wave format parameters,
01553      * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
01554     case MCI_WAVE_STATUS_FORMATTAG:
01555         if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
01556         lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
01557         else {
01558         lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_FORMAT_PCM, WAVE_FORMAT_PCM_S);
01559         ret = MCI_RESOURCE_RETURNED;
01560         }
01561         TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
01562         break;
01563     case MCI_WAVE_STATUS_AVGBYTESPERSEC:
01564         lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
01565         TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
01566         break;
01567     case MCI_WAVE_STATUS_BITSPERSAMPLE:
01568         lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
01569         TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
01570         break;
01571     case MCI_WAVE_STATUS_BLOCKALIGN:
01572         lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
01573         TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
01574         break;
01575     case MCI_WAVE_STATUS_CHANNELS:
01576         lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
01577         TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
01578         break;
01579     case MCI_WAVE_STATUS_SAMPLESPERSEC:
01580         lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
01581         TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
01582         break;
01583     case MCI_WAVE_STATUS_LEVEL:
01584         TRACE("MCI_WAVE_STATUS_LEVEL !\n");
01585         lpParms->dwReturn = 0xAAAA5555;
01586         break;
01587     default:
01588             WARN("unknown command %08X !\n", lpParms->dwItem);
01589         return MCIERR_UNSUPPORTED_FUNCTION;
01590     }
01591     }
01592     if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
01593     WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
01594     return ret;
01595 }
01596 
01597 /**************************************************************************
01598  *              WAVE_mciGetDevCaps      [internal]
01599  */
01600 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
01601                 LPMCI_GETDEVCAPS_PARMS lpParms)
01602 {
01603     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
01604     DWORD       ret = 0;
01605 
01606     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
01607 
01608     if (lpParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
01609     if (wmw == NULL)        return MCIERR_INVALID_DEVICE_ID;
01610 
01611     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
01612     switch(lpParms->dwItem) {
01613     case MCI_GETDEVCAPS_DEVICE_TYPE:
01614         lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
01615         ret = MCI_RESOURCE_RETURNED;
01616         break;
01617     case MCI_GETDEVCAPS_HAS_AUDIO:
01618         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01619         ret = MCI_RESOURCE_RETURNED;
01620         break;
01621     case MCI_GETDEVCAPS_HAS_VIDEO:
01622         lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
01623         ret = MCI_RESOURCE_RETURNED;
01624         break;
01625     case MCI_GETDEVCAPS_USES_FILES:
01626         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01627         ret = MCI_RESOURCE_RETURNED;
01628         break;
01629     case MCI_GETDEVCAPS_COMPOUND_DEVICE:
01630         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01631         ret = MCI_RESOURCE_RETURNED;
01632         break;
01633     case MCI_GETDEVCAPS_CAN_RECORD:
01634         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01635         ret = MCI_RESOURCE_RETURNED;
01636         break;
01637     case MCI_GETDEVCAPS_CAN_EJECT:
01638         lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
01639         ret = MCI_RESOURCE_RETURNED;
01640         break;
01641     case MCI_GETDEVCAPS_CAN_PLAY:
01642         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01643         ret = MCI_RESOURCE_RETURNED;
01644         break;
01645     case MCI_GETDEVCAPS_CAN_SAVE:
01646         lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
01647         ret = MCI_RESOURCE_RETURNED;
01648         break;
01649     case MCI_WAVE_GETDEVCAPS_INPUTS:
01650         lpParms->dwReturn = waveInGetNumDevs();
01651         break;
01652     case MCI_WAVE_GETDEVCAPS_OUTPUTS:
01653         lpParms->dwReturn = waveOutGetNumDevs();
01654         break;
01655     default:
01656             FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
01657         return MCIERR_UNRECOGNIZED_COMMAND;
01658     }
01659     } else {
01660     WARN("No GetDevCaps-Item !\n");
01661     return MCIERR_UNRECOGNIZED_COMMAND;
01662     }
01663     if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
01664     WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
01665     return ret;
01666 }
01667 
01668 /**************************************************************************
01669  *              WAVE_mciInfo            [internal]
01670  */
01671 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
01672 {
01673     DWORD       ret = 0;
01674     LPCWSTR     str = 0;
01675     WINE_MCIWAVE*   wmw = WAVE_mciGetOpenDev(wDevID);
01676 
01677     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
01678 
01679     if (!lpParms || !lpParms->lpstrReturn)
01680     return MCIERR_NULL_PARAMETER_BLOCK;
01681 
01682     TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
01683 
01684     if (wmw == NULL) {
01685     ret = MCIERR_INVALID_DEVICE_ID;
01686     } else {
01687         static const WCHAR wszAudio  [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
01688         static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
01689         static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
01690 
01691     switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
01692     case MCI_INFO_PRODUCT: str = wszAudio; break;
01693     case MCI_INFO_FILE:    str = wmw->lpFileName; break;
01694     case MCI_WAVE_INPUT:   str = wszWaveIn; break;
01695     case MCI_WAVE_OUTPUT:  str = wszWaveOut; break;
01696     default:
01697             WARN("Don't know this info command (%u)\n", dwFlags);
01698         ret = MCIERR_UNRECOGNIZED_KEYWORD;
01699     }
01700     }
01701     if (!ret) {
01702     if (lpParms->dwRetSize) {
01703         WCHAR zero = 0;
01704         /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
01705          *        to the number of characters written, excluding \0. */
01706         lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize);
01707     } else ret = MCIERR_PARAM_OVERFLOW;
01708     }
01709     if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
01710     WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
01711     return ret;
01712 }
01713 
01714 /**************************************************************************
01715  *              DriverProc (MCIWAVE.@)
01716  */
01717 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
01718                                     LPARAM dwParam1, LPARAM dwParam2)
01719 {
01720     TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
01721       dwDevID, hDriv, wMsg, dwParam1, dwParam2);
01722 
01723     switch (wMsg) {
01724     case DRV_LOAD:      return 1;
01725     case DRV_FREE:      return 1;
01726     case DRV_OPEN:      return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
01727     case DRV_CLOSE:     return WAVE_drvClose(dwDevID);
01728     case DRV_ENABLE:        return 1;
01729     case DRV_DISABLE:       return 1;
01730     case DRV_QUERYCONFIGURE:    return 1;
01731     case DRV_CONFIGURE:     MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
01732     case DRV_INSTALL:       return DRVCNF_RESTART;
01733     case DRV_REMOVE:        return DRVCNF_RESTART;
01734     }
01735 
01736     if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
01737 
01738     switch (wMsg) {
01739     case MCI_OPEN_DRIVER:   return WAVE_mciOpen      (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW)  dwParam2);
01740     case MCI_CLOSE_DRIVER:  return WAVE_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
01741     case MCI_CUE:       return WAVE_mciCue       (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
01742     case MCI_PLAY:      return WAVE_mciPlay      (dwDevID, dwParam1, dwParam2, NULL);
01743     case MCI_RECORD:        return WAVE_mciRecord    (dwDevID, dwParam1, dwParam2, NULL);
01744     case MCI_STOP:      return WAVE_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
01745     case MCI_SET:       return WAVE_mciSet       (dwDevID, dwParam1, (LPMCI_WAVE_SET_PARMS)    dwParam2);
01746     case MCI_PAUSE:     return WAVE_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
01747     case MCI_RESUME:        return WAVE_mciResume    (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
01748     case MCI_STATUS:        return WAVE_mciStatus    (dwDevID, dwParam1, (LPMCI_STATUS_PARMS)      dwParam2);
01749     case MCI_GETDEVCAPS:    return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)  dwParam2);
01750     case MCI_INFO:      return WAVE_mciInfo      (dwDevID, dwParam1, (LPMCI_INFO_PARMSW)       dwParam2);
01751     case MCI_SEEK:      return WAVE_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)        dwParam2);
01752     case MCI_SAVE:      return WAVE_mciSave  (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW)       dwParam2);
01753     /* commands that should be supported */
01754     case MCI_LOAD:
01755     case MCI_FREEZE:
01756     case MCI_PUT:
01757     case MCI_REALIZE:
01758     case MCI_UNFREEZE:
01759     case MCI_UPDATE:
01760     case MCI_WHERE:
01761     case MCI_STEP:
01762     case MCI_SPIN:
01763     case MCI_ESCAPE:
01764     case MCI_COPY:
01765     case MCI_CUT:
01766     case MCI_DELETE:
01767     case MCI_PASTE:
01768     FIXME("Unsupported command [%u]\n", wMsg);
01769     break;
01770     case MCI_WINDOW:
01771     TRACE("Unsupported command [%u]\n", wMsg);
01772     break;
01773     /* option which can be silenced */
01774     case MCI_CONFIGURE:
01775     return 0;
01776     case MCI_OPEN:
01777     case MCI_CLOSE:
01778     ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
01779     break;
01780     default:
01781     FIXME("is probably wrong msg [%u]\n", wMsg);
01782     return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
01783     }
01784     return MCIERR_UNRECOGNIZED_COMMAND;
01785 }

Generated on Sat May 26 2012 04:23:15 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.