mciwave.c 62 KB
Newer Older
1
/*
2
 * Wine Driver for MCI wave forms
3
 *
4
 * Copyright 	1994 Martin Ayotte
Eric Pouech's avatar
Eric Pouech committed
5
 *		1999,2000,2005 Eric Pouech
6
 *              2000 Francois Jacques
7
 *		2009 Jörg Höhle
8 9 10 11 12 13 14 15 16 17 18 19 20
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 26
#include <stdarg.h>

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 41
    UINT			wDevID;
    HANDLE			hWave;
42
    int				nUseCount;	/* Incremented for each shared open */
Eric Pouech's avatar
Eric Pouech committed
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 51
    WORD			wInput;		/* wave input device */
    WORD			wOutput;	/* wave output device */
52
    volatile WORD		dwStatus;	/* one from MCI_MODE_xxxx */
53
    DWORD			dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
54
    DWORD			dwPosition;	/* position in bytes in chunk */
55
    HANDLE			hEvent;		/* for synchronization */
56
    LONG			dwEventCount;	/* for synchronization */
57 58
    MMCKINFO                   	ckMainRIFF;     /* main RIFF chunk */
    MMCKINFO                   	ckWaveData;     /* data chunk */
59 60
} WINE_MCIWAVE;

61 62 63 64 65
/* ===================================================================
 * ===================================================================
 * FIXME: should be using the new mmThreadXXXX functions from WINMM
 * instead of those
 * it would require to add a wine internal flag to mmThreadCreate
66
 * in order to pass a 32 bit function instead of a 16 bit one
67 68 69
 * ===================================================================
 * =================================================================== */

70
typedef DWORD (*async_cmd)(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE evt);
71

72
struct SCA {
73
    async_cmd   cmd;
74
    HANDLE      evt;
75
    UINT 	wDevID;
76 77
    DWORD_PTR   dwParam1;
    DWORD_PTR   dwParam2;
78 79 80 81 82 83 84 85 86 87
};

/**************************************************************************
 * 				MCI_SCAStarter			[internal]
 */
static DWORD CALLBACK	MCI_SCAStarter(LPVOID arg)
{
    struct SCA*	sca = (struct SCA*)arg;
    DWORD		ret;

88 89
    TRACE("In thread before async command (%08x,%08lx,%08lx)\n",
	  sca->wDevID, sca->dwParam1, sca->dwParam2);
90
    ret = sca->cmd(sca->wDevID, sca->dwParam1 | MCI_WAIT, sca->dwParam2, sca->evt);
91 92
    TRACE("In thread after async command (%08x,%08lx,%08lx)\n",
	  sca->wDevID, sca->dwParam1, sca->dwParam2);
93 94 95 96 97 98 99
    HeapFree(GetProcessHeap(), 0, sca);
    return ret;
}

/**************************************************************************
 * 				MCI_SendCommandAsync		[internal]
 */
100
static	DWORD MCI_SendCommandAsync(UINT wDevID, async_cmd cmd, DWORD_PTR dwParam1,
101
				   DWORD_PTR dwParam2, UINT size)
102
{
103
    HANDLE handles[2];
104
    struct SCA*	sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
105 106 107 108 109

    if (sca == 0)
	return MCIERR_OUT_OF_MEMORY;

    sca->wDevID   = wDevID;
110
    sca->cmd      = cmd;
111
    sca->dwParam1 = dwParam1;
112

113
    if (size && dwParam2) {
114
	sca->dwParam2 = (DWORD_PTR)sca + sizeof(struct SCA);
115 116
	/* copy structure passed by program in dwParam2 to be sure
	 * we can still use it whatever the program does
117 118 119 120 121 122
	 */
	memcpy((LPVOID)sca->dwParam2, (LPVOID)dwParam2, size);
    } else {
	sca->dwParam2 = dwParam2;
    }

123 124
    if ((sca->evt = handles[1] = CreateEventW(NULL, FALSE, FALSE, NULL)) == NULL ||
        (handles[0] = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
125
	WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
126 127
        if (handles[1]) CloseHandle(handles[1]);
        sca->evt = NULL;
128 129
	return MCI_SCAStarter(&sca);
    }
130 131 132 133 134 135 136 137 138

    SetThreadPriority(handles[0], THREAD_PRIORITY_TIME_CRITICAL);
    /* wait until either:
     * - the thread has finished (handles[0], likely an error)
     * - init phase of async command is done (handles[1])
     */
    WaitForMultipleObjects(2, handles, FALSE, INFINITE);
    CloseHandle(handles[0]);
    CloseHandle(handles[1]);
139 140 141
    return 0;
}

142
/*======================================================================*
143
 *                  	    MCI WAVE implementation			*
144 145
 *======================================================================*/

146
static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
147

148
/**************************************************************************
149
 * 				MCIWAVE_drvOpen			[internal]
150
 */
151
static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
152
{
153 154 155 156 157
    WINE_MCIWAVE*	wmw;

    if (modp == NULL) return 0xFFFFFFFF;

    wmw = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_MCIWAVE));
158

159 160 161 162
    if (!wmw)
	return 0;

    wmw->wDevID = modp->wDeviceID;
163
    mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
164
    modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
165
    modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
166

167
    wmw->wfxRef.wFormatTag     	= WAVE_FORMAT_PCM;
168 169 170
    wmw->wfxRef.nChannels       = 1;      /* MONO */
    wmw->wfxRef.nSamplesPerSec  = 11025;
    wmw->wfxRef.nAvgBytesPerSec = 11025;
171
    wmw->wfxRef.nBlockAlign     = 1;
172 173 174
    wmw->wfxRef.wBitsPerSample  = 8;
    wmw->wfxRef.cbSize          = 0;      /* don't care */

175
    return modp->wDeviceID;
176 177 178
}

/**************************************************************************
179
 * 				MCIWAVE_drvClose		[internal]
180
 */
181
static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
182
{
183
    WINE_MCIWAVE*  wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
184

185
    if (wmw) {
186
	HeapFree(GetProcessHeap(), 0, wmw);
187
	mciSetDriverData(dwDevID, 0);
188 189
	return 1;
    }
190
    return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
191 192
}

193
/**************************************************************************
194
 * 				WAVE_mciGetOpenDev		[internal]
195
 */
196
static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
197
{
198
    WINE_MCIWAVE*	wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
199

200
    if (wmw == NULL || wmw->nUseCount == 0) {
201
	WARN("Invalid wDevID=%u\n", wDevID);
202 203
	return 0;
    }
204
    return wmw;
205 206
}

207 208 209 210 211 212 213 214 215
/**************************************************************************
 *				WAVE_mciNotify			[internal]
 *
 * Notifications in MCI work like a 1-element queue.
 * Each new notification request supersedes the previous one.
 * This affects Play and Record; other commands are immediate.
 */
static void WAVE_mciNotify(DWORD_PTR hWndCallBack, WINE_MCIWAVE* wmw, UINT wStatus)
{
216 217 218
    /* We simply save one parameter by not passing the wDevID local
     * to the command.  They are the same (via mciGetDriverData).
     */
219
    MCIDEVICEID wDevID = wmw->wNotifyDeviceID;
220 221 222 223 224
    HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
    if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_SUPERSEDED);
    mciDriverNotify(HWND_32(LOWORD(hWndCallBack)), wDevID, wStatus);
}

225
/**************************************************************************
226
 * 				WAVE_ConvertByteToTimeFormat	[internal]
227
 */
228
static	DWORD 	WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val)
229
{
230
    DWORD	   ret = 0;
231

232 233
    switch (wmw->dwMciTimeFormat) {
    case MCI_FORMAT_MILLISECONDS:
234
	ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
235 236 237 238
	break;
    case MCI_FORMAT_BYTES:
	ret = val;
	break;
239 240
    case MCI_FORMAT_SAMPLES:
	ret = MulDiv(val,wmw->lpWaveFormat->nSamplesPerSec,wmw->lpWaveFormat->nAvgBytesPerSec);
241 242
	break;
    default:
243
	WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
244
    }
245
    TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
246 247 248
    return ret;
}

249
/**************************************************************************
250
 * 				WAVE_ConvertTimeFormatToByte	[internal]
251
 */
252 253 254
static	DWORD 	WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
{
    DWORD	ret = 0;
255

256 257
    switch (wmw->dwMciTimeFormat) {
    case MCI_FORMAT_MILLISECONDS:
258
	ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,1000);
259 260 261
	if (ret > wmw->ckWaveData.cksize &&
	    val == WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize))
	    ret = wmw->ckWaveData.cksize;
262 263 264 265
	break;
    case MCI_FORMAT_BYTES:
	ret = val;
	break;
266 267
    case MCI_FORMAT_SAMPLES:
	ret = MulDiv(val,wmw->lpWaveFormat->nAvgBytesPerSec,wmw->lpWaveFormat->nSamplesPerSec);
268 269
	break;
    default:
270
	WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
271
    }
272
    TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
273 274 275
    return ret;
}

276 277 278
/**************************************************************************
 * 			WAVE_mciReadFmt	                        [internal]
 */
279
static	DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
280 281
{
    MMCKINFO	mmckInfo;
282
    LONG	r;
283
    LPWAVEFORMATEX pwfx;
284

285 286 287
    mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
    if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
	return MCIERR_INVALID_FILE;
288
    TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
289
	  (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
290

291 292
    pwfx = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
    if (!pwfx) return MCIERR_OUT_OF_MEMORY;
293

294 295 296 297 298 299 300 301 302 303 304
    r = mmioRead(wmw->hFile, (HPSTR)pwfx, mmckInfo.cksize);
    if (r < sizeof(PCMWAVEFORMAT)) {
	HeapFree(GetProcessHeap(), 0, pwfx);
	return MCIERR_INVALID_FILE;
    }
    TRACE("wFormatTag=%04X !\n",   pwfx->wFormatTag);
    TRACE("nChannels=%d\n",        pwfx->nChannels);
    TRACE("nSamplesPerSec=%d\n",   pwfx->nSamplesPerSec);
    TRACE("nAvgBytesPerSec=%d\n",  pwfx->nAvgBytesPerSec);
    TRACE("nBlockAlign=%d\n",      pwfx->nBlockAlign);
    TRACE("wBitsPerSample=%u !\n", pwfx->wBitsPerSample);
305
    if (r >= sizeof(WAVEFORMATEX))
306 307 308 309 310 311 312
	TRACE("cbSize=%u !\n",     pwfx->cbSize);
    if ((pwfx->wFormatTag != WAVE_FORMAT_PCM)
	&& (r < sizeof(WAVEFORMATEX) || (r < sizeof(WAVEFORMATEX) + pwfx->cbSize))) {
	HeapFree(GetProcessHeap(), 0, pwfx);
	return MCIERR_INVALID_FILE;
    }
    wmw->lpWaveFormat = pwfx;
313

314
    mmioAscend(wmw->hFile, &mmckInfo, 0);
315 316
    wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
    if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
317
	TRACE("can't find data chunk\n");
318
	return MCIERR_INVALID_FILE;
319
    }
320
    TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
321
	  (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
322 323 324
    return 0;
}

Eric Pouech's avatar
Eric Pouech committed
325
/**************************************************************************
326
 * 			WAVE_mciDefaultFmt			[internal]
327 328 329 330
 *
 * wmw->lpWaveFormat points to the default wave format at wmw->wfxRef
 * until either Open File or Record.  It becomes immutable afterwards,
 * i.e. Set wave format or channels etc. is subsequently refused.
Eric Pouech's avatar
Eric Pouech committed
331
 */
332
static void WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
Eric Pouech's avatar
Eric Pouech committed
333
{
334
    wmw->lpWaveFormat = &wmw->wfxRef;
Eric Pouech's avatar
Eric Pouech committed
335 336
    wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
    wmw->lpWaveFormat->nChannels = 1;
337 338
    wmw->lpWaveFormat->nSamplesPerSec = 11025;
    wmw->lpWaveFormat->nAvgBytesPerSec = 11025;
Eric Pouech's avatar
Eric Pouech committed
339 340
    wmw->lpWaveFormat->nBlockAlign = 1;
    wmw->lpWaveFormat->wBitsPerSample = 8;
341
    wmw->lpWaveFormat->cbSize = 0;
Eric Pouech's avatar
Eric Pouech committed
342 343
}

344 345 346 347 348 349
/**************************************************************************
 * 			WAVE_mciCreateRIFFSkeleton              [internal]
 */
static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
{
   MMCKINFO     ckWaveFormat;
Eric Pouech's avatar
Eric Pouech committed
350 351
   LPMMCKINFO   lpckRIFF     = &(wmw->ckMainRIFF);
   LPMMCKINFO   lpckWaveData = &(wmw->ckWaveData);
352 353 354 355 356

   lpckRIFF->ckid    = FOURCC_RIFF;
   lpckRIFF->fccType = mmioFOURCC('W', 'A', 'V', 'E');
   lpckRIFF->cksize  = 0;

Eric Pouech's avatar
Eric Pouech committed
357
   if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
358 359 360 361
	goto err;

   ckWaveFormat.fccType = 0;
   ckWaveFormat.ckid    = mmioFOURCC('f', 'm', 't', ' ');
Eric Pouech's avatar
Eric Pouech committed
362
   ckWaveFormat.cksize  = sizeof(PCMWAVEFORMAT);
363

364
   /* Set wave format accepts PCM only, however open an
365 366
    * existing ADPCM file, record into it and the MCI will
    * happily save back in that format. */
367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
   if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
	if (wmw->lpWaveFormat->nBlockAlign !=
	    wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
	    WORD size = wmw->lpWaveFormat->nChannels *
		wmw->lpWaveFormat->wBitsPerSample/8;
	    WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
		wmw->lpWaveFormat->nBlockAlign, size);
	    wmw->lpWaveFormat->nBlockAlign = size;
	}
	if (wmw->lpWaveFormat->nAvgBytesPerSec !=
	    wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
	    DWORD speed = wmw->lpWaveFormat->nSamplesPerSec *
		wmw->lpWaveFormat->nBlockAlign;
	    WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
		wmw->lpWaveFormat->nAvgBytesPerSec, speed);
	    wmw->lpWaveFormat->nAvgBytesPerSec = speed;
	}
384
   }
385 386 387
   if (wmw->lpWaveFormat == &wmw->wfxRef) {
	LPWAVEFORMATEX pwfx = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WAVEFORMATEX));
	if (!pwfx) return MCIERR_OUT_OF_MEMORY;
388 389
	/* Set wave format accepts PCM only so the size is known. */
	assert(wmw->wfxRef.wFormatTag == WAVE_FORMAT_PCM);
390 391 392
	*pwfx = wmw->wfxRef;
	wmw->lpWaveFormat = pwfx;
   }
Eric Pouech's avatar
Eric Pouech committed
393 394

   if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, &ckWaveFormat, 0))
395 396
	goto err;

397 398
   if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, (WAVE_FORMAT_PCM==wmw->lpWaveFormat->wFormatTag)
	? sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX)+wmw->lpWaveFormat->cbSize))
399
	goto err;
400

Eric Pouech's avatar
Eric Pouech committed
401
   if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
402 403 404 405 406
	goto err;

   lpckWaveData->cksize  = 0;
   lpckWaveData->fccType = 0;
   lpckWaveData->ckid    = mmioFOURCC('d', 'a', 't', 'a');
407

408
   /* create data chunk */
Eric Pouech's avatar
Eric Pouech committed
409
   if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
410 411 412 413
	goto err;

   return 0;

414
err:
415
   /* mciClose takes care of wmw->lpWaveFormat. */
416 417 418
   return MCIERR_INVALID_FILE;
}

Eric Pouech's avatar
Eric Pouech committed
419 420 421 422 423 424 425 426 427 428 429
static DWORD create_tmp_file(HMMIO* hFile, LPWSTR* pszTmpFileName)
{
    WCHAR       szTmpPath[MAX_PATH];
    WCHAR       szPrefix[4];
    DWORD       dwRet = MMSYSERR_NOERROR;

    szPrefix[0] = 'M';
    szPrefix[1] = 'C';
    szPrefix[2] = 'I';
    szPrefix[3] = '\0';

430
    if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
Eric Pouech's avatar
Eric Pouech committed
431
        WARN("can't retrieve temp path!\n");
432
        *pszTmpFileName = NULL;
Eric Pouech's avatar
Eric Pouech committed
433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461
        return MCIERR_FILE_NOT_FOUND;
    }

    *pszTmpFileName = HeapAlloc(GetProcessHeap(),
                                HEAP_ZERO_MEMORY,
                                MAX_PATH * sizeof(WCHAR));
    if (!GetTempFileNameW(szTmpPath, szPrefix, 0, *pszTmpFileName)) {
        WARN("can't retrieve temp file name!\n");
        HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
        return MCIERR_FILE_NOT_FOUND;
    }

    TRACE("%s!\n", debugstr_w(*pszTmpFileName));

    if (*pszTmpFileName && (strlenW(*pszTmpFileName) > 0)) {

        *hFile = mmioOpenW(*pszTmpFileName, NULL,
                           MMIO_ALLOCBUF | MMIO_READWRITE | MMIO_CREATE);

        if (*hFile == 0) {
            WARN("can't create file=%s!\n", debugstr_w(*pszTmpFileName));
            /* temporary file could not be created. clean filename. */
            HeapFree(GetProcessHeap(), 0, *pszTmpFileName);
            dwRet = MCIERR_FILE_NOT_FOUND;
        }
    }
    return dwRet;
}

462
static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, LPCWSTR filename)
Eric Pouech's avatar
Eric Pouech committed
463 464
{
    LRESULT dwRet = MMSYSERR_NOERROR;
465
    LPWSTR fn;
Eric Pouech's avatar
Eric Pouech committed
466 467 468 469

    fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
    if (!fn) return MCIERR_OUT_OF_MEMORY;
    strcpyW(fn, filename);
470
    HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
471
    wmw->lpFileName = fn;
Eric Pouech's avatar
Eric Pouech committed
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487

    if (strlenW(filename) > 0) {
        /* FIXME : what should be done if wmw->hFile is already != 0, or the driver is playin' */
        TRACE("MCI_OPEN_ELEMENT %s!\n", debugstr_w(filename));

        wmw->hFile = mmioOpenW((LPWSTR)filename, NULL,
                               MMIO_ALLOCBUF | MMIO_DENYWRITE | MMIO_READ);

        if (wmw->hFile == 0) {
            WARN("can't find file=%s!\n", debugstr_w(filename));
            dwRet = MCIERR_FILE_NOT_FOUND;
        }
        else
        {
            LPMMCKINFO          lpckMainRIFF = &wmw->ckMainRIFF;

Sven Baars's avatar
Sven Baars committed
488
            /* make sure we're at the beginning of the file */
Eric Pouech's avatar
Eric Pouech committed
489 490 491 492 493 494
            mmioSeek(wmw->hFile, 0, SEEK_SET);

            /* first reading of this file. read the waveformat chunk */
            if (mmioDescend(wmw->hFile, lpckMainRIFF, NULL, 0) != 0) {
                dwRet = MCIERR_INVALID_FILE;
            } else {
495
                TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
Eric Pouech's avatar
Eric Pouech committed
496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511
                      (LPSTR)&(lpckMainRIFF->ckid),
                      (LPSTR) &(lpckMainRIFF->fccType),
                      (lpckMainRIFF->cksize));

                if ((lpckMainRIFF->ckid != FOURCC_RIFF) ||
                    lpckMainRIFF->fccType != mmioFOURCC('W', 'A', 'V', 'E')) {
                    dwRet = MCIERR_INVALID_FILE;
                } else {
                    dwRet = WAVE_mciReadFmt(wmw, lpckMainRIFF);
                }
            }
        }
    }
    return dwRet;
}

512 513 514
/**************************************************************************
 * 			WAVE_mciOpen	                        [internal]
 */
515
static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
516
{
517
    DWORD		dwRet = 0;
518
    WINE_MCIWAVE*	wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
519

520
    TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
521 522 523
    if (lpOpenParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
    if (wmw == NULL) 		return MCIERR_INVALID_DEVICE_ID;

524
    if (dwFlags & MCI_OPEN_SHAREABLE)
525
	return MCIERR_UNSUPPORTED_FUNCTION;
526

527
    if (wmw->nUseCount > 0) {
528 529 530 531
	/* The driver is already opened on this channel
	 * Wave driver cannot be shared
	 */
	return MCIERR_DEVICE_OPEN;
532
    }
533

534
    wmw->nUseCount++;
535

536
    wmw->wInput = wmw->wOutput = WAVE_MAPPER;
537
    wmw->fInput = FALSE;
538
    wmw->hWave = 0;
539
    wmw->dwStatus = MCI_MODE_NOT_READY;
Eric Pouech's avatar
Eric Pouech committed
540
    wmw->hFile = 0;
541
    wmw->lpFileName = NULL; /* will be set by WAVE_mciOpenFile */
542
    wmw->hCallback = NULL;
543
    WAVE_mciDefaultFmt(wmw);
544

545
    TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
546 547
    /* Logs show the native winmm calls us with 0 still in lpOpenParms.wDeviceID */
    wmw->wNotifyDeviceID = wDevID;
548

549
    if (dwFlags & MCI_OPEN_ELEMENT) {
550
	if (dwFlags & MCI_OPEN_ELEMENT_ID) {
551 552
	    /* could it be that (DWORD)lpOpenParms->lpstrElementName
	     * contains the hFile value ?
553 554
	     */
	    dwRet = MCIERR_UNRECOGNIZED_COMMAND;
555
	} else {
Eric Pouech's avatar
Eric Pouech committed
556
            dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
557 558
	}
    }
559
    TRACE("hFile=%p\n", wmw->hFile);
560

561
    if (dwRet == 0) {
562
	wmw->dwPosition = 0;
563

564
	wmw->dwStatus = MCI_MODE_STOP;
565 566 567

	if (dwFlags & MCI_NOTIFY)
	    WAVE_mciNotify(lpOpenParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
568 569 570
    } else {
	wmw->nUseCount--;
	if (wmw->hFile != 0)
571
	    mmioClose(wmw->hFile, 0);
572
	wmw->hFile = 0;
573 574
	HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
	wmw->lpFileName = NULL;
575
    }
Eric Pouech's avatar
Eric Pouech committed
576
    return dwRet;
577 578 579 580 581
}

/**************************************************************************
 *                               WAVE_mciCue             [internal]
 */
582
static DWORD WAVE_mciCue(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
583 584
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
585

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605
    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);

    /* Tests on systems without sound drivers show that Cue, like
     * Record and Play, opens winmm, returning MCIERR_WAVE_xyPUTSUNSUITABLE.
     * The first Cue Notify does not immediately return the
     * notification, as if a player or recorder thread is started.
     * PAUSE mode is reported when successful, but this mode is
     * different from the normal Pause, because a) Pause then returns
     * NONAPPLICABLE_FUNCTION instead of 0 and b) Set Channels etc. is
     * still accepted, returning the original notification as ABORTED.
     * I.e. Cue allows subsequent format changes, unlike Record or
     * Open file, closes winmm if the format changes and stops this
     * thread.
     * Wine creates one player or recorder thread per async. Play or
     * Record command.  Notification behaviour suggests that MS-W*
     * reuses a single thread to improve response times.  Having Cue
     * start this thread early helps to improve Play/Record's initial
     * response time.  In effect, Cue is a performance hint, which
     * justifies our almost no-op implementation.
     */
606

607 608
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
    if (wmw->dwStatus != MCI_MODE_STOP) return MCIERR_NONAPPLICABLE_FUNCTION;
609

610 611
    if ((dwFlags & MCI_NOTIFY) && lpParms)
	WAVE_mciNotify(lpParms->dwCallback,wmw,MCI_NOTIFY_SUCCESSFUL);
612

613
    return MMSYSERR_NOERROR;
614 615 616 617 618
}

/**************************************************************************
 * 				WAVE_mciStop			[internal]
 */
619
static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
620
{
621
    DWORD 		dwRet = 0;
622
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
623

624
    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
625

626
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
627

628 629 630 631 632
    if (wmw->dwStatus != MCI_MODE_STOP) {
	HANDLE old = InterlockedExchangePointer(&wmw->hCallback, NULL);
	if (old) mciDriverNotify(old, wDevID, MCI_NOTIFY_ABORTED);
    }

633 634 635 636 637 638 639 640 641 642 643
    /* wait for playback thread (if any) to exit before processing further */
    switch (wmw->dwStatus) {
    case MCI_MODE_PAUSE:
    case MCI_MODE_PLAY:
    case MCI_MODE_RECORD:
	{
	    int oldStat = wmw->dwStatus;
	    wmw->dwStatus = MCI_MODE_NOT_READY;
	    if (oldStat == MCI_MODE_PAUSE)
		dwRet = (wmw->fInput) ? waveInReset(wmw->hWave) : waveOutReset(wmw->hWave);
	}
644
	while (wmw->dwStatus != MCI_MODE_STOP)
645 646 647 648 649 650
	    Sleep(10);
	break;
    }

    /* sanity resets */
    wmw->dwStatus = MCI_MODE_STOP;
651

652 653
    if ((dwFlags & MCI_NOTIFY) && lpParms && MMSYSERR_NOERROR==dwRet)
	WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
654

655
    return dwRet;
656 657 658 659 660
}

/**************************************************************************
 *				WAVE_mciClose		[internal]
 */
661
static DWORD WAVE_mciClose(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
662
{
663
    DWORD		dwRet = 0;
664
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
665

666
    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
667

668
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
669

670
    if (wmw->dwStatus != MCI_MODE_STOP) {
671
        /* mciStop handles MCI_NOTIFY_ABORTED */
672
	dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
673
    }
674

675
    wmw->nUseCount--;
676

677 678
    if (wmw->nUseCount == 0) {
	if (wmw->hFile != 0) {
679
	    mmioClose(wmw->hFile, 0);
680 681 682
	    wmw->hFile = 0;
	}
    }
683

684 685 686
    if (wmw->lpWaveFormat != &wmw->wfxRef)
	HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
    wmw->lpWaveFormat = &wmw->wfxRef;
687
    HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
688
    wmw->lpFileName = NULL;
689

Eric Pouech's avatar
Eric Pouech committed
690
    if ((dwFlags & MCI_NOTIFY) && lpParms) {
691 692
	WAVE_mciNotify(lpParms->dwCallback, wmw,
	    (dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
693
    }
694

695 696 697
    return 0;
}

698 699 700
/**************************************************************************
 * 				WAVE_mciPlayCallback		[internal]
 */
701
static	void	CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
702 703
					      DWORD_PTR dwInstance,
					      LPARAM dwParam1, LPARAM dwParam2)
704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
{
    WINE_MCIWAVE*	wmw = (WINE_MCIWAVE*)dwInstance;

    switch (uMsg) {
    case WOM_OPEN:
    case WOM_CLOSE:
	break;
    case WOM_DONE:
	InterlockedIncrement(&wmw->dwEventCount);
	TRACE("Returning waveHdr=%lx\n", dwParam1);
	SetEvent(wmw->hEvent);
	break;
    default:
	ERR("Unknown uMsg=%d\n", uMsg);
    }
}

Eric Pouech's avatar
Eric Pouech committed
721
/******************************************************************
722
 *			WAVE_mciPlayWaitDone		[internal]
Eric Pouech's avatar
Eric Pouech committed
723
 */
724 725 726 727 728 729 730 731
static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
{
    for (;;) {
	ResetEvent(wmw->hEvent);
	if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
	    break;
	}
	InterlockedIncrement(&wmw->dwEventCount);
732

733 734 735 736
	WaitForSingleObject(wmw->hEvent, INFINITE);
    }
}

737 738 739
/**************************************************************************
 * 				WAVE_mciPlay		[internal]
 */
740
static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
741
{
742
    LPMCI_PLAY_PARMS    lpParms = (void*)pmt;
743
    DWORD		end;
744
    LONG		bufsize, count, left;
745
    DWORD		dwRet;
746
    LPWAVEHDR		waveHdr = NULL;
747
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
748
    HANDLE		oldcb;
749
    int			whidx;
750

751
    TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
752

753 754
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
755

756
    if (wmw->hFile == 0) {
757
	WARN("Can't play: no file=%s!\n", debugstr_w(wmw->lpFileName));
758 759
	return MCIERR_FILE_NOT_FOUND;
    }
760

761 762
    if (wmw->dwStatus == MCI_MODE_PAUSE && !wmw->fInput && !(dwFlags & (MCI_FROM | MCI_TO))) {
	/* FIXME: notification is different with Resume than Play */
763 764
	return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
    }
765

766 767 768 769
    /** This function will be called again by a thread when async is used.
     * We have to set MCI_MODE_PLAY before we do this so that the app can spin
     * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
     */
770 771 772 773 774 775
    if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
	!((wmw->dwStatus == MCI_MODE_PLAY) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
	/* FIXME: Check FROM/TO parameters first. */
	/* FIXME: Play; Play [notify|wait] must hook into the running player. */
	dwRet = WAVE_mciStop(wDevID, MCI_WAIT, NULL);
	if (dwRet) return dwRet;
776 777
    }

778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800
    if (wmw->lpWaveFormat->wFormatTag == WAVE_FORMAT_PCM) {
        if (wmw->lpWaveFormat->nBlockAlign !=
            wmw->lpWaveFormat->nChannels * wmw->lpWaveFormat->wBitsPerSample/8) {
            WARN("Incorrect nBlockAlign (%d), setting it to %d\n",
                wmw->lpWaveFormat->nBlockAlign,
                wmw->lpWaveFormat->nChannels *
                 wmw->lpWaveFormat->wBitsPerSample/8);
            wmw->lpWaveFormat->nBlockAlign =
                wmw->lpWaveFormat->nChannels *
                wmw->lpWaveFormat->wBitsPerSample/8;
        }
        if (wmw->lpWaveFormat->nAvgBytesPerSec !=
            wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
            WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
                wmw->lpWaveFormat->nAvgBytesPerSec,
                wmw->lpWaveFormat->nSamplesPerSec *
                 wmw->lpWaveFormat->nBlockAlign);
            wmw->lpWaveFormat->nAvgBytesPerSec =
                wmw->lpWaveFormat->nSamplesPerSec *
                wmw->lpWaveFormat->nBlockAlign;
        }
    }

801
    end = wmw->ckWaveData.cksize;
802
    if (dwFlags & MCI_TO) {
803 804 805 806
	DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
	if (position > end)		return MCIERR_OUTOFRANGE;
	end = position;
    }
807
    if (dwFlags & MCI_FROM) {
808 809 810 811 812 813
	DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
	if (position > end)		return MCIERR_OUTOFRANGE;
	/* Seek rounds down, so do we. */
	position /= wmw->lpWaveFormat->nBlockAlign;
	position *= wmw->lpWaveFormat->nBlockAlign;
	wmw->dwPosition = position;
814
    }
815 816 817 818 819 820 821 822
    if (end < wmw->dwPosition) return MCIERR_OUTOFRANGE;
    left = end - wmw->dwPosition;
    if (0==left) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */

    wmw->fInput = FALSE; /* FIXME: waveInOpen may have been called. */
    wmw->dwStatus = MCI_MODE_PLAY;

    if (!(dwFlags & MCI_WAIT)) {
823
	return MCI_SendCommandAsync(wDevID, WAVE_mciPlay, dwFlags,
824
				    (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
825
    }
826

827
    TRACE("Playing from byte=%u to byte=%u\n", wmw->dwPosition, end);
828

829 830 831 832 833
    oldcb = InterlockedExchangePointer(&wmw->hCallback,
	(dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
    if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
    oldcb = NULL;

834 835 836
#define	WAVE_ALIGN_ON_BLOCK(wmw,v) \
((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)

837
    /* go back to beginning of chunk plus the requested position */
838
    /* FIXME: I'm not sure this is correct, notably because some data linked to
Austin English's avatar
Austin English committed
839
     * the decompression state machine will not be correctly initialized.
840 841 842
     * try it this way (other way would be to decompress from 0 up to dwPosition
     * and to start sending to hWave when dwPosition is reached)
     */
843
    mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
844

845
    dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, wmw->wOutput, wmw->lpWaveFormat,
846
			(DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
847

Eric Pouech's avatar
Eric Pouech committed
848
    if (dwRet != 0) {
849
	TRACE("Can't open low level audio device %d\n", dwRet);
850 851 852
	dwRet = MCIERR_DEVICE_OPEN;
	wmw->hWave = 0;
	goto cleanUp;
Eric Pouech's avatar
Eric Pouech committed
853
    }
854

855 856 857 858 859 860
    /* make it so that 3 buffers per second are needed */
    bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);

    waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
    waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
    waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
861 862 863 864
    waveHdr[0].dwUser         = waveHdr[1].dwUser         = 0L;
    waveHdr[0].dwLoops        = waveHdr[1].dwLoops        = 0L;
    waveHdr[0].dwFlags        = waveHdr[1].dwFlags        = 0L;
    waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;
865
    if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
866 867 868 869
	waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
	dwRet = MCIERR_INTERNAL;
	goto cleanUp;
    }
870 871

    whidx = 0;
872
    wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
873 874 875 876
    if (!wmw->hEvent) {
	dwRet = MCIERR_OUT_OF_MEMORY;
	goto cleanUp;
    }
877
    wmw->dwEventCount = 1L; /* for first buffer */
878

879
    TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
880
    if (hEvent) SetEvent(hEvent);
881

882
    /* FIXME: this doesn't work if wmw->dwPosition != 0 */
883
    while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
884
	count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
885
	TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
886
	if (count < 1)
Eric Pouech's avatar
Eric Pouech committed
887
	    break;
888 889
	/* count is always <= bufsize, so this is correct regarding the
	 * waveOutPrepareHeader function
890
	 */
891
	waveHdr[whidx].dwBufferLength = count;
892
	waveHdr[whidx].dwFlags &= ~WHDR_DONE;
893 894
	TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u\n",
	      &waveHdr[whidx], waveHdr[whidx].dwBufferLength);
895
	dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
896 897 898 899 900
	if (dwRet) {
	    ERR("Aborting play loop, WODM_WRITE error %d\n", dwRet);
	    dwRet = MCIERR_HARDWARE;
	    break;
	}
901
	left -= count;
902
	wmw->dwPosition += count;
903
	TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
904
	/* InterlockedDecrement if and only if waveOutWrite is successful */
905
	WAVE_mciPlayWaitDone(wmw);
906
	whidx ^= 1;
907
    }
Eric Pouech's avatar
Eric Pouech committed
908

909 910 911
    WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */

    /* just to get rid of some race conditions between play, stop and pause */
912
    waveOutReset(wmw->hWave);
Eric Pouech's avatar
Eric Pouech committed
913

914 915 916
    waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
    waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));

917
cleanUp:
918 919 920
    if (dwFlags & MCI_NOTIFY)
	oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);

921 922 923 924 925 926 927
    HeapFree(GetProcessHeap(), 0, waveHdr);

    if (wmw->hWave) {
	waveOutClose(wmw->hWave);
	wmw->hWave = 0;
    }
    CloseHandle(wmw->hEvent);
928
    wmw->hEvent = NULL;
929

930 931
    wmw->dwStatus = MCI_MODE_STOP;

Sven Baars's avatar
Sven Baars committed
932
    /* Let the potentially asynchronous commands support FAILURE notification. */
933 934
    if (oldcb) mciDriverNotify(oldcb, wDevID,
	dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
935 936

    return dwRet;
937 938
}

939
/**************************************************************************
940
 * 				WAVE_mciRecordCallback		[internal]
941
 */
942
static	void	CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
943 944
                                                DWORD_PTR dwInstance,
                                                LPARAM dwParam1, LPARAM dwParam2)
945 946
{
    WINE_MCIWAVE*	wmw = (WINE_MCIWAVE*)dwInstance;
Eric Pouech's avatar
Eric Pouech committed
947
    LPWAVEHDR           lpWaveHdr;
948
    LONG                count = 0;
Eric Pouech's avatar
Eric Pouech committed
949

950 951 952 953 954 955 956 957 958 959 960 961
    switch (uMsg) {
    case WIM_OPEN:
    case WIM_CLOSE:
	break;
    case WIM_DATA:
	lpWaveHdr = (LPWAVEHDR) dwParam1;

	InterlockedIncrement(&wmw->dwEventCount);

	count = mmioWrite(wmw->hFile, lpWaveHdr->lpData, lpWaveHdr->dwBytesRecorded);

	lpWaveHdr->dwFlags &= ~WHDR_DONE;
Eric Pouech's avatar
Eric Pouech committed
962 963 964
        if (count > 0)
            wmw->dwPosition  += count;
        /* else error reporting ?? */
965 966 967 968 969
        if (wmw->dwStatus == MCI_MODE_RECORD)
        {
           /* Only queue up another buffer if we are recording.  We could receive this
              message also when waveInReset() is called, since it notifies on all wave
              buffers that are outstanding.  Queueing up more sometimes causes waveInClose
970
              to fail. */
971
           waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
972
           TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
973
        }
974

975 976 977 978 979 980 981
	SetEvent(wmw->hEvent);
	break;
    default:
	ERR("Unknown uMsg=%d\n", uMsg);
    }
}

Eric Pouech's avatar
Eric Pouech committed
982
/******************************************************************
983
 *			WAVE_mciRecordWaitDone		[internal]
Eric Pouech's avatar
Eric Pouech committed
984
 */
985 986 987 988 989 990 991 992
static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
{
    for (;;) {
	ResetEvent(wmw->hEvent);
	if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
	    break;
	}
	InterlockedIncrement(&wmw->dwEventCount);
993

994 995 996 997
	WaitForSingleObject(wmw->hEvent, INFINITE);
    }
}

998 999 1000
/**************************************************************************
 * 				WAVE_mciRecord			[internal]
 */
1001
static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD_PTR dwFlags, DWORD_PTR pmt, HANDLE hEvent)
1002
{
1003
    LPMCI_RECORD_PARMS  lpParms = (void*)pmt;
1004
    DWORD		end;
Eric Pouech's avatar
Eric Pouech committed
1005
    DWORD		dwRet = MMSYSERR_NOERROR;
1006
    LONG		bufsize;
1007
    LPWAVEHDR		waveHdr = NULL;
1008
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1009
    HANDLE		oldcb;
1010

1011
    TRACE("(%u, %08lX, %p);\n", wDevID, dwFlags, lpParms);
1012

1013 1014
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
1015

1016 1017 1018 1019 1020
    if (wmw->dwStatus == MCI_MODE_PAUSE && wmw->fInput) {
        /* FIXME: parameters (start/end) in lpParams may not be used */
        return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
    }

1021
    /** This function will be called again by a thread when async is used.
1022
     * We have to set MCI_MODE_RECORD before we do this so that the app can spin
1023 1024
     * on MCI_STATUS, so we have to allow it here if we're not going to start this thread.
     */
1025 1026
    if ( !(wmw->dwStatus == MCI_MODE_STOP) &&
	!((wmw->dwStatus == MCI_MODE_RECORD) && (dwFlags & MCI_WAIT) && !wmw->hWave)) {
1027
	return MCIERR_INTERNAL;
1028
    }
1029

1030
    wmw->fInput = TRUE; /* FIXME: waveOutOpen may have been called. */
1031
    wmw->dwStatus = MCI_MODE_RECORD;
1032 1033

    if (!(dwFlags & MCI_WAIT)) {
1034
	return MCI_SendCommandAsync(wDevID, WAVE_mciRecord, dwFlags,
1035
				    (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
1036 1037
    }

Eric Pouech's avatar
Eric Pouech committed
1038 1039 1040 1041
    /* FIXME: we only re-create the RIFF structure from an existing file (if any)
     * we don't modify the wave part of an existing file (ie. we always erase an
     * existing content, we don't overwrite)
     */
1042
    HeapFree(GetProcessHeap(), 0, wmw->lpFileName);
1043
    dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->lpFileName);
Eric Pouech's avatar
Eric Pouech committed
1044 1045
    if (dwRet != 0) return dwRet;

1046
    /* new RIFF file, lpWaveFormat now valid */
Eric Pouech's avatar
Eric Pouech committed
1047
    dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
1048
    if (dwRet != 0) return dwRet;
1049

1050
    if (dwFlags & MCI_TO) {
1051
	end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
1052
    } else end = 0xFFFFFFFF;
1053
    if (dwFlags & MCI_FROM) {
1054 1055 1056 1057 1058 1059
	DWORD position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
	if (wmw->ckWaveData.cksize < position)	return MCIERR_OUTOFRANGE;
	/* Seek rounds down, so do we. */
	position /= wmw->lpWaveFormat->nBlockAlign;
	position *= wmw->lpWaveFormat->nBlockAlign;
	wmw->dwPosition = position;
1060
    }
1061
    if (end==wmw->dwPosition) return MMSYSERR_NOERROR; /* FIXME: NOTIFY */
1062

1063
    TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1064

1065 1066 1067 1068 1069
    oldcb = InterlockedExchangePointer(&wmw->hCallback,
	(dwFlags & MCI_NOTIFY) ? HWND_32(LOWORD(lpParms->dwCallback)) : NULL);
    if (oldcb) mciDriverNotify(oldcb, wDevID, MCI_NOTIFY_ABORTED);
    oldcb = NULL;

1070 1071 1072 1073 1074
#define	WAVE_ALIGN_ON_BLOCK(wmw,v) \
((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)

    wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);

Austin English's avatar
Austin English committed
1075
    /* Go back to the beginning of the chunk plus the requested position */
1076
    /* FIXME: I'm not sure this is correct, notably because some data linked to
Austin English's avatar
Austin English committed
1077 1078 1079
     * the decompression state machine will not be correctly initialized.
     * Try it this way (other way would be to decompress from 0 up to dwPosition
     * and to start sending to hWave when dwPosition is reached).
1080 1081 1082
     */
    mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */

1083
    dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, wmw->wInput, wmw->lpWaveFormat,
1084
			(DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1085

Eric Pouech's avatar
Eric Pouech committed
1086
    if (dwRet != MMSYSERR_NOERROR) {
1087
	TRACE("Can't open low level audio device %d\n", dwRet);
1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
	dwRet = MCIERR_DEVICE_OPEN;
	wmw->hWave = 0;
	goto cleanUp;
    }

    /* make it so that 3 buffers per second are needed */
    bufsize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->lpWaveFormat->nAvgBytesPerSec / 3);

    waveHdr = HeapAlloc(GetProcessHeap(), 0, 2 * sizeof(WAVEHDR) + 2 * bufsize);
    waveHdr[0].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR);
    waveHdr[1].lpData = (char*)waveHdr + 2 * sizeof(WAVEHDR) + bufsize;
    waveHdr[0].dwUser         = waveHdr[1].dwUser         = 0L;
    waveHdr[0].dwLoops        = waveHdr[1].dwLoops        = 0L;
    waveHdr[0].dwFlags        = waveHdr[1].dwFlags        = 0L;
    waveHdr[0].dwBufferLength = waveHdr[1].dwBufferLength = bufsize;

1104
    if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1105 1106 1107 1108 1109 1110 1111 1112 1113 1114
	waveInPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
	dwRet = MCIERR_INTERNAL;
	goto cleanUp;
    }

    if (waveInAddBuffer(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
	waveInAddBuffer(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
	dwRet = MCIERR_INTERNAL;
	goto cleanUp;
    }
1115

1116
    wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1117 1118
    wmw->dwEventCount = 1L; /* for first buffer */

1119
    TRACE("Recording (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, end - wmw->dwPosition);
1120

1121 1122
    dwRet = waveInStart(wmw->hWave);

1123 1124
    if (hEvent) SetEvent(hEvent);

Eric Pouech's avatar
Eric Pouech committed
1125
    while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1126
	WAVE_mciRecordWaitDone(wmw);
1127
    }
1128 1129 1130 1131 1132
    /* Grab callback before another thread kicks in after we change dwStatus. */
    if (dwFlags & MCI_NOTIFY) {
	oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);
	dwFlags &= ~MCI_NOTIFY;
    }
1133 1134
    /* needed so that the callback above won't add again the buffers returned by the reset */
    wmw->dwStatus = MCI_MODE_STOP;
1135

1136
    waveInReset(wmw->hWave);
1137

1138 1139 1140 1141 1142
    waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
    waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));

    dwRet = 0;

1143
cleanUp:
1144 1145 1146
    if (dwFlags & MCI_NOTIFY)
	oldcb = InterlockedExchangePointer(&wmw->hCallback, NULL);

1147 1148 1149 1150 1151 1152 1153 1154
    HeapFree(GetProcessHeap(), 0, waveHdr);

    if (wmw->hWave) {
	waveInClose(wmw->hWave);
	wmw->hWave = 0;
    }
    CloseHandle(wmw->hEvent);

1155 1156
    wmw->dwStatus = MCI_MODE_STOP;

1157 1158
    if (oldcb) mciDriverNotify(oldcb, wDevID,
	dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1159 1160 1161

    return dwRet;

1162 1163 1164 1165 1166
}

/**************************************************************************
 * 				WAVE_mciPause			[internal]
 */
1167
static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1168 1169 1170
{
    DWORD 		dwRet;
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1171

1172
    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1173

1174
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
1175

1176 1177 1178
    switch (wmw->dwStatus) {
    case MCI_MODE_PLAY:
	dwRet = waveOutPause(wmw->hWave);
1179 1180 1181 1182 1183
	if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
	else { /* When playthread was not started yet, winmm not opened, error 5 MMSYSERR_INVALHANDLE */
	    ERR("waveOutPause error %d\n",dwRet);
	    dwRet = MCIERR_INTERNAL;
	}
1184 1185 1186
	break;
    case MCI_MODE_RECORD:
	dwRet = waveInStop(wmw->hWave);
1187 1188 1189 1190 1191
	if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PAUSE;
	else {
	    ERR("waveInStop error %d\n",dwRet);
	    dwRet = MCIERR_INTERNAL;
	}
1192 1193 1194 1195 1196
	break;
    case MCI_MODE_PAUSE:
	dwRet = MMSYSERR_NOERROR;
	break;
    default:
1197
	dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1198
    }
1199 1200
    if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
	WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1201
    return dwRet;
1202 1203 1204 1205 1206
}

/**************************************************************************
 * 				WAVE_mciResume			[internal]
 */
1207
static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1208 1209
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1210
    DWORD		dwRet;
1211

1212
    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1213

1214
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
1215

1216 1217
    switch (wmw->dwStatus) {
    case MCI_MODE_PAUSE:
1218
	/* Only update dwStatus if wave* succeeds and will exchange buffers buffers. */
1219 1220
	if (wmw->fInput) {
	    dwRet = waveInStart(wmw->hWave);
1221 1222 1223 1224 1225
	    if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_RECORD;
	    else {
		ERR("waveInStart error %d\n",dwRet);
		dwRet = MCIERR_INTERNAL;
	    }
1226 1227
	} else {
	    dwRet = waveOutRestart(wmw->hWave);
1228 1229 1230 1231 1232
	    if (dwRet==MMSYSERR_NOERROR) wmw->dwStatus = MCI_MODE_PLAY;
	    else {
		ERR("waveOutRestart error %d\n",dwRet);
		dwRet = MCIERR_INTERNAL;
	    }
1233 1234 1235 1236 1237 1238 1239
	}
	break;
    case MCI_MODE_PLAY:
    case MCI_MODE_RECORD:
	dwRet = MMSYSERR_NOERROR;
	break;
    default:
1240
	dwRet = MCIERR_NONAPPLICABLE_FUNCTION;
1241
    }
1242 1243
    if (MMSYSERR_NOERROR==dwRet && (dwFlags & MCI_NOTIFY) && lpParms)
	WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1244
    return dwRet;
1245 1246 1247 1248 1249
}

/**************************************************************************
 * 				WAVE_mciSeek			[internal]
 */
1250
static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1251 1252
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1253
    DWORD		position, dwRet;
1254

1255
    TRACE("(%04X, %08X, %p);\n", wDevID, dwFlags, lpParms);
1256

1257 1258 1259
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;

1260 1261 1262
    position = dwFlags & (MCI_SEEK_TO_START|MCI_SEEK_TO_END|MCI_TO);
    if (!position)		return MCIERR_MISSING_PARAMETER;
    if (position&(position-1))	return MCIERR_FLAGS_NOT_COMPATIBLE;
1263

1264 1265 1266 1267 1268 1269 1270 1271 1272 1273
    /* Stop sends MCI_NOTIFY_ABORTED when needed */
    dwRet = WAVE_mciStop(wDevID, MCI_WAIT, 0);
    if (dwRet != MMSYSERR_NOERROR) return dwRet;

    if (dwFlags & MCI_TO) {
	position = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
	if (position > wmw->ckWaveData.cksize)
	    return MCIERR_OUTOFRANGE;
    } else if (dwFlags & MCI_SEEK_TO_START) {
	position = 0;
1274
    } else {
1275
	position = wmw->ckWaveData.cksize;
1276
    }
1277 1278 1279 1280 1281 1282 1283
    /* Seek rounds down, unless at end */
    if (position != wmw->ckWaveData.cksize) {
	position /= wmw->lpWaveFormat->nBlockAlign;
	position *= wmw->lpWaveFormat->nBlockAlign;
    }
    wmw->dwPosition = position;
    TRACE("Seeking to position=%u bytes\n", position);
1284

1285 1286 1287
    if (dwFlags & MCI_NOTIFY)
	WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);

1288
    return MMSYSERR_NOERROR;
1289
}
1290 1291 1292 1293

/**************************************************************************
 * 				WAVE_mciSet			[internal]
 */
1294
static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_SET_PARMS lpParms)
1295 1296
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1297

1298
    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1299

1300
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
1301
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
1302

1303 1304 1305
    if (dwFlags & MCI_SET_TIME_FORMAT) {
	switch (lpParms->dwTimeFormat) {
	case MCI_FORMAT_MILLISECONDS:
1306
	    TRACE("MCI_FORMAT_MILLISECONDS !\n");
1307 1308 1309
	    wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
	    break;
	case MCI_FORMAT_BYTES:
1310
	    TRACE("MCI_FORMAT_BYTES !\n");
1311 1312 1313
	    wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
	    break;
	case MCI_FORMAT_SAMPLES:
1314
	    TRACE("MCI_FORMAT_SAMPLES !\n");
1315 1316 1317
	    wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
	    break;
	default:
1318
            WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1319 1320 1321 1322
	    return MCIERR_BAD_TIME_FORMAT;
	}
    }
    if (dwFlags & MCI_SET_VIDEO) {
1323
	TRACE("No support for video !\n");
1324 1325 1326
	return MCIERR_UNSUPPORTED_FUNCTION;
    }
    if (dwFlags & MCI_SET_DOOR_OPEN) {
1327
	TRACE("No support for door open !\n");
1328 1329 1330
	return MCIERR_UNSUPPORTED_FUNCTION;
    }
    if (dwFlags & MCI_SET_DOOR_CLOSED) {
1331
	TRACE("No support for door close !\n");
1332 1333 1334 1335
	return MCIERR_UNSUPPORTED_FUNCTION;
    }
    if (dwFlags & MCI_SET_AUDIO) {
	if (dwFlags & MCI_SET_ON) {
1336
	    TRACE("MCI_SET_ON audio !\n");
1337
	} else if (dwFlags & MCI_SET_OFF) {
1338
	    TRACE("MCI_SET_OFF audio !\n");
1339
	} else {
1340
	    WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1341 1342
	    return MCIERR_BAD_INTEGER;
	}
1343

1344 1345 1346 1347 1348
	switch (lpParms->dwAudio)
        {
        case MCI_SET_AUDIO_ALL:         TRACE("MCI_SET_AUDIO_ALL !\n"); break;
        case MCI_SET_AUDIO_LEFT:        TRACE("MCI_SET_AUDIO_LEFT !\n"); break;
        case MCI_SET_AUDIO_RIGHT:       TRACE("MCI_SET_AUDIO_RIGHT !\n"); break;
1349
        default:                        WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1350
        }
1351
    }
1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379
    if (dwFlags & MCI_WAVE_INPUT) {
	TRACE("MCI_WAVE_INPUT = %d\n", lpParms->wInput);
	if (lpParms->wInput >= waveInGetNumDevs())
	    return MCIERR_OUTOFRANGE;
	if (wmw->wInput != (WORD)lpParms->wInput)
	    WAVE_mciStop(wDevID, MCI_WAIT, NULL);
	wmw->wInput = lpParms->wInput;
    }
    if (dwFlags & MCI_WAVE_OUTPUT) {
	TRACE("MCI_WAVE_OUTPUT = %d\n", lpParms->wOutput);
	if (lpParms->wOutput >= waveOutGetNumDevs())
	    return MCIERR_OUTOFRANGE;
	if (wmw->wOutput != (WORD)lpParms->wOutput)
	    WAVE_mciStop(wDevID, MCI_WAIT, NULL);
	wmw->wOutput = lpParms->wOutput;
    }
    if (dwFlags & MCI_WAVE_SET_ANYINPUT) {
	TRACE("MCI_WAVE_SET_ANYINPUT\n");
	if (wmw->wInput != (WORD)lpParms->wInput)
	    WAVE_mciStop(wDevID, MCI_WAIT, NULL);
	wmw->wInput = WAVE_MAPPER;
    }
    if (dwFlags & MCI_WAVE_SET_ANYOUTPUT) {
	TRACE("MCI_WAVE_SET_ANYOUTPUT\n");
	if (wmw->wOutput != (WORD)lpParms->wOutput)
	    WAVE_mciStop(wDevID, MCI_WAIT, NULL);
	wmw->wOutput = WAVE_MAPPER;
    }
1380
    /* Set wave format parameters is refused after Open or Record.*/
1381
    if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1382
	TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", lpParms->wFormatTag);
1383
	if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1384
	if (lpParms->wFormatTag != WAVE_FORMAT_PCM)
1385 1386
	    return MCIERR_OUTOFRANGE;
    }
1387
    if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
1388
	if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1389
	wmw->wfxRef.nAvgBytesPerSec = lpParms->nAvgBytesPerSec;
1390
	TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1391 1392
    }
    if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
1393
	if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1394
	wmw->wfxRef.wBitsPerSample = lpParms->wBitsPerSample;
1395 1396 1397
	TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
    }
    if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
1398
	if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1399
	wmw->wfxRef.nBlockAlign = lpParms->nBlockAlign;
1400 1401 1402
	TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
    }
    if (dwFlags & MCI_WAVE_SET_CHANNELS) {
1403
	if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1404
	wmw->wfxRef.nChannels = lpParms->nChannels;
1405 1406 1407
	TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
    }
    if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
1408
	if (wmw->lpWaveFormat != &wmw->wfxRef) return MCIERR_NONAPPLICABLE_FUNCTION;
1409
	wmw->wfxRef.nSamplesPerSec = lpParms->nSamplesPerSec;
1410
	TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1411
    }
1412 1413
    if (dwFlags & MCI_NOTIFY)
	WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1414 1415 1416
    return 0;
}

1417 1418 1419
/**************************************************************************
 *				WAVE_mciSave		[internal]
 */
1420
static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1421 1422
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1423
    DWORD		ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1424

1425
    TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1426 1427 1428 1429 1430 1431 1432
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
    if (wmw     == NULL)	return MCIERR_INVALID_DEVICE_ID;

    if (dwFlags & MCI_WAIT)
    {
    	FIXME("MCI_WAIT not implemented\n");
    }
Eric Pouech's avatar
Eric Pouech committed
1433
    WAVE_mciStop(wDevID, 0, NULL);
1434 1435 1436 1437 1438

    ret = mmioAscend(wmw->hFile, &wmw->ckWaveData, 0);
    ret = mmioAscend(wmw->hFile, &wmw->ckMainRIFF, 0);

    ret = mmioClose(wmw->hFile, 0);
Eric Pouech's avatar
Eric Pouech committed
1439
    wmw->hFile = 0;
1440

1441 1442 1443 1444 1445 1446 1447 1448 1449 1450
    /*
      If the destination file already exists, it has to be overwritten.  (Behaviour
      verified in Windows (2000)).  If it doesn't overwrite, it is breaking one of
      my applications.  We are making use of mmioRename, which WILL NOT overwrite
      the destination file (which is what Windows does, also verified in Win2K)
      So, lets delete the destination file before calling mmioRename.  If the
      destination file DOESN'T exist, the delete will fail silently.  Let's also be
      careful not to lose our previous error code.
    */
    tmpRet = GetLastError();
1451
    DeleteFileW (lpParms->lpfilename);
1452
    SetLastError(tmpRet);
1453

1454 1455
    /* FIXME: Open file.wav; Save; must not rename the original file.
     * Nor must Save a.wav; Save b.wav rename a. */
1456
    if (0 == mmioRenameW(wmw->lpFileName, lpParms->lpfilename, 0, 0 )) {
Eric Pouech's avatar
Eric Pouech committed
1457
	ret = MMSYSERR_NOERROR;
1458
    }
1459

1460 1461
    if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
	WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1462

Eric Pouech's avatar
Eric Pouech committed
1463 1464 1465
    if (ret == MMSYSERR_NOERROR)
        ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);

1466 1467 1468
    return ret;
}

1469 1470 1471
/**************************************************************************
 * 				WAVE_mciStatus		[internal]
 */
1472
static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1473 1474
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1475
    DWORD		ret = 0;
1476

1477
    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1478
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
1479
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
1480
    if (!(dwFlags & MCI_STATUS_ITEM))	return MCIERR_MISSING_PARAMETER;
1481

1482
    if (dwFlags & MCI_STATUS_ITEM) {
1483
	switch (lpParms->dwItem) {
1484 1485
	case MCI_STATUS_CURRENT_TRACK:
	    lpParms->dwReturn = 1;
1486
            TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1487 1488
	    break;
	case MCI_STATUS_LENGTH:
1489 1490 1491 1492
	    if (!wmw->hFile) {
		lpParms->dwReturn = 0;
		return MCIERR_UNSUPPORTED_FUNCTION;
	    }
1493
	    /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1494
	    lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize);
1495
            TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1496 1497
	    break;
	case MCI_STATUS_MODE:
1498 1499 1500
	    TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
	    lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
	    ret = MCI_RESOURCE_RETURNED;
1501 1502
	    break;
	case MCI_STATUS_MEDIA_PRESENT:
1503
	    TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1504 1505
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1506 1507 1508 1509
	    break;
	case MCI_STATUS_NUMBER_OF_TRACKS:
	    /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
	    lpParms->dwReturn = 1;
1510
            TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1511 1512
	    break;
	case MCI_STATUS_POSITION:
1513 1514 1515 1516
	    if (!wmw->hFile) {
		lpParms->dwReturn = 0;
		return MCIERR_UNSUPPORTED_FUNCTION;
	    }
1517
	    /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1518
	    lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1519
							     (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition);
1520
            TRACE("MCI_STATUS_POSITION %s => %lu\n",
1521 1522 1523
		  (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
	    break;
	case MCI_STATUS_READY:
1524 1525 1526 1527
	    lpParms->dwReturn = (wmw->dwStatus == MCI_MODE_NOT_READY) ?
		MAKEMCIRESOURCE(FALSE, MCI_FALSE) : MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    TRACE("MCI_STATUS_READY => %u!\n", LOWORD(lpParms->dwReturn));
	    ret = MCI_RESOURCE_RETURNED;
1528 1529
	    break;
	case MCI_STATUS_TIME_FORMAT:
1530
	    lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1531
            TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1532
	    ret = MCI_RESOURCE_RETURNED;
1533 1534
	    break;
	case MCI_WAVE_INPUT:
1535 1536 1537 1538 1539 1540
	    if (wmw->wInput != (WORD)WAVE_MAPPER)
		lpParms->dwReturn = wmw->wInput;
	    else {
		lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
		ret = MCI_RESOURCE_RETURNED;
	    }
1541
	    TRACE("MCI_WAVE_INPUT => %d\n", (signed)wmw->wInput);
1542 1543
	    break;
	case MCI_WAVE_OUTPUT:
1544 1545 1546 1547 1548 1549
	    if (wmw->wOutput != (WORD)WAVE_MAPPER)
		lpParms->dwReturn = wmw->wOutput;
	    else {
		lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_MAPPER, WAVE_MAPPER_S);
		ret = MCI_RESOURCE_RETURNED;
	    }
1550
	    TRACE("MCI_WAVE_OUTPUT => %d\n", (signed)wmw->wOutput);
1551
	    break;
1552 1553
	/* It is always ok to query wave format parameters,
	 * except on auto-open yield MCIERR_UNSUPPORTED_FUNCTION. */
1554 1555 1556 1557 1558 1559 1560 1561 1562
	case MCI_WAVE_STATUS_FORMATTAG:
	    if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
		lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
	    else {
		lpParms->dwReturn = MAKEMCIRESOURCE(WAVE_FORMAT_PCM, WAVE_FORMAT_PCM_S);
		ret = MCI_RESOURCE_RETURNED;
	    }
	    TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
	    break;
1563
	case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1564
	    lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1565
	    TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1566 1567
	    break;
	case MCI_WAVE_STATUS_BITSPERSAMPLE:
1568
	    lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1569
	    TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1570 1571
	    break;
	case MCI_WAVE_STATUS_BLOCKALIGN:
1572
	    lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1573
	    TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1574 1575
	    break;
	case MCI_WAVE_STATUS_CHANNELS:
1576
	    lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1577
	    TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1578
	    break;
1579 1580 1581
	case MCI_WAVE_STATUS_SAMPLESPERSEC:
	    lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
	    TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1582 1583
	    break;
	case MCI_WAVE_STATUS_LEVEL:
1584
	    TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1585 1586 1587
	    lpParms->dwReturn = 0xAAAA5555;
	    break;
	default:
1588
            WARN("unknown command %08X !\n", lpParms->dwItem);
1589
	    return MCIERR_UNSUPPORTED_FUNCTION;
1590 1591
	}
    }
1592 1593
    if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
	WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1594
    return ret;
1595 1596 1597 1598 1599
}

/**************************************************************************
 * 				WAVE_mciGetDevCaps		[internal]
 */
1600
static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1601 1602 1603
				LPMCI_GETDEVCAPS_PARMS lpParms)
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1604 1605
    DWORD		ret = 0;

1606
    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1607

1608
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
1609
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
1610

1611 1612 1613
    if (dwFlags & MCI_GETDEVCAPS_ITEM) {
	switch(lpParms->dwItem) {
	case MCI_GETDEVCAPS_DEVICE_TYPE:
1614 1615
	    lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
	    ret = MCI_RESOURCE_RETURNED;
1616 1617
	    break;
	case MCI_GETDEVCAPS_HAS_AUDIO:
1618 1619
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1620 1621
	    break;
	case MCI_GETDEVCAPS_HAS_VIDEO:
1622 1623
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
1624 1625
	    break;
	case MCI_GETDEVCAPS_USES_FILES:
1626 1627
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1628 1629
	    break;
	case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1630 1631
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1632 1633
	    break;
	case MCI_GETDEVCAPS_CAN_RECORD:
1634 1635
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1636 1637
	    break;
	case MCI_GETDEVCAPS_CAN_EJECT:
1638 1639
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
1640 1641
	    break;
	case MCI_GETDEVCAPS_CAN_PLAY:
1642 1643
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1644 1645
	    break;
	case MCI_GETDEVCAPS_CAN_SAVE:
1646 1647
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1648 1649
	    break;
	case MCI_WAVE_GETDEVCAPS_INPUTS:
1650
	    lpParms->dwReturn = waveInGetNumDevs();
1651 1652
	    break;
	case MCI_WAVE_GETDEVCAPS_OUTPUTS:
1653
	    lpParms->dwReturn = waveOutGetNumDevs();
1654 1655
	    break;
	default:
1656
            FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1657 1658
	    return MCIERR_UNRECOGNIZED_COMMAND;
	}
1659 1660 1661
    } else {
	WARN("No GetDevCaps-Item !\n");
	return MCIERR_UNRECOGNIZED_COMMAND;
1662
    }
1663 1664
    if ((dwFlags & MCI_NOTIFY) && HRESULT_CODE(ret)==0)
	WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1665
    return ret;
1666 1667 1668 1669 1670
}

/**************************************************************************
 * 				WAVE_mciInfo			[internal]
 */
1671
static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1672 1673
{
    DWORD		ret = 0;
1674
    LPCWSTR		str = 0;
1675
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1676

1677
    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1678

1679 1680 1681
    if (!lpParms || !lpParms->lpstrReturn)
	return MCIERR_NULL_PARAMETER_BLOCK;

1682 1683
    TRACE("buf=%p, len=%u\n", lpParms->lpstrReturn, lpParms->dwRetSize);

1684
    if (wmw == NULL) {
1685 1686
	ret = MCIERR_INVALID_DEVICE_ID;
    } else {
1687 1688 1689 1690
        static const WCHAR wszAudio  [] = {'W','i','n','e','\'','s',' ','a','u','d','i','o',' ','p','l','a','y','e','r',0};
        static const WCHAR wszWaveIn [] = {'W','i','n','e',' ','W','a','v','e',' ','I','n',0};
        static const WCHAR wszWaveOut[] = {'W','i','n','e',' ','W','a','v','e',' ','O','u','t',0};

Eric Pouech's avatar
Eric Pouech committed
1691
	switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1692
	case MCI_INFO_PRODUCT: str = wszAudio; break;
1693
	case MCI_INFO_FILE:    str = wmw->lpFileName; break;
1694 1695
	case MCI_WAVE_INPUT:   str = wszWaveIn; break;
	case MCI_WAVE_OUTPUT:  str = wszWaveOut; break;
1696
	default:
1697
            WARN("Don't know this info command (%u)\n", dwFlags);
1698
	    ret = MCIERR_UNRECOGNIZED_KEYWORD;
1699 1700
	}
    }
1701 1702 1703 1704 1705 1706 1707
    if (!ret) {
	if (lpParms->dwRetSize) {
	    WCHAR zero = 0;
	    /* FIXME? Since NT, mciwave, mciseq and mcicda set dwRetSize
	     *        to the number of characters written, excluding \0. */
	    lstrcpynW(lpParms->lpstrReturn, str ? str : &zero, lpParms->dwRetSize);
	} else ret = MCIERR_PARAM_OVERFLOW;
1708
    }
1709 1710
    if (MMSYSERR_NOERROR==ret && (dwFlags & MCI_NOTIFY))
	WAVE_mciNotify(lpParms->dwCallback, wmw, MCI_NOTIFY_SUCCESSFUL);
1711 1712 1713 1714
    return ret;
}

/**************************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
1715
 * 				DriverProc (MCIWAVE.@)
1716
 */
1717 1718
LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
                                    LPARAM dwParam1, LPARAM dwParam2)
1719
{
1720
    TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1721
	  dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1722

1723
    switch (wMsg) {
1724 1725
    case DRV_LOAD:		return 1;
    case DRV_FREE:		return 1;
1726
    case DRV_OPEN:		return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1727
    case DRV_CLOSE:		return WAVE_drvClose(dwDevID);
1728 1729 1730
    case DRV_ENABLE:		return 1;
    case DRV_DISABLE:		return 1;
    case DRV_QUERYCONFIGURE:	return 1;
1731
    case DRV_CONFIGURE:		MessageBoxA(0, "MCI waveaudio Driver !", "Wine Driver", MB_OK);	return 1;
1732 1733
    case DRV_INSTALL:		return DRVCNF_RESTART;
    case DRV_REMOVE:		return DRVCNF_RESTART;
1734 1735 1736 1737 1738
    }

    if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;

    switch (wMsg) {
1739
    case MCI_OPEN_DRIVER:	return WAVE_mciOpen      (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW)  dwParam2);
1740 1741
    case MCI_CLOSE_DRIVER:	return WAVE_mciClose     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
    case MCI_CUE:		return WAVE_mciCue       (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1742 1743
    case MCI_PLAY:		return WAVE_mciPlay      (dwDevID, dwParam1, dwParam2, NULL);
    case MCI_RECORD:		return WAVE_mciRecord    (dwDevID, dwParam1, dwParam2, NULL);
1744
    case MCI_STOP:		return WAVE_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
1745
    case MCI_SET:		return WAVE_mciSet       (dwDevID, dwParam1, (LPMCI_WAVE_SET_PARMS)    dwParam2);
1746 1747 1748 1749
    case MCI_PAUSE:		return WAVE_mciPause     (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
    case MCI_RESUME:		return WAVE_mciResume    (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
    case MCI_STATUS:		return WAVE_mciStatus    (dwDevID, dwParam1, (LPMCI_STATUS_PARMS)      dwParam2);
    case MCI_GETDEVCAPS:	return WAVE_mciGetDevCaps(dwDevID, dwParam1, (LPMCI_GETDEVCAPS_PARMS)  dwParam2);
1750
    case MCI_INFO:		return WAVE_mciInfo      (dwDevID, dwParam1, (LPMCI_INFO_PARMSW)       dwParam2);
1751
    case MCI_SEEK:		return WAVE_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)        dwParam2);
1752
    case MCI_SAVE:		return WAVE_mciSave	 (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW)       dwParam2);
1753
	/* commands that should be supported */
1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767
    case MCI_LOAD:
    case MCI_FREEZE:
    case MCI_PUT:
    case MCI_REALIZE:
    case MCI_UNFREEZE:
    case MCI_UPDATE:
    case MCI_WHERE:
    case MCI_STEP:
    case MCI_SPIN:
    case MCI_ESCAPE:
    case MCI_COPY:
    case MCI_CUT:
    case MCI_DELETE:
    case MCI_PASTE:
Sven Baars's avatar
Sven Baars committed
1768
	FIXME("Unsupported command [%u]\n", wMsg);
1769
	break;
1770
    case MCI_WINDOW:
1771
	TRACE("Unsupported command [%u]\n", wMsg);
1772
	break;
1773 1774 1775
	/* option which can be silenced */
    case MCI_CONFIGURE:
	return 0;
1776 1777
    case MCI_OPEN:
    case MCI_CLOSE:
1778
	ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1779 1780
	break;
    default:
1781
	FIXME("is probably wrong msg [%u]\n", wMsg);
1782
	return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1783 1784 1785
    }
    return MCIERR_UNRECOGNIZED_COMMAND;
}