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