wined3d: Use struct opengl_funcs for storing GL extension pointers.
[wine] / dlls / mciwave / mciwave.c
1 /*
2  * Wine Driver for MCI wave forms
3  *
4  * Copyright    1994 Martin Ayotte
5  *              1999,2000,2005 Eric Pouech
6  *              2000 Francois Jacques
7  *              2009 Jörg Höhle
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include <assert.h>
25 #include <stdarg.h>
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "mmddk.h"
32 #include "wownt32.h"
33 #include "digitalv.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
38
39 typedef struct {
40     UINT                        wDevID;
41     HANDLE                      hWave;
42     int                         nUseCount;      /* Incremented for each shared open */
43     HMMIO                       hFile;          /* mmio file handle open as Element */
44     MCIDEVICEID                 wNotifyDeviceID;        /* MCI device ID with a pending notification */
45     HANDLE                      hCallback;      /* Callback handle for pending notification */
46     LPWSTR                      lpFileName;     /* Name of file (if any)                     */
47     WAVEFORMATEX                wfxRef;
48     LPWAVEFORMATEX              lpWaveFormat;   /* Points to wfxRef until set by OPEN or RECORD */
49     BOOL                        fInput;         /* FALSE = Output, TRUE = Input */
50     WORD                        wInput;         /* wave input device */
51     WORD                        wOutput;        /* wave output device */
52     volatile WORD               dwStatus;       /* one from MCI_MODE_xxxx */
53     DWORD                       dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
54     DWORD                       dwPosition;     /* position in bytes in chunk */
55     HANDLE                      hEvent;         /* for synchronization */
56     LONG                        dwEventCount;   /* for synchronization */
57     MMCKINFO                    ckMainRIFF;     /* main RIFF chunk */
58     MMCKINFO                    ckWaveData;     /* data chunk */
59 } WINE_MCIWAVE;
60
61 /* ===================================================================
62  * ===================================================================
63  * FIXME: should be using the new mmThreadXXXX functions from WINMM
64  * instead of those
65  * it would require to add a wine internal flag to mmThreadCreate
66  * in order to pass a 32 bit function instead of a 16 bit one
67  * ===================================================================
68  * =================================================================== */
69
70 typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
71
72 struct SCA {
73     async_cmd   cmd;
74     HANDLE      evt;
75     UINT        wDevID;
76     DWORD_PTR   dwParam1;
77     DWORD_PTR   dwParam2;
78 };
79
80 /**************************************************************************
81  *                              MCI_SCAStarter                  [internal]
82  */
83 static DWORD CALLBACK   MCI_SCAStarter(LPVOID arg)
84 {
85     struct SCA* sca = (struct SCA*)arg;
86     DWORD               ret;
87
88     TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
89           sca->wDevID, sca->dwParam1, sca->dwParam2);
90     ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
91     TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
92           sca->wDevID, sca->dwParam1, sca->dwParam2);
93     HeapFree(GetProcessHeap(), 0, sca);
94     return ret;
95 }
96
97 /**************************************************************************
98  *                              MCI_SendCommandAsync            [internal]
99  */
100 static  DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
101                                    DWORD_PTR dwParam2, UINT size)
102 {
103     HANDLE handles[2];
104     struct SCA* sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
105
106     if (sca == 0)
107         return MCIERR_OUT_OF_MEMORY;
108
109     sca->wDevID   = wDevID;
110     sca->cmd      = cmd;
111     sca->dwParam1 = dwParam1;
112
113     if (size && dwParam2) {
114         sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
115         /* copy structure passed by program in dwParam2 to be sure
116          * we can still use it whatever the program does
117          */
118         memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
119     } else {
120         sca->dwParam2 = dwParam2;
121     }
122
123     if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL ||
124         (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
125         WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
126         if (handles[1]) CloseHandle(handles[1]);
127         sca->evt = NULL;
128         return MCI_SCAStarter(&sca);
129     }
130
131     SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL);
132     /* wait until either:
133      * - the thread has finished (handles[0], likely an error)
134      * - init phase of async command is done (handles[1])
135      */
136     WaitForMultipleObjects(2, handles, FALSE, INFINITE);
137     CloseHandle(handles[0]);
138     CloseHandle(handles[1]);
139     return 0;
140 }
141
142 /*======================================================================*
143  *                          MCI WAVE implementation                     *
144  *======================================================================*/
145
146 static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
147
148 /**************************************************************************
149  *                              MCIWAVE_drvOpen                 [internal]
150  */
151 static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
152 {
153     WINE_MCIWAVE*       wmw;
154
155     if (modp == NULL) return 0xFFFFFFFF;
156
157     wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
158
159     if (!wmw)
160         return 0;
161
162     wmw->wDevID = modp->wDeviceID;
163     mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
164     modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
165     modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
166
167     wmw->wfxRef.wFormatTag      = WAVE_FORMAT_PCM;
168     wmw->wfxRef.nChannels       = 1;      /* MONO */
169     wmw->wfxRef.nSamplesPerSec  = 11025;
170     wmw->wfxRef.nAvgBytesPerSec = 11025;
171     wmw->wfxRef.nBlockAlign     = 1;
172     wmw->wfxRef.wBitsPerSample  = 8;
173     wmw->wfxRef.cbSize          = 0;      /* don't care */
174
175     return modp->wDeviceID;
176 }
177
178 /**************************************************************************
179  *                              MCIWAVE_drvClose                [internal]
180  */
181 static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
182 {
183     WINE_MCIWAVE*  wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
184
185     if (wmw) {
186         HeapFree(GetProcessHeap(), 0, wmw);
187         mciSetDriverData(dwDevID, 0);
188         return 1;
189     }
190     return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
191 }
192
193 /**************************************************************************
194  *                              WAVE_mciGetOpenDev              [internal]
195  */
196 static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
197 {
198     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
199
200     if (wmw == NULL || wmw->nUseCount == 0) {
201         WARN("Invalid wDevID=%u\n", wDevID);
202         return 0;
203     }
204     return wmw;
205 }
206
207 /**************************************************************************
208  *                              WAVE_mciNotify                  [internal]
209  *
210  * Notifications in MCI work like a 1-element queue.
211  * Each new notification request supersedes the previous one.
212  * This affects Play and Record; other commands are immediate.
213  */
214 static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus)
215 {
216     /* We simply save one parameter by not passing the wDevID local
217      * to the command.  They are the same (via mciGetDriverData).
218      */
219     MCIDEVICEID wDevID = wmw->wNotifyDeviceID;
220     HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
221     if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
222     mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
223 }
224
225 /**************************************************************************
226  *                              WAVE_ConvertByteToTimeFormat    [internal]
227  */
228 static  DWORD   WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val)
229 {
230     DWORD          ret = 0;
231
232     switch (wmw->dwMciTimeFormat) {
233     case MCI_FORMAT_MILLISECONDS:
234         ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
235         break;
236     case MCI_FORMAT_BYTES:
237         ret = val;
238         break;
239     case MCI_FORMAT_SAMPLES:
240         ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
241         break;
242     default:
243         WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
244     }
245     TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
246     return ret;
247 }
248
249 /**************************************************************************
250  *                              WAVE_ConvertTimeFormatToByte    [internal]
251  */
252 static  DWORD   WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
253 {
254     DWORD       ret = 0;
255
256     switch (wmw->dwMciTimeFormat) {
257     case MCI_FORMAT_MILLISECONDS:
258         ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
259         if (ret > wmw->ckWaveData.cksize &&
260             val == WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize))
261             ret = wmw->ckWaveData.cksize;
262         break;
263     case MCI_FORMAT_BYTES:
264         ret = val;
265         break;
266     case MCI_FORMAT_SAMPLES:
267         ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
268         break;
269     default:
270         WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
271     }
272     TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
273     return ret;
274 }
275
276 /**************************************************************************
277  *                      WAVE_mciReadFmt                         [internal]
278  */
279 static  DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
280 {
281     MMCKINFO    mmckInfo;
282     LONG        r;
283     LPWAVEFORMATEX pwfx;
284
285     mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
286     if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
287         return MCIERR_INVALID_FILE;
288     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
289           (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
290
291     pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
292     if (!pwfx) return MCIERR_OUT_OF_MEMORY;
293
294     r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize);
295     if (r < sizeof(PCMWAVEFORMAT)) {
296         HeapFree(GetProcessHeap(), 0, pwfx);
297         return MCIERR_INVALID_FILE;
298     }
299     TRACE("wFormatTag=%04X !\n",   pwfx->wFormatTag);
300     TRACE("nChannels=%d\n",        pwfx->nChannels);
301     TRACE("nSamplesPerSec=%d\n",   pwfx->nSamplesPerSec);
302     TRACE("nAvgBytesPerSec=%d\n",  pwfx->nAvgBytesPerSec);
303     TRACE("nBlockAlign=%d\n",      pwfx->nBlockAlign);
304     TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample);
305     if (r >= sizeof(WAVEFORMATEX))
306         TRACE("cbSize=%u !\n",     pwfx->cbSize);
307     if ((pwfx->wFormatTag != WAVE_FORMAT_PCM)
308         && (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) {
309         HeapFree(GetProcessHeap(), 0, pwfx);
310         return MCIERR_INVALID_FILE;
311     }
312     wmw->lpWaveFormat = pwfx;
313
314     mmioAscend(wmw->hFile, &mmckInfo, 0);
315     wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
316     if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
317         TRACE("can't find data chunk\n");
318         return MCIERR_INVALID_FILE;
319     }
320     TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
321           (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
322     return 0;
323 }
324
325 /**************************************************************************
326  *                      WAVE_mciDefaultFmt                      [internal]
327  *
328  * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
329  * until either Open File or Record.  It becomes immutable afterwards,
330  * i.e. Set wave format or channels etc. is subsequently refused.
331  */
332 static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
333 {
334     wmw->lpWaveFormat = &wmw->wfxRef;
335     wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
336     wmw->lpWaveFormat->nChannels = 1;
337     wmw->lpWaveFormat->nSamplesPerSec = 11025;
338     wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
339     wmw->lpWaveFormat->nBlockAlign = 1;
340     wmw->lpWaveFormat->wBitsPerSample = 8;
341     wmw->lpWaveFormat->cbSize = 0;
342 }
343
344 /**************************************************************************
345  *                      WAVE_mciCreateRIFFSkeleton              [internal]
346  */
347 static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
348 {
349    MMCKINFO     ckWaveFormat;
350    LPMMCKINFO   lpckRIFF     = &(wmw->ckMainRIFF);
351    LPMMCKINFO   lpckWaveData = &(wmw->ckWaveData);
352
353    lpckRIFF->ckid    = FOURCC_RIFF;
354    lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
355    lpckRIFF->cksize  = 0;
356
357    if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
358         goto err;
359
360    ckWaveFormat.fccType = 0;
361    ckWaveFormat.ckid    = mmioFOURCC('f', 'm', 't', ' ');
362    ckWaveFormat.cksize  = sizeof(PCMWAVEFORMAT);
363
364    /* Set wave format accepts PCM only, however open an
365     * existing ADPCM file, record into it and the MCI will
366     * happily save back in that format. */
367    if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
368         if (wmw->lpWaveFormat->nBlockAlign !=
369             wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
370             WORD size = wmw->lpWaveFormat->nChannels *
371                 wmw->lpWaveFormat->wBitsPerSample/8;
372             WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
373                 wmw->lpWaveFormat->nBlockAlign, size);
374             wmw->lpWaveFormat->nBlockAlign = size;
375         }
376         if (wmw->lpWaveFormat->nAvgBytesPerSec !=
377             wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
378             DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
379                 wmw->lpWaveFormat->nBlockAlign;
380             WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
381                 wmw->lpWaveFormat->nAvgBytesPerSec, speed);
382             wmw->lpWaveFormat->nAvgBytesPerSec = speed;
383         }
384    }
385    if (wmw->lpWaveFormat == &wmw->wfxRef) {
386         LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
387         if (!pwfx) return MCIERR_OUT_OF_MEMORY;
388         /* Set wave format accepts PCM only so the size is known. */
389         assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM);
390         *pwfx = wmw->wfxRef;
391         wmw->lpWaveFormat = pwfx;
392    }
393
394    if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
395         goto err;
396
397    if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag)
398         ? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize))
399         goto err;
400
401    if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
402         goto err;
403
404    lpckWaveData->cksize  = 0;
405    lpckWaveData->fccType = 0;
406    lpckWaveData->ckid    = mmioFOURCC('d', 'a', 't', 'a');
407
408    /* create data chunk */
409    if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
410         goto err;
411
412    return 0;
413
414 err:
415    /* mciClose takes care of wmw->lpWaveFormat. */
416    return MCIERR_INVALID_FILE;
417 }
418
419 static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
420 {
421     WCHAR       szTmpPath[MAX_PATH];
422     WCHAR       szPrefix[4];
423     DWORD       dwRet = MMSYSERR_NOERROR;
424
425     szPrefix[0] = 'M';
426     szPrefix[1] = 'C';
427     szPrefix[2] = 'I';
428     szPrefix[3] = '\0';
429
430     if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
431         WARN("can't retrieve temp path!\n");
432         *pszTmpFileName = NULL;
433         return MCIERR_FILE_NOT_FOUND;
434     }
435
436     *pszTmpFileName = HeapAlloc(GetProcessHeap(),
437                                 HEAP_ZERO_MEMORY,
438                                 MAX_PATH * sizeof(WCHAR));
439     if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
440         WARN("can't retrieve temp file name!\n");
441         HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
442         return MCIERR_FILE_NOT_FOUND;
443     }
444
445     TRACE("%s!\n", debugstr_w(*pszTmpFileName));
446
447     if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {
448
449         *hFile = mmioOpenW(*pszTmpFileName, NULL,
450                            MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);
451
452         if (*hFile == 0) {
453             WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
454             /* temporary file could not be created. clean filename. */
455             HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
456             dwRet = MCIERR_FILE_NOT_FOUND;
457         }
458     }
459     return dwRet;
460 }
461
462 static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename)
463 {
464     LRESULT dwRet = MMSYSERR_NOERROR;
465     LPWSTR fn;
466
467     fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
468     if (!fn) return MCIERR_OUT_OF_MEMORY;
469     strcpyW(fn, filename);
470     HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
471     wmw->lpFileName = fn;
472
473     if (strlenW(filename) > 0) {
474         /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
475         TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));
476
477         wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
478                                MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);
479
480         if (wmw->hFile == 0) {
481             WARN("can't find file=%s!\n", debugstr_w(filename));
482             dwRet = MCIERR_FILE_NOT_FOUND;
483         }
484         else
485         {
486             LPMMCKINFO          lpckMainRIFF = &wmw->ckMainRIFF;
487
488             /* make sure we're at the beginning of the file */
489             mmioSeek(wmw->hFile, 0, SEEK_SET);
490
491             /* first reading of this file. read the waveformat chunk */
492             if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
493                 dwRet = MCIERR_INVALID_FILE;
494             } else {
495                 TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
496                       (LPSTR)&(lpckMainRIFF->ckid),
497                       (LPSTR) &(lpckMainRIFF->fccType),
498                       (lpckMainRIFF->cksize));
499
500                 if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
501                     lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
502                     dwRet = MCIERR_INVALID_FILE;
503                 } else {
504                     dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
505                 }
506             }
507         }
508     }
509     return dwRet;
510 }
511
512 /**************************************************************************
513  *                      WAVE_mciOpen                            [internal]
514  */
515 static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
516 {
517     DWORD               dwRet = 0;
518     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
519
520     TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
521     if (lpOpenParms == NULL)    return MCIERR_NULL_PARAMETER_BLOCK;
522     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
523
524     if (dwFlags & MCI_OPEN_SHAREABLE)
525         return MCIERR_UNSUPPORTED_FUNCTION;
526
527     if (wmw->nUseCount > 0) {
528         /* The driver is already opened on this channel
529          * Wave driver cannot be shared
530          */
531         return MCIERR_DEVICE_OPEN;
532     }
533
534     wmw->nUseCount++;
535
536     wmw->wInput = wmw->wOutput = WAVE_MAPPER;
537     wmw->fInput = FALSE;
538     wmw->hWave = 0;
539     wmw->dwStatus = MCI_MODE_NOT_READY;
540     wmw->hFile = 0;
541     wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */
542     wmw->hCallback = NULL;
543     WAVE_mciDefaultFmt(wmw);
544
545     TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
546     /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
547     wmw->wNotifyDeviceID = wDevID;
548
549     if (dwFlags & MCI_OPEN_ELEMENT) {
550         if (dwFlags & MCI_OPEN_ELEMENT_ID) {
551             /* could it be that (DWORD)lpOpenParms->lpstrElementName
552              * contains the hFile value ?
553              */
554             dwRet = MCIERR_UNRECOGNIZED_COMMAND;
555         } else {
556             dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
557         }
558     }
559     TRACE("hFile=%p\n", wmw->hFile);
560
561     if (dwRet == 0) {
562         wmw->dwPosition = 0;
563
564         wmw->dwStatus = MCI_MODE_STOP;
565
566         if (dwFlags & MCI_NOTIFY)
567             WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
568     } else {
569         wmw->nUseCount--;
570         if (wmw->hFile != 0)
571             mmioClose(wmw->hFile, 0);
572         wmw->hFile = 0;
573         HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
574         wmw->lpFileName = NULL;
575     }
576     return dwRet;
577 }
578
579 /**************************************************************************
580  *                               WAVE_mciCue             [internal]
581  */
582 static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
583 {
584     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
585
586     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
587
588     /* Tests on systems without sound drivers show that Cue, like
589      * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
590      * The first Cue Notify does not immediately return the
591      * notification, as if a player or recorder thread is started.
592      * PAUSE mode is reported when successful, but this mode is
593      * different from the normal Pause, because a) Pause then returns
594      * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
595      * still accepted, returning the original notification as ABORTED.
596      * I.e. Cue allows subsequent format changes, unlike Record or
597      * Open file, closes winmm if the format changes and stops this
598      * thread.
599      * Wine creates one player or recorder thread per async. Play or
600      * Record command.  Notification behaviour suggests that MS-W*
601      * reuses a single thread to improve response times.  Having Cue
602      * start this thread early helps to improve Play/Record's initial
603      * response time.  In effect, Cue is a performance hint, which
604      * justifies our almost no-op implementation.
605      */
606
607     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
608     if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
609
610     if ((dwFlags & MCI_NOTIFY) && lpParms)
611         WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
612
613     return MMSYSERR_NOERROR;
614 }
615
616 /**************************************************************************
617  *                              WAVE_mciStop                    [internal]
618  */
619 static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
620 {
621     DWORD               dwRet = 0;
622     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
623
624     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
625
626     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
627
628     if (wmw->dwStatus != MCI_MODE_STOP) {
629         HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
630         if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
631     }
632
633     /* wait for playback thread (if any) to exit before processing further */
634     switch (wmw->dwStatus) {
635     case MCI_MODE_PAUSE:
636     case MCI_MODE_PLAY:
637     case MCI_MODE_RECORD:
638         {
639             int oldStat = wmw->dwStatus;
640             wmw->dwStatus = MCI_MODE_NOT_READY;
641             if (oldStat == MCI_MODE_PAUSE)
642                 dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
643         }
644         while (wmw->dwStatus != MCI_MODE_STOP)
645             Sleep(10);
646         break;
647     }
648
649     /* sanity resets */
650     wmw->dwStatus = MCI_MODE_STOP;
651
652     if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
653         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
654
655     return dwRet;
656 }
657
658 /**************************************************************************
659  *                              WAVE_mciClose           [internal]
660  */
661 static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
662 {
663     DWORD               dwRet = 0;
664     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
665
666     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
667
668     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
669
670     if (wmw->dwStatus != MCI_MODE_STOP) {
671         /* mciStop handles MCI_NOTIFY_ABORTED */
672         dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
673     }
674
675     wmw->nUseCount--;
676
677     if (wmw->nUseCount == 0) {
678         if (wmw->hFile != 0) {
679             mmioClose(wmw->hFile, 0);
680             wmw->hFile = 0;
681         }
682     }
683
684     if (wmw->lpWaveFormat != &wmw->wfxRef)
685         HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
686     wmw->lpWaveFormat = &wmw->wfxRef;
687     HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
688     wmw->lpFileName = NULL;
689
690     if ((dwFlags & MCI_NOTIFY) && lpParms) {
691         WAVE_mciNotify(lpParms->dwCallback, wmw,
692             (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
693     }
694
695     return 0;
696 }
697
698 /**************************************************************************
699  *                              WAVE_mciPlayCallback            [internal]
700  */
701 static  void    CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
702                                               DWORD_PTR dwInstance,
703                                               LPARAM dwParam1, LPARAM dwParam2)
704 {
705     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)dwInstance;
706
707     switch (uMsg) {
708     case WOM_OPEN:
709     case WOM_CLOSE:
710         break;
711     case WOM_DONE:
712         InterlockedIncrement(&wmw->dwEventCount);
713         TRACE("Returning waveHdr=%lx\n", dwParam1);
714         SetEvent(wmw->hEvent);
715         break;
716     default:
717         ERR("Unknown uMsg=%d\n", uMsg);
718     }
719 }
720
721 /******************************************************************
722  *                      WAVE_mciPlayWaitDone            [internal]
723  */
724 static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
725 {
726     for (;;) {
727         ResetEvent(wmw->hEvent);
728         if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
729             break;
730         }
731         InterlockedIncrement(&wmw->dwEventCount);
732
733         WaitForSingleObject(wmw->hEvent, INFINITE);
734     }
735 }
736
737 /**************************************************************************
738  *                              WAVE_mciPlay            [internal]
739  */
740 static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
741 {
742     LPMCI_PLAY_PARMS    lpParms = (void*)pmt;
743     DWORD               end;
744     LONG                bufsize, count, left;
745     DWORD               dwRet;
746     LPWAVEHDR           waveHdr = NULL;
747     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
748     HANDLE              oldcb;
749     int                 whidx;
750
751     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
752
753     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
754     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
755
756     if (wmw->hFile == 0) {
757         WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
758         return MCIERR_FILE_NOT_FOUND;
759     }
760
761     if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput && !(dwFlags & (MCI_FROM | MCI_TO))) {
762         /* FIXME: notification is different with Resume than Play */
763         return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
764     }
765
766     /** This function will be called again by a thread when async is used.
767      * We have to set MCI_MODE_PLAY before we do this so that the app can spin
768      * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
769      */
770     if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
771         !((wmw->dwStatus == MCI_MODE_PLAY) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
772         /* FIXME: Check FROM/TO parameters first. */
773         /* FIXME: Play; Play [notify|wait] must hook into the running player. */
774         dwRet = WAVE_mciStop(wDevID, MCI_WAIT, NULL);
775         if (dwRet) return dwRet;
776     }
777
778     if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
779         if (wmw->lpWaveFormat->nBlockAlign !=
780             wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
781             WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
782                 wmw->lpWaveFormat->nBlockAlign,
783                 wmw->lpWaveFormat->nChannels *
784                  wmw->lpWaveFormat->wBitsPerSample/8);
785             wmw->lpWaveFormat->nBlockAlign =
786                 wmw->lpWaveFormat->nChannels *
787                 wmw->lpWaveFormat->wBitsPerSample/8;
788         }
789         if (wmw->lpWaveFormat->nAvgBytesPerSec !=
790             wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
791             WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
792                 wmw->lpWaveFormat->nAvgBytesPerSec,
793                 wmw->lpWaveFormat->nSamplesPerSec *
794                  wmw->lpWaveFormat->nBlockAlign);
795             wmw->lpWaveFormat->nAvgBytesPerSec =
796                 wmw->lpWaveFormat->nSamplesPerSec *
797                 wmw->lpWaveFormat->nBlockAlign;
798         }
799     }
800
801     end = wmw->ckWaveData.cksize;
802     if (dwFlags & MCI_TO) {
803         DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
804         if (position > end)             return MCIERR_OUTOFRANGE;
805         end = position;
806     }
807     if (dwFlags & MCI_FROM) {
808         DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
809         if (position > end)             return MCIERR_OUTOFRANGE;
810         /* Seek rounds down, so do we. */
811         position /= wmw->lpWaveFormat->nBlockAlign;
812         position *= wmw->lpWaveFormat->nBlockAlign;
813         wmw->dwPosition = position;
814     }
815     if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
816     left = end - wmw->dwPosition;
817     if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
818
819     wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
820     wmw->dwStatus = MCI_MODE_PLAY;
821
822     if (!(dwFlags & MCI_WAIT)) {
823         return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags,
824                                     (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
825     }
826
827     TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
828
829     oldcb = InterlockedExchangePointer(&wmw->hCallback,
830         (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
831     if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
832     oldcb = NULL;
833
834 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
835 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
836
837     /* go back to beginning of chunk plus the requested position */
838     /* FIXME: I'm not sure this is correct, notably because some data linked to
839      * the decompression state machine will not be correctly initialized.
840      * try it this way (other way would be to decompress from 0 up to dwPosition
841      * and to start sending to hWave when dwPosition is reached)
842      */
843     mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
844
845     dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, wmw->wOutput, wmw->lpWaveFormat,
846                         (DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
847
848     if (dwRet != 0) {
849         TRACE("Can't open low level audio device %d\n", dwRet);
850         dwRet = MCIERR_DEVICE_OPEN;
851         wmw->hWave = 0;
852         goto cleanUp;
853     }
854
855     /* make it so that 3 buffers per second are needed */
856     bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
857
858     waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
859     waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
860     waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
861     waveHdr[0].dwUser         = waveHdr[1].dwUser         = 0L;
862     waveHdr[0].dwLoops        = waveHdr[1].dwLoops        = 0L;
863     waveHdr[0].dwFlags        = waveHdr[1].dwFlags        = 0L;
864     waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
865     if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
866         waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
867         dwRet = MCIERR_INTERNAL;
868         goto cleanUp;
869     }
870
871     whidx = 0;
872     wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
873     if (!wmw->hEvent) {
874         dwRet = MCIERR_OUT_OF_MEMORY;
875         goto cleanUp;
876     }
877     wmw->dwEventCount = 1L; /* for first buffer */
878
879     TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
880     if (hEvent) SetEvent(hEvent);
881
882     /* FIXME: this doesn't work if wmw->dwPosition != 0 */
883     while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
884         count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
885         TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
886         if (count < 1)
887             break;
888         /* count is always <= bufsize, so this is correct regarding the
889          * waveOutPrepareHeader function
890          */
891         waveHdr[whidx].dwBufferLength = count;
892         waveHdr[whidx].dwFlags &= ~WHDR_DONE;
893         TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
894               &waveHdr[whidx], waveHdr[whidx].dwBufferLength);
895         dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
896         if (dwRet) {
897             ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet);
898             dwRet = MCIERR_HARDWARE;
899             break;
900         }
901         left -= count;
902         wmw->dwPosition += count;
903         TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
904         /* InterlockedDecrement if and only if waveOutWrite is successful */
905         WAVE_mciPlayWaitDone(wmw);
906         whidx ^= 1;
907     }
908
909     WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */
910
911     /* just to get rid of some race conditions between play, stop and pause */
912     waveOutReset(wmw->hWave);
913
914     waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
915     waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
916
917 cleanUp:
918     if (dwFlags & MCI_NOTIFY)
919         oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
920
921     HeapFree(GetProcessHeap(), 0, waveHdr);
922
923     if (wmw->hWave) {
924         waveOutClose(wmw->hWave);
925         wmw->hWave = 0;
926     }
927     CloseHandle(wmw->hEvent);
928     wmw->hEvent = NULL;
929
930     wmw->dwStatus = MCI_MODE_STOP;
931
932     /* Let the potentially asynchronous commands support FAILURE notification. */
933     if (oldcb) mciDriverNotify(oldcb, wDevID,
934         dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
935
936     return dwRet;
937 }
938
939 /**************************************************************************
940  *                              WAVE_mciRecordCallback          [internal]
941  */
942 static  void    CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
943                                                 DWORD_PTR dwInstance,
944                                                 LPARAM dwParam1, LPARAM dwParam2)
945 {
946     WINE_MCIWAVE*       wmw = (WINE_MCIWAVE*)dwInstance;
947     LPWAVEHDR           lpWaveHdr;
948     LONG                count = 0;
949
950     switch (uMsg) {
951     case WIM_OPEN:
952     case WIM_CLOSE:
953         break;
954     case WIM_DATA:
955         lpWaveHdr = (LPWAVEHDR) dwParam1;
956
957         InterlockedIncrement(&wmw->dwEventCount);
958
959         count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);
960
961         lpWaveHdr->dwFlags &= ~WHDR_DONE;
962         if (count > 0)
963             wmw->dwPosition  += count;
964         /* else error reporting ?? */
965         if (wmw->dwStatus == MCI_MODE_RECORD)
966         {
967            /* Only queue up another buffer if we are recording.  We could receive this
968               message also when waveInReset() is called, since it notifies on all wave
969               buffers that are outstanding.  Queueing up more sometimes causes waveInClose
970               to fail. */
971            waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
972            TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
973         }
974
975         SetEvent(wmw->hEvent);
976         break;
977     default:
978         ERR("Unknown uMsg=%d\n", uMsg);
979     }
980 }
981
982 /******************************************************************
983  *                      WAVE_mciRecordWaitDone          [internal]
984  */
985 static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
986 {
987     for (;;) {
988         ResetEvent(wmw->hEvent);
989         if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
990             break;
991         }
992         InterlockedIncrement(&wmw->dwEventCount);
993
994         WaitForSingleObject(wmw->hEvent, INFINITE);
995     }
996 }
997
998 /**************************************************************************
999  *                              WAVE_mciRecord                  [internal]
1000  */
1001 static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
1002 {
1003     LPMCI_RECORD_PARMS  lpParms = (void*)pmt;
1004     DWORD               end;
1005     DWORD               dwRet = MMSYSERR_NOERROR;
1006     LONG                bufsize;
1007     LPWAVEHDR           waveHdr = NULL;
1008     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1009     HANDLE              oldcb;
1010
1011     TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1012
1013     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1014     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1015
1016     if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
1017         /* FIXME: parameters (start/end) in lpParams may not be used */
1018         return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
1019     }
1020
1021     /** This function will be called again by a thread when async is used.
1022      * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1023      * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
1024      */
1025     if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
1026         !((wmw->dwStatus == MCI_MODE_RECORD) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
1027         return MCIERR_INTERNAL;
1028     }
1029
1030     wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
1031     wmw->dwStatus = MCI_MODE_RECORD;
1032
1033     if (!(dwFlags & MCI_WAIT)) {
1034         return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags,
1035                                     (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1036     }
1037
1038     /* FIXME: we only re-create the RIFF structure from an existing file (if any)
1039      * we don't modify the wave part of an existing file (ie. we always erase an
1040      * existing content, we don't overwrite)
1041      */
1042     HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
1043     dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
1044     if (dwRet != 0) return dwRet;
1045
1046     /* new RIFF file, lpWaveFormat now valid */
1047     dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1048     if (dwRet != 0) return dwRet;
1049
1050     if (dwFlags & MCI_TO) {
1051         end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1052     } else end = 0xFFFFFFFF;
1053     if (dwFlags & MCI_FROM) {
1054         DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
1055         if (wmw->ckWaveData.cksize < position)  return MCIERR_OUTOFRANGE;
1056         /* Seek rounds down, so do we. */
1057         position /= wmw->lpWaveFormat->nBlockAlign;
1058         position *= wmw->lpWaveFormat->nBlockAlign;
1059         wmw->dwPosition = position;
1060     }
1061     if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
1062
1063     TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1064
1065     oldcb = InterlockedExchangePointer(&wmw->hCallback,
1066         (dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
1067     if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
1068     oldcb = NULL;
1069
1070 #define WAVE_ALIGN_ON_BLOCK(wmw,v) \
1071 ((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)
1072
1073     wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);
1074
1075     /* Go back to the beginning of the chunk plus the requested position */
1076     /* FIXME: I'm not sure this is correct, notably because some data linked to
1077      * the decompression state machine will not be correctly initialized.
1078      * Try it this way (other way would be to decompress from 0 up to dwPosition
1079      * and to start sending to hWave when dwPosition is reached).
1080      */
1081     mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
1082
1083     dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, wmw->wInput, wmw->lpWaveFormat,
1084                         (DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1085
1086     if (dwRet != MMSYSERR_NOERROR) {
1087         TRACE("Can't open low level audio device %d\n", dwRet);
1088         dwRet = MCIERR_DEVICE_OPEN;
1089         wmw->hWave = 0;
1090         goto cleanUp;
1091     }
1092
1093     /* make it so that 3 buffers per second are needed */
1094     bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);
1095
1096     waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
1097     waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
1098     waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
1099     waveHdr[0].dwUser         = waveHdr[1].dwUser         = 0L;
1100     waveHdr[0].dwLoops        = waveHdr[1].dwLoops        = 0L;
1101     waveHdr[0].dwFlags        = waveHdr[1].dwFlags        = 0L;
1102     waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
1103
1104     if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1105         waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1106         dwRet = MCIERR_INTERNAL;
1107         goto cleanUp;
1108     }
1109
1110     if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1111         waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
1112         dwRet = MCIERR_INTERNAL;
1113         goto cleanUp;
1114     }
1115
1116     wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1117     wmw->dwEventCount = 1L; /* for first buffer */
1118
1119     TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1120
1121     dwRet = waveInStart(wmw->hWave);
1122
1123     if (hEvent) SetEvent(hEvent);
1124
1125     while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1126         WAVE_mciRecordWaitDone(wmw);
1127     }
1128     /* Grab callback before another thread kicks in after we change dwStatus. */
1129     if (dwFlags & MCI_NOTIFY) {
1130         oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1131         dwFlags &= ~MCI_NOTIFY;
1132     }
1133     /* needed so that the callback above won't add again the buffers returned by the reset */
1134     wmw->dwStatus = MCI_MODE_STOP;
1135
1136     waveInReset(wmw->hWave);
1137
1138     waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
1139     waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));
1140
1141     dwRet = 0;
1142
1143 cleanUp:
1144     if (dwFlags & MCI_NOTIFY)
1145         oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
1146
1147     HeapFree(GetProcessHeap(), 0, waveHdr);
1148
1149     if (wmw->hWave) {
1150         waveInClose(wmw->hWave);
1151         wmw->hWave = 0;
1152     }
1153     CloseHandle(wmw->hEvent);
1154
1155     wmw->dwStatus = MCI_MODE_STOP;
1156
1157     if (oldcb) mciDriverNotify(oldcb, wDevID,
1158         dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1159
1160     return dwRet;
1161
1162 }
1163
1164 /**************************************************************************
1165  *                              WAVE_mciPause                   [internal]
1166  */
1167 static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1168 {
1169     DWORD               dwRet;
1170     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1171
1172     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1173
1174     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1175
1176     switch (wmw->dwStatus) {
1177     case MCI_MODE_PLAY:
1178         dwRet = waveOutPause(wmw->hWave);
1179         if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1180         else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
1181             ERR("waveOutPause error %d\n",dwRet);
1182             dwRet = MCIERR_INTERNAL;
1183         }
1184         break;
1185     case MCI_MODE_RECORD:
1186         dwRet = waveInStop(wmw->hWave);
1187         if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
1188         else {
1189             ERR("waveInStop error %d\n",dwRet);
1190             dwRet = MCIERR_INTERNAL;
1191         }
1192         break;
1193     case MCI_MODE_PAUSE:
1194         dwRet = MMSYSERR_NOERROR;
1195         break;
1196     default:
1197         dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1198     }
1199     if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1200         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1201     return dwRet;
1202 }
1203
1204 /**************************************************************************
1205  *                              WAVE_mciResume                  [internal]
1206  */
1207 static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1208 {
1209     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1210     DWORD               dwRet;
1211
1212     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1213
1214     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1215
1216     switch (wmw->dwStatus) {
1217     case MCI_MODE_PAUSE:
1218         /* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1219         if (wmw->fInput) {
1220             dwRet = waveInStart(wmw->hWave);
1221             if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
1222             else {
1223                 ERR("waveInStart error %d\n",dwRet);
1224                 dwRet = MCIERR_INTERNAL;
1225             }
1226         } else {
1227             dwRet = waveOutRestart(wmw->hWave);
1228             if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
1229             else {
1230                 ERR("waveOutRestart error %d\n",dwRet);
1231                 dwRet = MCIERR_INTERNAL;
1232             }
1233         }
1234         break;
1235     case MCI_MODE_PLAY:
1236     case MCI_MODE_RECORD:
1237         dwRet = MMSYSERR_NOERROR;
1238         break;
1239     default:
1240         dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1241     }
1242     if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
1243         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1244     return dwRet;
1245 }
1246
1247 /**************************************************************************
1248  *                              WAVE_mciSeek                    [internal]
1249  */
1250 static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1251 {
1252     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1253     DWORD               position, dwRet;
1254
1255     TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1256
1257     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1258     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1259
1260     position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
1261     if (!position)              return MCIERR_MISSING_PARAMETER;
1262     if (position&(position-1))  return MCIERR_FLAGS_NOT_COMPATIBLE;
1263
1264     /* Stop sends MCI_NOTIFY_ABORTED when needed */
1265     dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
1266     if (dwRet != MMSYSERR_NOERROR) return dwRet;
1267
1268     if (dwFlags & MCI_TO) {
1269         position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1270         if (position > wmw->ckWaveData.cksize)
1271             return MCIERR_OUTOFRANGE;
1272     } else if (dwFlags & MCI_SEEK_TO_START) {
1273         position = 0;
1274     } else {
1275         position = wmw->ckWaveData.cksize;
1276     }
1277     /* Seek rounds down, unless at end */
1278     if (position != wmw->ckWaveData.cksize) {
1279         position /= wmw->lpWaveFormat->nBlockAlign;
1280         position *= wmw->lpWaveFormat->nBlockAlign;
1281     }
1282     wmw->dwPosition = position;
1283     TRACE("Seeking to position=%u bytes\n", position);
1284
1285     if (dwFlags & MCI_NOTIFY)
1286         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1287
1288     return MMSYSERR_NOERROR;
1289 }
1290
1291 /**************************************************************************
1292  *                              WAVE_mciSet                     [internal]
1293  */
1294 static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_SET_PARMS lpParms)
1295 {
1296     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1297
1298     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1299
1300     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1301     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1302
1303     if (dwFlags & MCI_SET_TIME_FORMAT) {
1304         switch (lpParms->dwTimeFormat) {
1305         case MCI_FORMAT_MILLISECONDS:
1306             TRACE("MCI_FORMAT_MILLISECONDS !\n");
1307             wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
1308             break;
1309         case MCI_FORMAT_BYTES:
1310             TRACE("MCI_FORMAT_BYTES !\n");
1311             wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
1312             break;
1313         case MCI_FORMAT_SAMPLES:
1314             TRACE("MCI_FORMAT_SAMPLES !\n");
1315             wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
1316             break;
1317         default:
1318             WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1319             return MCIERR_BAD_TIME_FORMAT;
1320         }
1321     }
1322     if (dwFlags & MCI_SET_VIDEO) {
1323         TRACE("No support for video !\n");
1324         return MCIERR_UNSUPPORTED_FUNCTION;
1325     }
1326     if (dwFlags & MCI_SET_DOOR_OPEN) {
1327         TRACE("No support for door open !\n");
1328         return MCIERR_UNSUPPORTED_FUNCTION;
1329     }
1330     if (dwFlags & MCI_SET_DOOR_CLOSED) {
1331         TRACE("No support for door close !\n");
1332         return MCIERR_UNSUPPORTED_FUNCTION;
1333     }
1334     if (dwFlags & MCI_SET_AUDIO) {
1335         if (dwFlags & MCI_SET_ON) {
1336             TRACE("MCI_SET_ON audio !\n");
1337         } else if (dwFlags & MCI_SET_OFF) {
1338             TRACE("MCI_SET_OFF audio !\n");
1339         } else {
1340             WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1341             return MCIERR_BAD_INTEGER;
1342         }
1343
1344         switch (lpParms->dwAudio)
1345         {
1346         case MCI_SET_AUDIO_ALL:         TRACE("MCI_SET_AUDIO_ALL !\n"); break;
1347         case MCI_SET_AUDIO_LEFT:        TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
1348         case MCI_SET_AUDIO_RIGHT:       TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1349         default:                        WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1350         }
1351     }
1352     if (dwFlags & MCI_WAVE_INPUT) {
1353         TRACE("MCI_WAVE_INPUT = %d\n", lpParms->wInput);
1354         if (lpParms->wInput >= waveInGetNumDevs())
1355             return MCIERR_OUTOFRANGE;
1356         if (wmw->wInput != (WORD)lpParms->wInput)
1357             WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1358         wmw->wInput = lpParms->wInput;
1359     }
1360     if (dwFlags & MCI_WAVE_OUTPUT) {
1361         TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms->wOutput);
1362         if (lpParms->wOutput >= waveOutGetNumDevs())
1363             return MCIERR_OUTOFRANGE;
1364         if (wmw->wOutput != (WORD)lpParms->wOutput)
1365             WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1366         wmw->wOutput = lpParms->wOutput;
1367     }
1368     if (dwFlags & MCI_WAVE_SET_ANYINPUT) {
1369         TRACE("MCI_WAVE_SET_ANYINPUT\n");
1370         if (wmw->wInput != (WORD)lpParms->wInput)
1371             WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1372         wmw->wInput = WAVE_MAPPER;
1373     }
1374     if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) {
1375         TRACE("MCI_WAVE_SET_ANYOUTPUT\n");
1376         if (wmw->wOutput != (WORD)lpParms->wOutput)
1377             WAVE_mciStop(wDevID, MCI_WAIT, NULL);
1378         wmw->wOutput = WAVE_MAPPER;
1379     }
1380     /* Set wave format parameters is refused after Open or Record.*/
1381     if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1382         TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms->wFormatTag);
1383         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1384         if (lpParms->wFormatTag != WAVE_FORMAT_PCM)
1385             return MCIERR_OUTOFRANGE;
1386     }
1387     if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1388         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1389         wmw->wfxRef.nAvgBytesPerSec = lpParms->nAvgBytesPerSec;
1390         TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1391     }
1392     if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1393         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1394         wmw->wfxRef.wBitsPerSample = lpParms->wBitsPerSample;
1395         TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
1396     }
1397     if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1398         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1399         wmw->wfxRef.nBlockAlign = lpParms->nBlockAlign;
1400         TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
1401     }
1402     if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1403         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1404         wmw->wfxRef.nChannels = lpParms->nChannels;
1405         TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
1406     }
1407     if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1408         if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1409         wmw->wfxRef.nSamplesPerSec = lpParms->nSamplesPerSec;
1410         TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1411     }
1412     if (dwFlags & MCI_NOTIFY)
1413         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1414     return 0;
1415 }
1416
1417 /**************************************************************************
1418  *                              WAVE_mciSave            [internal]
1419  */
1420 static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1421 {
1422     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1423     DWORD               ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1424
1425     TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1426     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1427     if (wmw     == NULL)        return MCIERR_INVALID_DEVICE_ID;
1428
1429     if (dwFlags & MCI_WAIT)
1430     {
1431         FIXME("MCI_WAIT not implemented\n");
1432     }
1433     WAVE_mciStop(wDevID, 0, NULL);
1434
1435     ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
1436     ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);
1437
1438     ret = mmioClose(wmw->hFile, 0);
1439     wmw->hFile = 0;
1440
1441     /*
1442       If the destination file already exists, it has to be overwritten.  (Behaviour
1443       verified in Windows (2000)).  If it doesn't overwrite, it is breaking one of
1444       my applications.  We are making use of mmioRename, which WILL NOT overwrite
1445       the destination file (which is what Windows does, also verified in Win2K)
1446       So, lets delete the destination file before calling mmioRename.  If the
1447       destination file DOESN'T exist, the delete will fail silently.  Let's also be
1448       careful not to lose our previous error code.
1449     */
1450     tmpRet = GetLastError();
1451     DeleteFileW (lpParms->lpfilename);
1452     SetLastError(tmpRet);
1453
1454     /* FIXME: Open file.wav; Save; must not rename the original file.
1455      * Nor must Save a.wav; Save b.wav rename a. */
1456     if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
1457         ret = MMSYSERR_NOERROR;
1458     }
1459
1460     if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1461         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1462
1463     if (ret == MMSYSERR_NOERROR)
1464         ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);
1465
1466     return ret;
1467 }
1468
1469 /**************************************************************************
1470  *                              WAVE_mciStatus          [internal]
1471  */
1472 static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1473 {
1474     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1475     DWORD               ret = 0;
1476
1477     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1478     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1479     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1480     if (!(dwFlags & MCI_STATUS_ITEM))   return MCIERR_MISSING_PARAMETER;
1481
1482     if (dwFlags & MCI_STATUS_ITEM) {
1483         switch (lpParms->dwItem) {
1484         case MCI_STATUS_CURRENT_TRACK:
1485             lpParms->dwReturn = 1;
1486             TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1487             break;
1488         case MCI_STATUS_LENGTH:
1489             if (!wmw->hFile) {
1490                 lpParms->dwReturn = 0;
1491                 return MCIERR_UNSUPPORTED_FUNCTION;
1492             }
1493             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1494             lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize);
1495             TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1496             break;
1497         case MCI_STATUS_MODE:
1498             TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
1499             lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
1500             ret = MCI_RESOURCE_RETURNED;
1501             break;
1502         case MCI_STATUS_MEDIA_PRESENT:
1503             TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1504             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1505             ret = MCI_RESOURCE_RETURNED;
1506             break;
1507         case MCI_STATUS_NUMBER_OF_TRACKS:
1508             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1509             lpParms->dwReturn = 1;
1510             TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1511             break;
1512         case MCI_STATUS_POSITION:
1513             if (!wmw->hFile) {
1514                 lpParms->dwReturn = 0;
1515                 return MCIERR_UNSUPPORTED_FUNCTION;
1516             }
1517             /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1518             lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1519                                                              (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
1520             TRACE("MCI_STATUS_POSITION %s => %lu\n",
1521                   (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
1522             break;
1523         case MCI_STATUS_READY:
1524             lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
1525                 MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1526             TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
1527             ret = MCI_RESOURCE_RETURNED;
1528             break;
1529         case MCI_STATUS_TIME_FORMAT:
1530             lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1531             TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1532             ret = MCI_RESOURCE_RETURNED;
1533             break;
1534         case MCI_WAVE_INPUT:
1535             if (wmw->wInput != (WORD)WAVE_MAPPER)
1536                 lpParms->dwReturn = wmw->wInput;
1537             else {
1538                 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1539                 ret = MCI_RESOURCE_RETURNED;
1540             }
1541             TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw->wInput);
1542             break;
1543         case MCI_WAVE_OUTPUT:
1544             if (wmw->wOutput != (WORD)WAVE_MAPPER)
1545                 lpParms->dwReturn = wmw->wOutput;
1546             else {
1547                 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
1548                 ret = MCI_RESOURCE_RETURNED;
1549             }
1550             TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw->wOutput);
1551             break;
1552         /* It is always ok to query wave format parameters,
1553          * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1554         case MCI_WAVE_STATUS_FORMATTAG:
1555             if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
1556                 lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1557             else {
1558                 lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_FORMAT_PCM, WAVE_FORMAT_PCM_S);
1559                 ret = MCI_RESOURCE_RETURNED;
1560             }
1561             TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1562             break;
1563         case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1564             lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1565             TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1566             break;
1567         case MCI_WAVE_STATUS_BITSPERSAMPLE:
1568             lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1569             TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1570             break;
1571         case MCI_WAVE_STATUS_BLOCKALIGN:
1572             lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1573             TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1574             break;
1575         case MCI_WAVE_STATUS_CHANNELS:
1576             lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1577             TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1578             break;
1579         case MCI_WAVE_STATUS_SAMPLESPERSEC:
1580             lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1581             TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1582             break;
1583         case MCI_WAVE_STATUS_LEVEL:
1584             TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1585             lpParms->dwReturn = 0xAAAA5555;
1586             break;
1587         default:
1588             WARN("unknown command %08X !\n", lpParms->dwItem);
1589             return MCIERR_UNSUPPORTED_FUNCTION;
1590         }
1591     }
1592     if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1593         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1594     return ret;
1595 }
1596
1597 /**************************************************************************
1598  *                              WAVE_mciGetDevCaps              [internal]
1599  */
1600 static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1601                                 LPMCI_GETDEVCAPS_PARMS lpParms)
1602 {
1603     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1604     DWORD               ret = 0;
1605
1606     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1607
1608     if (lpParms == NULL)        return MCIERR_NULL_PARAMETER_BLOCK;
1609     if (wmw == NULL)            return MCIERR_INVALID_DEVICE_ID;
1610
1611     if (dwFlags & MCI_GETDEVCAPS_ITEM) {
1612         switch(lpParms->dwItem) {
1613         case MCI_GETDEVCAPS_DEVICE_TYPE:
1614             lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
1615             ret = MCI_RESOURCE_RETURNED;
1616             break;
1617         case MCI_GETDEVCAPS_HAS_AUDIO:
1618             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1619             ret = MCI_RESOURCE_RETURNED;
1620             break;
1621         case MCI_GETDEVCAPS_HAS_VIDEO:
1622             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1623             ret = MCI_RESOURCE_RETURNED;
1624             break;
1625         case MCI_GETDEVCAPS_USES_FILES:
1626             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1627             ret = MCI_RESOURCE_RETURNED;
1628             break;
1629         case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1630             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1631             ret = MCI_RESOURCE_RETURNED;
1632             break;
1633         case MCI_GETDEVCAPS_CAN_RECORD:
1634             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1635             ret = MCI_RESOURCE_RETURNED;
1636             break;
1637         case MCI_GETDEVCAPS_CAN_EJECT:
1638             lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
1639             ret = MCI_RESOURCE_RETURNED;
1640             break;
1641         case MCI_GETDEVCAPS_CAN_PLAY:
1642             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1643             ret = MCI_RESOURCE_RETURNED;
1644             break;
1645         case MCI_GETDEVCAPS_CAN_SAVE:
1646             lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
1647             ret = MCI_RESOURCE_RETURNED;
1648             break;
1649         case MCI_WAVE_GETDEVCAPS_INPUTS:
1650             lpParms->dwReturn = waveInGetNumDevs();
1651             break;
1652         case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1653             lpParms->dwReturn = waveOutGetNumDevs();
1654             break;
1655         default:
1656             FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1657             return MCIERR_UNRECOGNIZED_COMMAND;
1658         }
1659     } else {
1660         WARN("No GetDevCaps-Item !\n");
1661         return MCIERR_UNRECOGNIZED_COMMAND;
1662     }
1663     if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
1664         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1665     return ret;
1666 }
1667
1668 /**************************************************************************
1669  *                              WAVE_mciInfo                    [internal]
1670  */
1671 static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1672 {
1673     DWORD               ret = 0;
1674     LPCWSTR             str = 0;
1675     WINE_MCIWAVE*       wmw = WAVE_mciGetOpenDev(wDevID);
1676
1677     TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1678
1679     if (!lpParms || !lpParms->lpstrReturn)
1680         return MCIERR_NULL_PARAMETER_BLOCK;
1681
1682     TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);
1683
1684     if (wmw == NULL) {
1685         ret = MCIERR_INVALID_DEVICE_ID;
1686     } else {
1687         static const WCHAR wszAudio  [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
1688         static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
1689         static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};
1690
1691         switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1692         case MCI_INFO_PRODUCT: str = wszAudio; break;
1693         case MCI_INFO_FILE:    str = wmw->lpFileName; break;
1694         case MCI_WAVE_INPUT:   str = wszWaveIn; break;
1695         case MCI_WAVE_OUTPUT:  str = wszWaveOut; break;
1696         default:
1697             WARN("Don't know this info command (%u)\n", dwFlags);
1698             ret = MCIERR_UNRECOGNIZED_KEYWORD;
1699         }
1700     }
1701     if (!ret) {
1702         if (lpParms->dwRetSize) {
1703             WCHAR zero = 0;
1704             /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
1705              *        to the number of characters written, excluding \0. */
1706             lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize);
1707         } else ret = MCIERR_PARAM_OVERFLOW;
1708     }
1709     if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
1710         WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1711     return ret;
1712 }
1713
1714 /**************************************************************************
1715  *                              DriverProc (MCIWAVE.@)
1716  */
1717 LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1718                                     LPARAM dwParam1, LPARAM dwParam2)
1719 {
1720     TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1721           dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1722
1723     switch (wMsg) {
1724     case DRV_LOAD:              return 1;
1725     case DRV_FREE:              return 1;
1726     case DRV_OPEN:              return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1727     case DRV_CLOSE:             return WAVE_drvClose(dwDevID);
1728     case DRV_ENABLE:            return 1;
1729     case DRV_DISABLE:           return 1;
1730     case DRV_QUERYCONFIGURE:    return 1;
1731     case DRV_CONFIGURE:         MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK); return 1;
1732     case DRV_INSTALL:           return DRVCNF_RESTART;
1733     case DRV_REMOVE:            return DRVCNF_RESTART;
1734     }
1735
1736     if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;
1737
1738     switch (wMsg) {
1739     case MCI_OPEN_DRIVER:       return WAVE_mciOpen      (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW)  dwParam2);
1740     case MCI_CLOSE_DRIVER:      return WAVE_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1741     case MCI_CUE:               return WAVE_mciCue       (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1742     case MCI_PLAY:              return WAVE_mciPlay      (dwDevID, dwParam1, dwParam2, NULL);
1743     case MCI_RECORD:            return WAVE_mciRecord    (dwDevID, dwParam1, dwParam2, NULL);
1744     case MCI_STOP:              return WAVE_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1745     case MCI_SET:               return WAVE_mciSet       (dwDevID, dwParam1, (LPMCI_WAVE_SET_PARMS)    dwParam2);
1746     case MCI_PAUSE:             return WAVE_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1747     case MCI_RESUME:            return WAVE_mciResume    (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1748     case MCI_STATUS:            return WAVE_mciStatus    (dwDevID, dwParam1, (LPMCI_STATUS_PARMS)      dwParam2);
1749     case MCI_GETDEVCAPS:        return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)  dwParam2);
1750     case MCI_INFO:              return WAVE_mciInfo      (dwDevID, dwParam1, (LPMCI_INFO_PARMSW)       dwParam2);
1751     case MCI_SEEK:              return WAVE_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)        dwParam2);
1752     case MCI_SAVE:              return WAVE_mciSave      (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW)       dwParam2);
1753         /* commands that should be supported */
1754     case MCI_LOAD:
1755     case MCI_FREEZE:
1756     case MCI_PUT:
1757     case MCI_REALIZE:
1758     case MCI_UNFREEZE:
1759     case MCI_UPDATE:
1760     case MCI_WHERE:
1761     case MCI_STEP:
1762     case MCI_SPIN:
1763     case MCI_ESCAPE:
1764     case MCI_COPY:
1765     case MCI_CUT:
1766     case MCI_DELETE:
1767     case MCI_PASTE:
1768         FIXME("Unsupported command [%u]\n", wMsg);
1769         break;
1770     case MCI_WINDOW:
1771         TRACE("Unsupported command [%u]\n", wMsg);
1772         break;
1773         /* option which can be silenced */
1774     case MCI_CONFIGURE:
1775         return 0;
1776     case MCI_OPEN:
1777     case MCI_CLOSE:
1778         ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1779         break;
1780     default:
1781         FIXME("is probably wrong msg [%u]\n", wMsg);
1782         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1783     }
1784     return MCIERR_UNRECOGNIZED_COMMAND;
1785 }