mciwave.c 55 KB
Newer Older
1
/*
2
 * Sample 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 8 9 10 11 12 13 14 15 16 17 18 19
 *
 * 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
20
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 22
 */

23 24
#include <stdarg.h>

25
#include "windef.h"
26
#include "winbase.h"
27
#include "wingdi.h"
28
#include "winuser.h"
29
#include "mmddk.h"
30
#include "wownt32.h"
31
#include "digitalv.h"
32
#include "wine/debug.h"
33
#include "wine/unicode.h"
34

35
WINE_DEFAULT_DEBUG_CHANNEL(mciwave);
36

37
typedef struct {
38 39
    UINT			wDevID;
    HANDLE			hWave;
40
    int				nUseCount;	/* Incremented for each shared open */
41
    BOOL			fShareable;	/* TRUE if first open was shareable */
Eric Pouech's avatar
Eric Pouech committed
42
    HMMIO			hFile;  	/* mmio file handle open as Element */
43
    MCI_WAVE_OPEN_PARMSW 	openParms;
44
    WAVEFORMATEX		wfxRef;
45
    LPWAVEFORMATEX		lpWaveFormat;
46
    BOOL			fInput;		/* FALSE = Output, TRUE = Input */
47
    volatile WORD		dwStatus;	/* one from MCI_MODE_xxxx */
48
    DWORD			dwMciTimeFormat;/* One of the supported MCI_FORMAT_xxxx */
49
    DWORD			dwPosition;	/* position in bytes in chunk */
50
    HANDLE			hEvent;		/* for synchronization */
51
    LONG			dwEventCount;	/* for synchronization */
52 53
    MMCKINFO                   	ckMainRIFF;     /* main RIFF chunk */
    MMCKINFO                   	ckWaveData;     /* data chunk */
54 55
} WINE_MCIWAVE;

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

struct SCA {
    UINT 	wDevID;
    UINT 	wMsg;
68 69
    DWORD_PTR   dwParam1;
    DWORD_PTR   dwParam2;
70 71 72 73 74 75 76 77 78 79
};

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

80
    TRACE("In thread before async command (%08x,%u,%08lx,%08lx)\n",
81 82
	  sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
    ret = mciSendCommandA(sca->wDevID, sca->wMsg, sca->dwParam1 | MCI_WAIT, sca->dwParam2);
83
    TRACE("In thread after async command (%08x,%u,%08lx,%08lx)\n",
84 85 86
	  sca->wDevID, sca->wMsg, sca->dwParam1, sca->dwParam2);
    HeapFree(GetProcessHeap(), 0, sca);
    ExitThread(ret);
87
    WARN("Should not happen ? what's wrong\n");
88 89 90 91 92 93 94
    /* should not go after this point */
    return ret;
}

/**************************************************************************
 * 				MCI_SendCommandAsync		[internal]
 */
95 96
static	DWORD MCI_SendCommandAsync(UINT wDevID, UINT wMsg, DWORD_PTR dwParam1,
				   DWORD_PTR dwParam2, UINT size)
97
{
Robert Reif's avatar
Robert Reif committed
98
    HANDLE handle;
99
    struct SCA*	sca = HeapAlloc(GetProcessHeap(), 0, sizeof(struct SCA) + size);
100 101 102 103 104 105 106

    if (sca == 0)
	return MCIERR_OUT_OF_MEMORY;

    sca->wDevID   = wDevID;
    sca->wMsg     = wMsg;
    sca->dwParam1 = dwParam1;
107

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

Robert Reif's avatar
Robert Reif committed
118
    if ((handle = CreateThread(NULL, 0, MCI_SCAStarter, sca, 0, NULL)) == 0) {
119
	WARN("Couldn't allocate thread for async command handling, sending synchronously\n");
120 121
	return MCI_SCAStarter(&sca);
    }
122
    SetThreadPriority(handle, THREAD_PRIORITY_TIME_CRITICAL);
Robert Reif's avatar
Robert Reif committed
123
    CloseHandle(handle);
124 125 126
    return 0;
}

127
/*======================================================================*
128
 *                  	    MCI WAVE implementation			*
129 130
 *======================================================================*/

131
static DWORD WAVE_mciResume(UINT wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms);
132

133
/**************************************************************************
134
 * 				MCIWAVE_drvOpen			[internal]
135
 */
136
static LRESULT WAVE_drvOpen(LPCWSTR str, LPMCI_OPEN_DRIVER_PARMSW modp)
137
{
138 139 140 141 142
    WINE_MCIWAVE*	wmw;

    if (modp == NULL) return 0xFFFFFFFF;

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

144 145 146 147
    if (!wmw)
	return 0;

    wmw->wDevID = modp->wDeviceID;
148
    mciSetDriverData(wmw->wDevID, (DWORD_PTR)wmw);
149
    modp->wCustomCommandTable = MCI_NO_COMMAND_TABLE;
150
    modp->wType = MCI_DEVTYPE_WAVEFORM_AUDIO;
151

152
    wmw->wfxRef.wFormatTag     	= WAVE_FORMAT_PCM;
153 154 155
    wmw->wfxRef.nChannels       = 1;      /* MONO */
    wmw->wfxRef.nSamplesPerSec  = 11025;
    wmw->wfxRef.nAvgBytesPerSec = 11025;
156
    wmw->wfxRef.nBlockAlign     = 1;
157 158 159
    wmw->wfxRef.wBitsPerSample  = 8;
    wmw->wfxRef.cbSize          = 0;      /* don't care */

160
    return modp->wDeviceID;
161 162 163
}

/**************************************************************************
164
 * 				MCIWAVE_drvClose		[internal]
165
 */
166
static LRESULT WAVE_drvClose(MCIDEVICEID dwDevID)
167
{
168
    WINE_MCIWAVE*  wmw = (WINE_MCIWAVE*)mciGetDriverData(dwDevID);
169

170
    if (wmw) {
171
	HeapFree(GetProcessHeap(), 0, wmw);
172
	mciSetDriverData(dwDevID, 0);
173 174
	return 1;
    }
175
    return (dwDevID == 0xFFFFFFFF) ? 1 : 0;
176 177
}

178
/**************************************************************************
179
 * 				WAVE_mciGetOpenDev		[internal]
180
 */
181
static WINE_MCIWAVE *WAVE_mciGetOpenDev(MCIDEVICEID wDevID)
182
{
183
    WINE_MCIWAVE*	wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
184

185
    if (wmw == NULL || wmw->nUseCount == 0) {
186
	WARN("Invalid wDevID=%u\n", wDevID);
187 188
	return 0;
    }
189
    return wmw;
190 191
}

192
/**************************************************************************
193
 * 				WAVE_ConvertByteToTimeFormat	[internal]
194 195
 */
static	DWORD 	WAVE_ConvertByteToTimeFormat(WINE_MCIWAVE* wmw, DWORD val, LPDWORD lpRet)
196
{
197
    DWORD	   ret = 0;
198

199 200
    switch (wmw->dwMciTimeFormat) {
    case MCI_FORMAT_MILLISECONDS:
201
	ret = MulDiv(val,1000,wmw->lpWaveFormat->nAvgBytesPerSec);
202 203 204 205 206
	break;
    case MCI_FORMAT_BYTES:
	ret = val;
	break;
    case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
207
	ret = (val * 8) / (wmw->lpWaveFormat->wBitsPerSample ? wmw->lpWaveFormat->wBitsPerSample : 1);
208 209
	break;
    default:
210
	WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
211
    }
212
    TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
213
    *lpRet = 0;
214 215 216
    return ret;
}

217
/**************************************************************************
218
 * 				WAVE_ConvertTimeFormatToByte	[internal]
219
 */
220 221 222
static	DWORD 	WAVE_ConvertTimeFormatToByte(WINE_MCIWAVE* wmw, DWORD val)
{
    DWORD	ret = 0;
223

224 225
    switch (wmw->dwMciTimeFormat) {
    case MCI_FORMAT_MILLISECONDS:
226
	ret = (val * wmw->lpWaveFormat->nAvgBytesPerSec) / 1000;
227 228 229 230 231
	break;
    case MCI_FORMAT_BYTES:
	ret = val;
	break;
    case MCI_FORMAT_SAMPLES: /* FIXME: is this correct ? */
232
	ret = (val * wmw->lpWaveFormat->wBitsPerSample) / 8;
233 234
	break;
    default:
235
	WARN("Bad time format %u!\n", wmw->dwMciTimeFormat);
236
    }
237
    TRACE("val=%u=0x%08x [tf=%u] => ret=%u\n", val, val, wmw->dwMciTimeFormat, ret);
238 239 240
    return ret;
}

241 242 243
/**************************************************************************
 * 			WAVE_mciReadFmt	                        [internal]
 */
244
static	DWORD WAVE_mciReadFmt(WINE_MCIWAVE* wmw, const MMCKINFO* pckMainRIFF)
245 246
{
    MMCKINFO	mmckInfo;
247 248
    long	r;

249 250 251
    mmckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');
    if (mmioDescend(wmw->hFile, &mmckInfo, pckMainRIFF, MMIO_FINDCHUNK) != 0)
	return MCIERR_INVALID_FILE;
252
    TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
253
	  (LPSTR)&mmckInfo.ckid, (LPSTR)&mmckInfo.fccType, mmckInfo.cksize);
254 255

    wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, mmckInfo.cksize);
Eric Pouech's avatar
Eric Pouech committed
256
    if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
257 258
    r = mmioRead(wmw->hFile, (HPSTR)wmw->lpWaveFormat, mmckInfo.cksize);
    if (r < sizeof(WAVEFORMAT))
259
	return MCIERR_INVALID_FILE;
260

261
    TRACE("wFormatTag=%04X !\n",   wmw->lpWaveFormat->wFormatTag);
262
    TRACE("nChannels=%d\n",       wmw->lpWaveFormat->nChannels);
263 264
    TRACE("nSamplesPerSec=%d\n",  wmw->lpWaveFormat->nSamplesPerSec);
    TRACE("nAvgBytesPerSec=%d\n", wmw->lpWaveFormat->nAvgBytesPerSec);
265
    TRACE("nBlockAlign=%d\n",     wmw->lpWaveFormat->nBlockAlign);
266 267 268
    TRACE("wBitsPerSample=%u !\n", wmw->lpWaveFormat->wBitsPerSample);
    if (r >= (long)sizeof(WAVEFORMATEX))
	TRACE("cbSize=%u !\n", wmw->lpWaveFormat->cbSize);
269

270
    mmioAscend(wmw->hFile, &mmckInfo, 0);
271 272
    wmw->ckWaveData.ckid = mmioFOURCC('d', 'a', 't', 'a');
    if (mmioDescend(wmw->hFile, &wmw->ckWaveData, pckMainRIFF, MMIO_FINDCHUNK) != 0) {
273
	TRACE("can't find data chunk\n");
274
	return MCIERR_INVALID_FILE;
275
    }
276
    TRACE("Chunk Found ckid=%.4s fccType=%.4s cksize=%08X\n",
277
	  (LPSTR)&wmw->ckWaveData.ckid, (LPSTR)&wmw->ckWaveData.fccType, wmw->ckWaveData.cksize);
278
    TRACE("nChannels=%d nSamplesPerSec=%d\n",
279
	  wmw->lpWaveFormat->nChannels, wmw->lpWaveFormat->nSamplesPerSec);
280

281 282 283
    return 0;
}

Eric Pouech's avatar
Eric Pouech committed
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
/**************************************************************************
 * 			WAVE_mciDefaultFmt	                 [internal]
 */
static	DWORD WAVE_mciDefaultFmt(WINE_MCIWAVE* wmw)
{
    wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), 0, sizeof(*wmw->lpWaveFormat));
    if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;

    wmw->lpWaveFormat->wFormatTag = WAVE_FORMAT_PCM;
    wmw->lpWaveFormat->nChannels = 1;
    wmw->lpWaveFormat->nSamplesPerSec = 44000;
    wmw->lpWaveFormat->nAvgBytesPerSec = 44000;
    wmw->lpWaveFormat->nBlockAlign = 1;
    wmw->lpWaveFormat->wBitsPerSample = 8;

    return 0;
}

302 303 304 305 306 307
/**************************************************************************
 * 			WAVE_mciCreateRIFFSkeleton              [internal]
 */
static DWORD WAVE_mciCreateRIFFSkeleton(WINE_MCIWAVE* wmw)
{
   MMCKINFO     ckWaveFormat;
Eric Pouech's avatar
Eric Pouech committed
308 309
   LPMMCKINFO   lpckRIFF     = &(wmw->ckMainRIFF);
   LPMMCKINFO   lpckWaveData = &(wmw->ckWaveData);
310 311 312 313 314

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

Eric Pouech's avatar
Eric Pouech committed
315
   if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckRIFF, MMIO_CREATERIFF))
316 317 318 319
	goto err;

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

Eric Pouech's avatar
Eric Pouech committed
322
   if (!wmw->lpWaveFormat)
323
   {
Eric Pouech's avatar
Eric Pouech committed
324 325
       wmw->lpWaveFormat = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wmw->lpWaveFormat));
       if (!wmw->lpWaveFormat) return MMSYSERR_NOMEM;
326
       *wmw->lpWaveFormat = wmw->wfxRef;
327 328
   }

Eric Pouech's avatar
Eric Pouech committed
329 330 331 332 333 334 335 336
   /* we can only record PCM files... there is no way in the MCI API to specify
    * the necessary data to initialize the extra bytes of the WAVEFORMATEX
    * structure
    */
   if (wmw->lpWaveFormat->wFormatTag != WAVE_FORMAT_PCM)
       goto err;

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

Eric Pouech's avatar
Eric Pouech committed
339
   if (-1 == mmioWrite(wmw->hFile, (HPCSTR)wmw->lpWaveFormat, sizeof(PCMWAVEFORMAT)))
340
	goto err;
341

Eric Pouech's avatar
Eric Pouech committed
342
   if (MMSYSERR_NOERROR != mmioAscend(wmw->hFile, &ckWaveFormat, 0))
343 344 345 346 347
	goto err;

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

349
   /* create data chunk */
Eric Pouech's avatar
Eric Pouech committed
350
   if (MMSYSERR_NOERROR != mmioCreateChunk(wmw->hFile, lpckWaveData, 0))
351 352 353 354
	goto err;

   return 0;

355
err:
356
   HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
Eric Pouech's avatar
Eric Pouech committed
357
   wmw->lpWaveFormat = NULL;
358 359 360
   return MCIERR_INVALID_FILE;
}

Eric Pouech's avatar
Eric Pouech committed
361 362 363 364 365 366 367 368 369 370 371
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';

372
    if (!GetTempPathW(sizeof(szTmpPath)/sizeof(szTmpPath[0]), szTmpPath)) {
Eric Pouech's avatar
Eric Pouech committed
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
        WARN("can't retrieve temp path!\n");
        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;
}

static LRESULT WAVE_mciOpenFile(WINE_MCIWAVE* wmw, const WCHAR* filename)
{
    LRESULT dwRet = MMSYSERR_NOERROR;
    WCHAR* fn;

    fn = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(filename) + 1) * sizeof(WCHAR));
    if (!fn) return MCIERR_OUT_OF_MEMORY;
    strcpyW(fn, filename);
    HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
    wmw->openParms.lpstrElementName = fn;

    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;

            /* make sure we're are the beginning of the file */
            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 {
436
                TRACE("ParentChunk ckid=%.4s fccType=%.4s cksize=%08X\n",
Eric Pouech's avatar
Eric Pouech committed
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452
                      (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;
}

453 454 455
/**************************************************************************
 * 			WAVE_mciOpen	                        [internal]
 */
456
static LRESULT WAVE_mciOpen(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_WAVE_OPEN_PARMSW lpOpenParms)
457
{
458
    DWORD		dwRet = 0;
459
    WINE_MCIWAVE*	wmw = (WINE_MCIWAVE*)mciGetDriverData(wDevID);
460

461
    TRACE("(%04X, %08X, %p)\n", wDevID, dwFlags, lpOpenParms);
462 463 464
    if (lpOpenParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
    if (wmw == NULL) 		return MCIERR_INVALID_DEVICE_ID;

465 466
    if (dwFlags & MCI_OPEN_SHAREABLE)
	return MCIERR_HARDWARE;
467

468
    if (wmw->nUseCount > 0) {
469 470 471 472
	/* The driver is already opened on this channel
	 * Wave driver cannot be shared
	 */
	return MCIERR_DEVICE_OPEN;
473
    }
474

475
    wmw->nUseCount++;
476

477
    wmw->fInput = FALSE;
478
    wmw->hWave = 0;
479
    wmw->dwStatus = MCI_MODE_NOT_READY;
Eric Pouech's avatar
Eric Pouech committed
480 481
    wmw->hFile = 0;
    memcpy(&wmw->openParms, lpOpenParms, sizeof(MCI_WAVE_OPEN_PARMSA));
482 483
    /* will be set by WAVE_mciOpenFile */
    wmw->openParms.lpstrElementName = NULL;
484

485
    TRACE("wDevID=%04X (lpParams->wDeviceID=%08X)\n", wDevID, lpOpenParms->wDeviceID);
486

487
    if (dwFlags & MCI_OPEN_ELEMENT) {
488
	if (dwFlags & MCI_OPEN_ELEMENT_ID) {
489 490
	    /* could it be that (DWORD)lpOpenParms->lpstrElementName
	     * contains the hFile value ?
491 492
	     */
	    dwRet = MCIERR_UNRECOGNIZED_COMMAND;
493
	} else {
Eric Pouech's avatar
Eric Pouech committed
494
            dwRet = WAVE_mciOpenFile(wmw, lpOpenParms->lpstrElementName);
495 496
	}
    }
497

498
    TRACE("hFile=%p\n", wmw->hFile);
499

Eric Pouech's avatar
Eric Pouech committed
500 501
    if (dwRet == 0 && !wmw->lpWaveFormat)
        dwRet = WAVE_mciDefaultFmt(wmw);
502

503
    if (dwRet == 0) {
504 505 506
	if (wmw->lpWaveFormat) {
	    switch (wmw->lpWaveFormat->wFormatTag) {
	    case WAVE_FORMAT_PCM:
507
		if (wmw->lpWaveFormat->nAvgBytesPerSec !=
508
		    wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
509
                    WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
510 511
			wmw->lpWaveFormat->nAvgBytesPerSec,
			wmw->lpWaveFormat->nSamplesPerSec *
512
			 wmw->lpWaveFormat->nBlockAlign);
513 514
		    wmw->lpWaveFormat->nAvgBytesPerSec =
			wmw->lpWaveFormat->nSamplesPerSec *
515 516 517 518 519
			wmw->lpWaveFormat->nBlockAlign;
		}
		break;
	    }
	}
520
	wmw->dwPosition = 0;
521

522
	wmw->dwStatus = MCI_MODE_STOP;
523 524 525
    } else {
	wmw->nUseCount--;
	if (wmw->hFile != 0)
526
	    mmioClose(wmw->hFile, 0);
527 528
	wmw->hFile = 0;
    }
Eric Pouech's avatar
Eric Pouech committed
529
    return dwRet;
530 531 532 533 534
}

/**************************************************************************
 *                               WAVE_mciCue             [internal]
 */
535
static DWORD WAVE_mciCue(MCIDEVICEID wDevID, LPARAM dwParam, LPMCI_GENERIC_PARMS lpParms)
536 537 538
{
    /*
      FIXME
539

540 541 542
      This routine is far from complete. At the moment only a check is done on the
      MCI_WAVE_INPUT flag. No explicit check on MCI_WAVE_OUTPUT is done since that
      is the default.
543

544 545 546
      The flags MCI_NOTIFY (and the callback parameter in lpParms) and MCI_WAIT
      are ignored
    */
547

548 549
    DWORD		dwRet;
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
550

551
    FIXME("(%u, %08lX, %p); likely to fail\n", wDevID, dwParam, lpParms);
552

553
    if (wmw == NULL)	return MCIERR_INVALID_DEVICE_ID;
554

Eric Pouech's avatar
Eric Pouech committed
555
    /* FIXME */
556
    /* always close elements ? */
557
    if (wmw->hFile != 0) {
558
	mmioClose(wmw->hFile, 0);
559 560
	wmw->hFile = 0;
    }
561

562
    dwRet = MMSYSERR_NOERROR;  /* assume success */
563

564
    if ((dwParam & MCI_WAVE_INPUT) && !wmw->fInput) {
565
	dwRet = waveOutClose(wmw->hWave);
566 567 568
	if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
	wmw->fInput = TRUE;
    } else if (wmw->fInput) {
569
	dwRet = waveInClose(wmw->hWave);
570 571 572
	if (dwRet != MMSYSERR_NOERROR) return MCIERR_INTERNAL;
	wmw->fInput = FALSE;
    }
573
    wmw->hWave = 0;
574 575 576 577 578 579
    return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
}

/**************************************************************************
 * 				WAVE_mciStop			[internal]
 */
580
static DWORD WAVE_mciStop(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
581
{
582
    DWORD 		dwRet = 0;
583
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
584

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

587
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
588

589 590 591 592 593 594 595 596 597 598 599
    /* 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);
	}
600
	while (wmw->dwStatus != MCI_MODE_STOP)
601 602 603 604
	    Sleep(10);
	break;
    }

605
    wmw->dwPosition = 0;
606 607 608

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

610
    if ((dwFlags & MCI_NOTIFY) && lpParms) {
611
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
612
			wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
613
    }
614

615
    return dwRet;
616 617 618 619 620
}

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

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

628
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
629

630
    if (wmw->dwStatus != MCI_MODE_STOP) {
631
	dwRet = WAVE_mciStop(wDevID, MCI_WAIT, lpParms);
632
    }
633

634
    wmw->nUseCount--;
635

636 637
    if (wmw->nUseCount == 0) {
	if (wmw->hFile != 0) {
638
	    mmioClose(wmw->hFile, 0);
639 640 641
	    wmw->hFile = 0;
	}
    }
642

643 644
    HeapFree(GetProcessHeap(), 0, wmw->lpWaveFormat);
    wmw->lpWaveFormat = NULL;
Eric Pouech's avatar
Eric Pouech committed
645 646
    HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
    wmw->openParms.lpstrElementName = NULL;
647

Eric Pouech's avatar
Eric Pouech committed
648
    if ((dwFlags & MCI_NOTIFY) && lpParms) {
649
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
650
			wmw->openParms.wDeviceID,
651
			(dwRet == 0) ? MCI_NOTIFY_SUCCESSFUL : MCI_NOTIFY_FAILURE);
652
    }
653

654 655 656
    return 0;
}

657 658 659
/**************************************************************************
 * 				WAVE_mciPlayCallback		[internal]
 */
660
static	void	CALLBACK WAVE_mciPlayCallback(HWAVEOUT hwo, UINT uMsg,
661 662
					      DWORD_PTR dwInstance,
					      LPARAM dwParam1, LPARAM dwParam2)
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
{
    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
680 681 682 683 684
/******************************************************************
 *		WAVE_mciPlayWaitDone
 *
 *
 */
685 686 687 688 689 690 691 692
static void WAVE_mciPlayWaitDone(WINE_MCIWAVE* wmw)
{
    for (;;) {
	ResetEvent(wmw->hEvent);
	if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
	    break;
	}
	InterlockedIncrement(&wmw->dwEventCount);
693

694 695 696 697
	WaitForSingleObject(wmw->hEvent, INFINITE);
    }
}

698 699 700
/**************************************************************************
 * 				WAVE_mciPlay		[internal]
 */
701
static DWORD WAVE_mciPlay(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_PLAY_PARMS lpParms)
702 703
{
    DWORD		end;
704
    LONG		bufsize, count, left;
705 706
    DWORD		dwRet = 0;
    LPWAVEHDR		waveHdr = NULL;
707
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
708
    int			whidx;
709

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

712 713
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
714 715 716

    wmw->fInput = FALSE;

717
    if (wmw->hFile == 0) {
718
	WARN("Can't play: no file=%s!\n", debugstr_w(wmw->openParms.lpstrElementName));
719 720
	return MCIERR_FILE_NOT_FOUND;
    }
721

722 723 724 725
    if (wmw->dwStatus == MCI_MODE_PAUSE) {
	/* FIXME: parameters (start/end) in lpParams may not be used */
	return WAVE_mciResume(wDevID, dwFlags, (LPMCI_GENERIC_PARMS)lpParms);
    }
726

727 728 729 730 731
    /** 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.
     */
    if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_PLAY) && (dwFlags & MCI_WAIT))) {
732 733 734
	return MCIERR_INTERNAL;
    }

735
    wmw->dwStatus = MCI_MODE_PLAY;
736

737
    if (!(dwFlags & MCI_WAIT)) {
738
	return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_PLAY, dwFlags,
739
				    (DWORD_PTR)lpParms, sizeof(MCI_PLAY_PARMS));
740 741
    }

742 743
    end = 0xFFFFFFFF;
    if (lpParms && (dwFlags & MCI_FROM)) {
744
	wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
745 746 747 748
    }
    if (lpParms && (dwFlags & MCI_TO)) {
	end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
    }
749

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

752 753 754
    if (end <= wmw->dwPosition)
	return TRUE;

755

756 757 758
#define	WAVE_ALIGN_ON_BLOCK(wmw,v) \
((((v) + (wmw)->lpWaveFormat->nBlockAlign - 1) / (wmw)->lpWaveFormat->nBlockAlign) * (wmw)->lpWaveFormat->nBlockAlign)

759 760 761 762 763 764 765
    wmw->dwPosition        = WAVE_ALIGN_ON_BLOCK(wmw, wmw->dwPosition);
    wmw->ckWaveData.cksize = WAVE_ALIGN_ON_BLOCK(wmw, wmw->ckWaveData.cksize);

    if (dwRet == 0) {
	if (wmw->lpWaveFormat) {
	    switch (wmw->lpWaveFormat->wFormatTag) {
	    case WAVE_FORMAT_PCM:
766
		if (wmw->lpWaveFormat->nAvgBytesPerSec !=
767
		    wmw->lpWaveFormat->nSamplesPerSec * wmw->lpWaveFormat->nBlockAlign) {
768
                    WARN("Incorrect nAvgBytesPerSec (%d), setting it to %d\n",
769 770
			wmw->lpWaveFormat->nAvgBytesPerSec,
			wmw->lpWaveFormat->nSamplesPerSec *
771
			 wmw->lpWaveFormat->nBlockAlign);
772 773
		    wmw->lpWaveFormat->nAvgBytesPerSec =
			wmw->lpWaveFormat->nSamplesPerSec *
774 775 776 777 778 779
			wmw->lpWaveFormat->nBlockAlign;
		}
		break;
	    }
	}
    } else {
780
	TRACE("can't retrieve wave format %d\n", dwRet);
781 782
	goto cleanUp;
    }
783 784


785
    /* go back to beginning of chunk plus the requested position */
786
    /* FIXME: I'm not sure this is correct, notably because some data linked to
Austin English's avatar
Austin English committed
787
     * the decompression state machine will not be correctly initialized.
788 789 790
     * 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)
     */
791
    mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */
792

Eric Pouech's avatar
Eric Pouech committed
793 794 795
    /* By default the device will be opened for output, the MCI_CUE function is there to
     * change from output to input and back
     */
796
    /* FIXME: how to choose between several output channels ? here mapper is forced */
797
    dwRet = waveOutOpen((HWAVEOUT *)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
798
			(DWORD_PTR)WAVE_mciPlayCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
799

Eric Pouech's avatar
Eric Pouech committed
800
    if (dwRet != 0) {
801
	TRACE("Can't open low level audio device %d\n", dwRet);
802 803 804
	dwRet = MCIERR_DEVICE_OPEN;
	wmw->hWave = 0;
	goto cleanUp;
Eric Pouech's avatar
Eric Pouech committed
805
    }
806

807 808 809 810 811 812
    /* 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;
813 814 815 816
    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;
817
    if (waveOutPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
818 819 820 821
	waveOutPrepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR))) {
	dwRet = MCIERR_INTERNAL;
	goto cleanUp;
    }
822 823

    whidx = 0;
824
    left = min(wmw->ckWaveData.cksize, end - wmw->dwPosition);
825
    wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
826
    wmw->dwEventCount = 1L; /* for first buffer */
827

828
    TRACE("Playing (normalized) from byte=%u for %u bytes\n", wmw->dwPosition, left);
829

830
    /* FIXME: this doesn't work if wmw->dwPosition != 0 */
831
    while (left > 0 && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
832
	count = mmioRead(wmw->hFile, waveHdr[whidx].lpData, min(bufsize, left));
833
	TRACE("mmioRead bufsize=%d count=%d\n", bufsize, count);
834
	if (count < 1)
Eric Pouech's avatar
Eric Pouech committed
835
	    break;
836 837
	/* count is always <= bufsize, so this is correct regarding the
	 * waveOutPrepareHeader function
838
	 */
839
	waveHdr[whidx].dwBufferLength = count;
840
	waveHdr[whidx].dwFlags &= ~WHDR_DONE;
841
	TRACE("before WODM_WRITE lpWaveHdr=%p dwBufferLength=%u dwBytesRecorded=%u\n",
842
	      &waveHdr[whidx], waveHdr[whidx].dwBufferLength,
843 844
	      waveHdr[whidx].dwBytesRecorded);
	dwRet = waveOutWrite(wmw->hWave, &waveHdr[whidx], sizeof(WAVEHDR));
845
	left -= count;
846
	wmw->dwPosition += count;
847
	TRACE("after WODM_WRITE dwPosition=%u\n", wmw->dwPosition);
848

849
	WAVE_mciPlayWaitDone(wmw);
850
	whidx ^= 1;
851
    }
Eric Pouech's avatar
Eric Pouech committed
852

853 854 855
    WAVE_mciPlayWaitDone(wmw); /* to balance first buffer */

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

858 859 860 861 862
    waveOutUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
    waveOutUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));

    dwRet = 0;

863
cleanUp:
864 865 866 867 868 869 870 871
    HeapFree(GetProcessHeap(), 0, waveHdr);

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

872
    if (lpParms && (dwFlags & MCI_NOTIFY)) {
873
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
874
			wmw->openParms.wDeviceID,
875
			dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
876
    }
877 878 879 880

    wmw->dwStatus = MCI_MODE_STOP;

    return dwRet;
881 882
}

883 884 885
/**************************************************************************
 * 				WAVE_mciPlayCallback		[internal]
 */
886
static	void	CALLBACK WAVE_mciRecordCallback(HWAVEOUT hwo, UINT uMsg,
887 888
                                                DWORD_PTR dwInstance,
                                                LPARAM dwParam1, LPARAM dwParam2)
889 890
{
    WINE_MCIWAVE*	wmw = (WINE_MCIWAVE*)dwInstance;
Eric Pouech's avatar
Eric Pouech committed
891
    LPWAVEHDR           lpWaveHdr;
892
    LONG                count = 0;
Eric Pouech's avatar
Eric Pouech committed
893

894 895 896 897 898 899 900 901 902 903 904 905
    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
906 907 908
        if (count > 0)
            wmw->dwPosition  += count;
        /* else error reporting ?? */
909 910 911 912 913
        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
914
              to fail. */
915
           waveInAddBuffer(wmw->hWave, lpWaveHdr, sizeof(*lpWaveHdr));
916
           TRACE("after mmioWrite dwPosition=%u\n", wmw->dwPosition);
917
        }
918

919 920 921 922 923 924 925
	SetEvent(wmw->hEvent);
	break;
    default:
	ERR("Unknown uMsg=%d\n", uMsg);
    }
}

Eric Pouech's avatar
Eric Pouech committed
926 927 928 929
/******************************************************************
 *		bWAVE_mciRecordWaitDone
 *
 */
930 931 932 933 934 935 936 937
static void WAVE_mciRecordWaitDone(WINE_MCIWAVE* wmw)
{
    for (;;) {
	ResetEvent(wmw->hEvent);
	if (InterlockedDecrement(&wmw->dwEventCount) >= 0) {
	    break;
	}
	InterlockedIncrement(&wmw->dwEventCount);
938

939 940 941 942
	WaitForSingleObject(wmw->hEvent, INFINITE);
    }
}

943 944 945
/**************************************************************************
 * 				WAVE_mciRecord			[internal]
 */
946
static DWORD WAVE_mciRecord(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_RECORD_PARMS lpParms)
947
{
948
    DWORD		end;
Eric Pouech's avatar
Eric Pouech committed
949
    DWORD		dwRet = MMSYSERR_NOERROR;
950
    LONG		bufsize;
951
    LPWAVEHDR		waveHdr = NULL;
952
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
953

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

956 957
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
958 959

    /* FIXME : since there is no way to determine in which mode the device is
960 961 962 963 964 965 966 967
     * open (recording/playback) automatically switch from a mode to another
     */
    wmw->fInput = TRUE;

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

969 970 971 972 973 974
    /** 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.
     */
    if ((wmw->dwStatus != MCI_MODE_STOP) && ((wmw->dwStatus != MCI_MODE_RECORD) && (dwFlags & MCI_WAIT))) {
	return MCIERR_INTERNAL;
975
    }
976

977
    wmw->dwStatus = MCI_MODE_RECORD;
978 979 980

    if (!(dwFlags & MCI_WAIT)) {
	return MCI_SendCommandAsync(wmw->openParms.wDeviceID, MCI_RECORD, dwFlags,
981
				    (DWORD_PTR)lpParms, sizeof(MCI_RECORD_PARMS));
982 983
    }

Eric Pouech's avatar
Eric Pouech committed
984 985 986 987 988 989 990 991 992 993 994
    /* 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)
     */
    HeapFree(GetProcessHeap(), 0, (void*)wmw->openParms.lpstrElementName);
    dwRet = create_tmp_file(&wmw->hFile, (WCHAR**)&wmw->openParms.lpstrElementName);
    if (dwRet != 0) return dwRet;

    /* new RIFF file */
    dwRet = WAVE_mciCreateRIFFSkeleton(wmw);
    if (dwRet != 0) return dwRet; /* FIXME: we leak resources */
995

996 997
    end = 0xFFFFFFFF;
    if (lpParms && (dwFlags & MCI_FROM)) {
998
	wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwFrom);
999 1000 1001 1002 1003 1004
    }

    if (lpParms && (dwFlags & MCI_TO)) {
	end = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
    }

1005
    TRACE("Recording from byte=%u to byte=%u\n", wmw->dwPosition, end);
1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017

    if (end <= wmw->dwPosition)
    {
	return TRUE;
    }

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

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

Austin English's avatar
Austin English committed
1018
    /* Go back to the beginning of the chunk plus the requested position */
1019
    /* FIXME: I'm not sure this is correct, notably because some data linked to
Austin English's avatar
Austin English committed
1020 1021 1022
     * 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).
1023 1024 1025 1026 1027 1028 1029
     */
    mmioSeek(wmw->hFile, wmw->ckWaveData.dwDataOffset + wmw->dwPosition, SEEK_SET); /* >= 0 */

    /* By default the device will be opened for output, the MCI_CUE function is there to
     * change from output to input and back
     */
    /* FIXME: how to choose between several output channels ? here mapper is forced */
1030
    dwRet = waveInOpen((HWAVEIN*)&wmw->hWave, WAVE_MAPPER, wmw->lpWaveFormat,
1031
			(DWORD_PTR)WAVE_mciRecordCallback, (DWORD_PTR)wmw, CALLBACK_FUNCTION);
1032

Eric Pouech's avatar
Eric Pouech committed
1033
    if (dwRet != MMSYSERR_NOERROR) {
1034
	TRACE("Can't open low level audio device %d\n", dwRet);
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050
	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;

1051
    if (waveInPrepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR)) ||
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
	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;
    }
1062

1063
    wmw->hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
1064 1065
    wmw->dwEventCount = 1L; /* for first buffer */

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

1068 1069
    dwRet = waveInStart(wmw->hWave);

Eric Pouech's avatar
Eric Pouech committed
1070
    while (wmw->dwPosition < end && wmw->dwStatus != MCI_MODE_STOP && wmw->dwStatus != MCI_MODE_NOT_READY) {
1071
	WAVE_mciRecordWaitDone(wmw);
1072
    }
1073 1074 1075

    /* needed so that the callback above won't add again the buffers returned by the reset */
    wmw->dwStatus = MCI_MODE_STOP;
1076

1077
    waveInReset(wmw->hWave);
1078

1079 1080 1081 1082 1083
    waveInUnprepareHeader(wmw->hWave, &waveHdr[0], sizeof(WAVEHDR));
    waveInUnprepareHeader(wmw->hWave, &waveHdr[1], sizeof(WAVEHDR));

    dwRet = 0;

1084
cleanUp:
1085 1086 1087 1088 1089 1090 1091 1092 1093
    HeapFree(GetProcessHeap(), 0, waveHdr);

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

    if (lpParms && (dwFlags & MCI_NOTIFY)) {
1094
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1095
			wmw->openParms.wDeviceID,
1096
			dwRet ? MCI_NOTIFY_FAILURE : MCI_NOTIFY_SUCCESSFUL);
1097
    }
1098 1099 1100 1101 1102

    wmw->dwStatus = MCI_MODE_STOP;

    return dwRet;

1103 1104 1105 1106 1107
}

/**************************************************************************
 * 				WAVE_mciPause			[internal]
 */
1108
static DWORD WAVE_mciPause(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1109 1110 1111
{
    DWORD 		dwRet;
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1112

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

1115
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
1116
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
1117

1118 1119
    if (wmw->dwStatus == MCI_MODE_PLAY) {
	wmw->dwStatus = MCI_MODE_PAUSE;
1120 1121
    }

1122 1123
    if (wmw->fInput)	dwRet = waveInStop(wmw->hWave);
    else		dwRet = waveOutPause(wmw->hWave);
1124

1125 1126 1127 1128 1129 1130
    return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
}

/**************************************************************************
 * 				WAVE_mciResume			[internal]
 */
1131
static DWORD WAVE_mciResume(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_GENERIC_PARMS lpParms)
1132 1133 1134
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
    DWORD		dwRet = 0;
1135

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

1138
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
1139

1140 1141
    if (wmw->dwStatus == MCI_MODE_PAUSE) {
	wmw->dwStatus = MCI_MODE_PLAY;
1142 1143
    }

1144 1145
    if (wmw->fInput)	dwRet = waveInStart(wmw->hWave);
    else		dwRet = waveOutRestart(wmw->hWave);
1146 1147 1148 1149 1150 1151
    return (dwRet == MMSYSERR_NOERROR) ? 0 : MCIERR_INTERNAL;
}

/**************************************************************************
 * 				WAVE_mciSeek			[internal]
 */
1152
static DWORD WAVE_mciSeek(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SEEK_PARMS lpParms)
1153 1154 1155
{
    DWORD		ret = 0;
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1156

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

1159 1160 1161 1162 1163 1164
    if (lpParms == NULL) {
	ret = MCIERR_NULL_PARAMETER_BLOCK;
    } else if (wmw == NULL) {
	ret = MCIERR_INVALID_DEVICE_ID;
    } else {
	WAVE_mciStop(wDevID, MCI_WAIT, 0);
1165

1166 1167 1168
	if (dwFlags & MCI_SEEK_TO_START) {
	    wmw->dwPosition = 0;
	} else if (dwFlags & MCI_SEEK_TO_END) {
1169
	    wmw->dwPosition = wmw->ckWaveData.cksize;
1170 1171 1172
	} else if (dwFlags & MCI_TO) {
	    wmw->dwPosition = WAVE_ConvertTimeFormatToByte(wmw, lpParms->dwTo);
	} else {
1173
	    WARN("dwFlag doesn't tell where to seek to...\n");
1174 1175
	    return MCIERR_MISSING_PARAMETER;
	}
1176

1177
	TRACE("Seeking to position=%u bytes\n", wmw->dwPosition);
1178

1179
	if (dwFlags & MCI_NOTIFY) {
1180
	    mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1181
			    wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1182 1183
	}
    }
1184 1185
    return ret;
}
1186 1187 1188 1189

/**************************************************************************
 * 				WAVE_mciSet			[internal]
 */
1190
static DWORD WAVE_mciSet(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SET_PARMS lpParms)
1191 1192
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1193

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

1196
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
1197
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
1198

1199 1200 1201
    if (dwFlags & MCI_SET_TIME_FORMAT) {
	switch (lpParms->dwTimeFormat) {
	case MCI_FORMAT_MILLISECONDS:
1202
	    TRACE("MCI_FORMAT_MILLISECONDS !\n");
1203 1204 1205
	    wmw->dwMciTimeFormat = MCI_FORMAT_MILLISECONDS;
	    break;
	case MCI_FORMAT_BYTES:
1206
	    TRACE("MCI_FORMAT_BYTES !\n");
1207 1208 1209
	    wmw->dwMciTimeFormat = MCI_FORMAT_BYTES;
	    break;
	case MCI_FORMAT_SAMPLES:
1210
	    TRACE("MCI_FORMAT_SAMPLES !\n");
1211 1212 1213
	    wmw->dwMciTimeFormat = MCI_FORMAT_SAMPLES;
	    break;
	default:
1214
            WARN("Bad time format %u!\n", lpParms->dwTimeFormat);
1215 1216 1217 1218
	    return MCIERR_BAD_TIME_FORMAT;
	}
    }
    if (dwFlags & MCI_SET_VIDEO) {
1219
	TRACE("No support for video !\n");
1220 1221 1222
	return MCIERR_UNSUPPORTED_FUNCTION;
    }
    if (dwFlags & MCI_SET_DOOR_OPEN) {
1223
	TRACE("No support for door open !\n");
1224 1225 1226
	return MCIERR_UNSUPPORTED_FUNCTION;
    }
    if (dwFlags & MCI_SET_DOOR_CLOSED) {
1227
	TRACE("No support for door close !\n");
1228 1229 1230 1231
	return MCIERR_UNSUPPORTED_FUNCTION;
    }
    if (dwFlags & MCI_SET_AUDIO) {
	if (dwFlags & MCI_SET_ON) {
1232
	    TRACE("MCI_SET_ON audio !\n");
1233
	} else if (dwFlags & MCI_SET_OFF) {
1234
	    TRACE("MCI_SET_OFF audio !\n");
1235
	} else {
1236
	    WARN("MCI_SET_AUDIO without SET_ON or SET_OFF\n");
1237 1238
	    return MCIERR_BAD_INTEGER;
	}
1239

1240 1241 1242 1243 1244
	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;
1245
        default:                        WARN("Unknown audio channel %u\n", lpParms->dwAudio); break;
1246
        }
1247
    }
1248
    if (dwFlags & MCI_WAVE_INPUT)
1249
	TRACE("MCI_WAVE_INPUT !\n");
1250
    if (dwFlags & MCI_WAVE_OUTPUT)
1251
	TRACE("MCI_WAVE_OUTPUT !\n");
1252
    if (dwFlags & MCI_WAVE_SET_ANYINPUT)
1253
	TRACE("MCI_WAVE_SET_ANYINPUT !\n");
1254
    if (dwFlags & MCI_WAVE_SET_ANYOUTPUT)
1255
	TRACE("MCI_WAVE_SET_ANYOUTPUT !\n");
1256 1257
    if (dwFlags & MCI_WAVE_SET_AVGBYTESPERSEC) {
	wmw->wfxRef.nAvgBytesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nAvgBytesPerSec;
1258
	TRACE("MCI_WAVE_SET_AVGBYTESPERSEC = %d\n", wmw->wfxRef.nAvgBytesPerSec);
1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271
    }
    if (dwFlags & MCI_WAVE_SET_BITSPERSAMPLE) {
	wmw->wfxRef.wBitsPerSample = ((LPMCI_WAVE_SET_PARMS)lpParms)->wBitsPerSample;
	TRACE("MCI_WAVE_SET_BITSPERSAMPLE = %d\n", wmw->wfxRef.wBitsPerSample);
    }
    if (dwFlags & MCI_WAVE_SET_BLOCKALIGN) {
	wmw->wfxRef.nBlockAlign = ((LPMCI_WAVE_SET_PARMS)lpParms)->nBlockAlign;
	TRACE("MCI_WAVE_SET_BLOCKALIGN = %d\n", wmw->wfxRef.nBlockAlign);
    }
    if (dwFlags & MCI_WAVE_SET_CHANNELS) {
	wmw->wfxRef.nChannels = ((LPMCI_WAVE_SET_PARMS)lpParms)->nChannels;
	TRACE("MCI_WAVE_SET_CHANNELS = %d\n", wmw->wfxRef.nChannels);
    }
1272
    if (dwFlags & MCI_WAVE_SET_FORMATTAG) {
1273 1274 1275 1276 1277
	wmw->wfxRef.wFormatTag = ((LPMCI_WAVE_SET_PARMS)lpParms)->wFormatTag;
	TRACE("MCI_WAVE_SET_FORMATTAG = %d\n", wmw->wfxRef.wFormatTag);
    }
    if (dwFlags & MCI_WAVE_SET_SAMPLESPERSEC) {
	wmw->wfxRef.nSamplesPerSec = ((LPMCI_WAVE_SET_PARMS)lpParms)->nSamplesPerSec;
1278
	TRACE("MCI_WAVE_SET_SAMPLESPERSEC = %d\n", wmw->wfxRef.nSamplesPerSec);
1279
    }
1280 1281 1282
    return 0;
}

1283 1284 1285
/**************************************************************************
 *				WAVE_mciSave		[internal]
 */
1286
static DWORD WAVE_mciSave(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_SAVE_PARMSW lpParms)
1287 1288
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1289
    DWORD		ret = MCIERR_FILE_NOT_SAVED, tmpRet;
1290
    WPARAM           	wparam = MCI_NOTIFY_FAILURE;
1291

1292
    TRACE("%d, %08X, %p);\n", wDevID, dwFlags, lpParms);
1293 1294 1295 1296 1297 1298 1299
    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
1300
    WAVE_mciStop(wDevID, 0, NULL);
1301 1302 1303 1304 1305

    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
1306
    wmw->hFile = 0;
1307

1308 1309 1310 1311 1312 1313 1314 1315 1316 1317
    /*
      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();
1318
    DeleteFileW (lpParms->lpfilename);
1319
    SetLastError(tmpRet);
1320

1321
    if (0 == mmioRenameW(wmw->openParms.lpstrElementName, lpParms->lpfilename, 0, 0 )) {
Eric Pouech's avatar
Eric Pouech committed
1322
	ret = MMSYSERR_NOERROR;
1323
    }
1324

1325
    if (dwFlags & MCI_NOTIFY) {
Eric Pouech's avatar
Eric Pouech committed
1326
	if (ret == MMSYSERR_NOERROR) wparam = MCI_NOTIFY_SUCCESSFUL;
1327

1328
    	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1329
			 wmw->openParms.wDeviceID, wparam);
1330 1331
    }

Eric Pouech's avatar
Eric Pouech committed
1332 1333 1334
    if (ret == MMSYSERR_NOERROR)
        ret = WAVE_mciOpenFile(wmw, lpParms->lpfilename);

1335 1336 1337
    return ret;
}

1338 1339 1340
/**************************************************************************
 * 				WAVE_mciStatus		[internal]
 */
1341
static DWORD WAVE_mciStatus(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_STATUS_PARMS lpParms)
1342 1343
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1344
    DWORD		ret = 0;
1345

1346
    TRACE("(%u, %08X, %p);\n", wDevID, dwFlags, lpParms);
1347
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
1348
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
1349

1350
    if (dwFlags & MCI_STATUS_ITEM) {
1351
	switch (lpParms->dwItem) {
1352 1353
	case MCI_STATUS_CURRENT_TRACK:
	    lpParms->dwReturn = 1;
1354
            TRACE("MCI_STATUS_CURRENT_TRACK => %lu\n", lpParms->dwReturn);
1355 1356
	    break;
	case MCI_STATUS_LENGTH:
1357 1358 1359 1360
	    if (!wmw->hFile) {
		lpParms->dwReturn = 0;
		return MCIERR_UNSUPPORTED_FUNCTION;
	    }
1361
	    /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1362
	    lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw, wmw->ckWaveData.cksize, &ret);
1363
            TRACE("MCI_STATUS_LENGTH => %lu\n", lpParms->dwReturn);
1364 1365
	    break;
	case MCI_STATUS_MODE:
1366 1367 1368
	    TRACE("MCI_STATUS_MODE => %u\n", wmw->dwStatus);
	    lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwStatus, wmw->dwStatus);
	    ret = MCI_RESOURCE_RETURNED;
1369 1370
	    break;
	case MCI_STATUS_MEDIA_PRESENT:
1371
	    TRACE("MCI_STATUS_MEDIA_PRESENT => TRUE!\n");
1372 1373
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1374 1375 1376 1377
	    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;
1378
            TRACE("MCI_STATUS_NUMBER_OF_TRACKS => %lu\n", lpParms->dwReturn);
1379 1380
	    break;
	case MCI_STATUS_POSITION:
1381 1382 1383 1384
	    if (!wmw->hFile) {
		lpParms->dwReturn = 0;
		return MCIERR_UNSUPPORTED_FUNCTION;
	    }
1385
	    /* only one track in file is currently handled, so don't take care of MCI_TRACK flag */
1386
	    lpParms->dwReturn = WAVE_ConvertByteToTimeFormat(wmw,
1387 1388
							     (dwFlags & MCI_STATUS_START) ? 0 : wmw->dwPosition,
							     &ret);
1389
            TRACE("MCI_STATUS_POSITION %s => %lu\n",
1390 1391 1392
		  (dwFlags & MCI_STATUS_START) ? "start" : "current", lpParms->dwReturn);
	    break;
	case MCI_STATUS_READY:
1393 1394 1395 1396
	    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;
1397 1398
	    break;
	case MCI_STATUS_TIME_FORMAT:
1399
	    lpParms->dwReturn = MAKEMCIRESOURCE(wmw->dwMciTimeFormat, MCI_FORMAT_RETURN_BASE + wmw->dwMciTimeFormat);
1400
            TRACE("MCI_STATUS_TIME_FORMAT => %lu\n", lpParms->dwReturn);
1401
	    ret = MCI_RESOURCE_RETURNED;
1402 1403
	    break;
	case MCI_WAVE_INPUT:
1404
	    TRACE("MCI_WAVE_INPUT !\n");
1405
	    lpParms->dwReturn = 0;
1406
	    ret = MCIERR_WAVE_INPUTUNSPECIFIED;
1407 1408
	    break;
	case MCI_WAVE_OUTPUT:
1409
	    TRACE("MCI_WAVE_OUTPUT !\n");
1410 1411 1412 1413 1414 1415 1416 1417 1418
	    {
		UINT	id;
		if (waveOutGetID(wmw->hWave, &id) == MMSYSERR_NOERROR) {
		    lpParms->dwReturn = id;
		} else {
		    lpParms->dwReturn = 0;
		    ret = MCIERR_WAVE_INPUTUNSPECIFIED;
		}
	    }
1419 1420
	    break;
	case MCI_WAVE_STATUS_AVGBYTESPERSEC:
1421 1422 1423 1424 1425
	    if (!wmw->hFile) {
		lpParms->dwReturn = 0;
		return MCIERR_UNSUPPORTED_FUNCTION;
	    }
	    lpParms->dwReturn = wmw->lpWaveFormat->nAvgBytesPerSec;
1426
            TRACE("MCI_WAVE_STATUS_AVGBYTESPERSEC => %lu\n", lpParms->dwReturn);
1427 1428
	    break;
	case MCI_WAVE_STATUS_BITSPERSAMPLE:
1429 1430 1431 1432 1433
	    if (!wmw->hFile) {
		lpParms->dwReturn = 0;
		return MCIERR_UNSUPPORTED_FUNCTION;
	    }
	    lpParms->dwReturn = wmw->lpWaveFormat->wBitsPerSample;
1434
            TRACE("MCI_WAVE_STATUS_BITSPERSAMPLE => %lu\n", lpParms->dwReturn);
1435 1436
	    break;
	case MCI_WAVE_STATUS_BLOCKALIGN:
1437 1438 1439 1440 1441
	    if (!wmw->hFile) {
		lpParms->dwReturn = 0;
		return MCIERR_UNSUPPORTED_FUNCTION;
	    }
	    lpParms->dwReturn = wmw->lpWaveFormat->nBlockAlign;
1442
            TRACE("MCI_WAVE_STATUS_BLOCKALIGN => %lu\n", lpParms->dwReturn);
1443 1444
	    break;
	case MCI_WAVE_STATUS_CHANNELS:
1445 1446 1447 1448 1449
	    if (!wmw->hFile) {
		lpParms->dwReturn = 0;
		return MCIERR_UNSUPPORTED_FUNCTION;
	    }
	    lpParms->dwReturn = wmw->lpWaveFormat->nChannels;
1450
            TRACE("MCI_WAVE_STATUS_CHANNELS => %lu\n", lpParms->dwReturn);
1451 1452
	    break;
	case MCI_WAVE_STATUS_FORMATTAG:
1453 1454 1455 1456 1457
	    if (!wmw->hFile) {
		lpParms->dwReturn = 0;
		return MCIERR_UNSUPPORTED_FUNCTION;
	    }
	    lpParms->dwReturn = wmw->lpWaveFormat->wFormatTag;
1458
            TRACE("MCI_WAVE_FORMATTAG => %lu\n", lpParms->dwReturn);
1459 1460
	    break;
	case MCI_WAVE_STATUS_LEVEL:
1461
	    TRACE("MCI_WAVE_STATUS_LEVEL !\n");
1462 1463 1464
	    lpParms->dwReturn = 0xAAAA5555;
	    break;
	case MCI_WAVE_STATUS_SAMPLESPERSEC:
1465 1466 1467 1468 1469
	    if (!wmw->hFile) {
		lpParms->dwReturn = 0;
		return MCIERR_UNSUPPORTED_FUNCTION;
	    }
	    lpParms->dwReturn = wmw->lpWaveFormat->nSamplesPerSec;
1470
            TRACE("MCI_WAVE_STATUS_SAMPLESPERSEC => %lu\n", lpParms->dwReturn);
1471 1472
	    break;
	default:
1473
            WARN("unknown command %08X !\n", lpParms->dwItem);
1474 1475 1476 1477
	    return MCIERR_UNRECOGNIZED_COMMAND;
	}
    }
    if (dwFlags & MCI_NOTIFY) {
1478
	mciDriverNotify(HWND_32(LOWORD(lpParms->dwCallback)),
1479
			wmw->openParms.wDeviceID, MCI_NOTIFY_SUCCESSFUL);
1480
    }
1481
    return ret;
1482 1483 1484 1485 1486
}

/**************************************************************************
 * 				WAVE_mciGetDevCaps		[internal]
 */
1487
static DWORD WAVE_mciGetDevCaps(MCIDEVICEID wDevID, DWORD dwFlags,
1488 1489 1490
				LPMCI_GETDEVCAPS_PARMS lpParms)
{
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1491 1492
    DWORD		ret = 0;

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

1495
    if (lpParms == NULL)	return MCIERR_NULL_PARAMETER_BLOCK;
1496
    if (wmw == NULL)		return MCIERR_INVALID_DEVICE_ID;
1497

1498 1499 1500
    if (dwFlags & MCI_GETDEVCAPS_ITEM) {
	switch(lpParms->dwItem) {
	case MCI_GETDEVCAPS_DEVICE_TYPE:
1501 1502
	    lpParms->dwReturn = MAKEMCIRESOURCE(MCI_DEVTYPE_WAVEFORM_AUDIO, MCI_DEVTYPE_WAVEFORM_AUDIO);
	    ret = MCI_RESOURCE_RETURNED;
1503 1504
	    break;
	case MCI_GETDEVCAPS_HAS_AUDIO:
1505 1506
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1507 1508
	    break;
	case MCI_GETDEVCAPS_HAS_VIDEO:
1509 1510
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
1511 1512
	    break;
	case MCI_GETDEVCAPS_USES_FILES:
1513 1514
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1515 1516
	    break;
	case MCI_GETDEVCAPS_COMPOUND_DEVICE:
1517 1518
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1519 1520
	    break;
	case MCI_GETDEVCAPS_CAN_RECORD:
1521 1522
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1523 1524
	    break;
	case MCI_GETDEVCAPS_CAN_EJECT:
1525 1526
	    lpParms->dwReturn = MAKEMCIRESOURCE(FALSE, MCI_FALSE);
	    ret = MCI_RESOURCE_RETURNED;
1527 1528
	    break;
	case MCI_GETDEVCAPS_CAN_PLAY:
1529 1530
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1531 1532
	    break;
	case MCI_GETDEVCAPS_CAN_SAVE:
1533 1534
	    lpParms->dwReturn = MAKEMCIRESOURCE(TRUE, MCI_TRUE);
	    ret = MCI_RESOURCE_RETURNED;
1535 1536 1537 1538 1539 1540 1541 1542
	    break;
	case MCI_WAVE_GETDEVCAPS_INPUTS:
	    lpParms->dwReturn = 1;
	    break;
	case MCI_WAVE_GETDEVCAPS_OUTPUTS:
	    lpParms->dwReturn = 1;
	    break;
	default:
1543
            FIXME("Unknown capability (%08x) !\n", lpParms->dwItem);
1544 1545
	    return MCIERR_UNRECOGNIZED_COMMAND;
	}
1546 1547 1548
    } else {
	WARN("No GetDevCaps-Item !\n");
	return MCIERR_UNRECOGNIZED_COMMAND;
1549
    }
1550
    return ret;
1551 1552 1553 1554 1555
}

/**************************************************************************
 * 				WAVE_mciInfo			[internal]
 */
1556
static DWORD WAVE_mciInfo(MCIDEVICEID wDevID, DWORD dwFlags, LPMCI_INFO_PARMSW lpParms)
1557 1558
{
    DWORD		ret = 0;
1559
    LPCWSTR		str = 0;
1560
    WINE_MCIWAVE*	wmw = WAVE_mciGetOpenDev(wDevID);
1561

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

1564 1565 1566 1567 1568
    if (lpParms == NULL || lpParms->lpstrReturn == NULL) {
	ret = MCIERR_NULL_PARAMETER_BLOCK;
    } else if (wmw == NULL) {
	ret = MCIERR_INVALID_DEVICE_ID;
    } else {
1569 1570 1571 1572
        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};

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

Eric Pouech's avatar
Eric Pouech committed
1575
	switch (dwFlags & ~(MCI_WAIT|MCI_NOTIFY)) {
1576 1577 1578 1579
	case MCI_INFO_PRODUCT: str = wszAudio; break;
	case MCI_INFO_FILE:    str = wmw->openParms.lpstrElementName; break;
	case MCI_WAVE_INPUT:   str = wszWaveIn; break;
	case MCI_WAVE_OUTPUT:  str = wszWaveOut; break;
1580
	default:
1581
            WARN("Don't know this info command (%u)\n", dwFlags);
1582
	    ret = MCIERR_UNRECOGNIZED_COMMAND;
1583 1584 1585
	}
    }
    if (str) {
1586
	if (strlenW(str) + 1 > lpParms->dwRetSize) {
1587 1588
	    ret = MCIERR_PARAM_OVERFLOW;
	} else {
1589
	    lstrcpynW(lpParms->lpstrReturn, str, lpParms->dwRetSize);
1590 1591 1592 1593
	}
    } else {
	lpParms->lpstrReturn[0] = 0;
    }
1594

1595 1596 1597 1598
    return ret;
}

/**************************************************************************
Patrik Stridvall's avatar
Patrik Stridvall committed
1599
 * 				DriverProc (MCIWAVE.@)
1600
 */
1601 1602
LRESULT CALLBACK MCIWAVE_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
                                    LPARAM dwParam1, LPARAM dwParam2)
1603
{
1604
    TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1605
	  dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1606

1607
    switch (wMsg) {
1608 1609
    case DRV_LOAD:		return 1;
    case DRV_FREE:		return 1;
1610
    case DRV_OPEN:		return WAVE_drvOpen((LPCWSTR)dwParam1, (LPMCI_OPEN_DRIVER_PARMSW)dwParam2);
1611
    case DRV_CLOSE:		return WAVE_drvClose(dwDevID);
1612 1613 1614
    case DRV_ENABLE:		return 1;
    case DRV_DISABLE:		return 1;
    case DRV_QUERYCONFIGURE:	return 1;
1615
    case DRV_CONFIGURE:		MessageBoxA(0, "Sample MultiMedia Driver !", "OSS Driver", MB_OK);	return 1;
1616 1617
    case DRV_INSTALL:		return DRVCNF_RESTART;
    case DRV_REMOVE:		return DRVCNF_RESTART;
1618 1619 1620 1621 1622
    }

    if (dwDevID == 0xFFFFFFFF) return MCIERR_UNSUPPORTED_FUNCTION;

    switch (wMsg) {
1623
    case MCI_OPEN_DRIVER:	return WAVE_mciOpen      (dwDevID, dwParam1, (LPMCI_WAVE_OPEN_PARMSW)  dwParam2);
1624 1625 1626 1627 1628 1629 1630 1631 1632 1633
    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);
    case MCI_PLAY:		return WAVE_mciPlay      (dwDevID, dwParam1, (LPMCI_PLAY_PARMS)        dwParam2);
    case MCI_RECORD:		return WAVE_mciRecord    (dwDevID, dwParam1, (LPMCI_RECORD_PARMS)      dwParam2);
    case MCI_STOP:		return WAVE_mciStop      (dwDevID, dwParam1, (LPMCI_GENERIC_PARMS)     dwParam2);
    case MCI_SET:		return WAVE_mciSet       (dwDevID, dwParam1, (LPMCI_SET_PARMS)         dwParam2);
    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);
1634
    case MCI_INFO:		return WAVE_mciInfo      (dwDevID, dwParam1, (LPMCI_INFO_PARMSW)       dwParam2);
1635
    case MCI_SEEK:		return WAVE_mciSeek      (dwDevID, dwParam1, (LPMCI_SEEK_PARMS)        dwParam2);
1636
    case MCI_SAVE:		return WAVE_mciSave	 (dwDevID, dwParam1, (LPMCI_SAVE_PARMSW)       dwParam2);
1637
	/* commands that should be supported */
1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651
    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:
1652
	FIXME("Unsupported yet command [%u]\n", wMsg);
1653
	break;
1654
    case MCI_WINDOW:
1655
	TRACE("Unsupported command [%u]\n", wMsg);
1656
	break;
1657 1658 1659
	/* option which can be silenced */
    case MCI_CONFIGURE:
	return 0;
1660 1661
    case MCI_OPEN:
    case MCI_CLOSE:
1662
	ERR("Shouldn't receive a MCI_OPEN or CLOSE message\n");
1663 1664
	break;
    default:
1665
	FIXME("is probably wrong msg [%u]\n", wMsg);
1666
	return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1667 1668 1669
    }
    return MCIERR_UNRECOGNIZED_COMMAND;
}