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