1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Wine Wave mapper driver
5 * Copyright 1999,2001 Eric Pouech
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23 * + better protection against evilish dwUser parameters
24 * + use asynchronous ACM conversion
25 * + don't use callback functions when none is required in open
26 * + the buffer sizes may not be accurate, so there may be some
27 * remaining bytes in src and dst buffers after ACM conversions...
28 * those should be taken care of...
40 #include "wine/unicode.h"
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(wavemap);
45 typedef struct tagWAVEMAPDATA {
46 struct tagWAVEMAPDATA* self;
57 HACMSTREAM hAcmStream;
58 /* needed data to filter callbacks. Only needed when hAcmStream is not 0 */
60 DWORD dwClientInstance;
62 /* ratio to compute position from a PCM playback to any format */
65 /* channel size of inner and outer */
66 DWORD nSamplesPerSecOuter;
67 DWORD nSamplesPerSecInner;
70 static BOOL WAVEMAP_IsData(WAVEMAPDATA* wm)
72 return (!IsBadReadPtr(wm, sizeof(WAVEMAPDATA)) && wm->self == wm);
75 /*======================================================================*
77 *======================================================================*/
79 static void CALLBACK wodCallback(HWAVEOUT hWave, UINT uMsg, DWORD dwInstance,
80 DWORD dwParam1, DWORD dwParam2)
82 WAVEMAPDATA* wom = (WAVEMAPDATA*)dwInstance;
84 TRACE("(%p %u %ld %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
86 if (!WAVEMAP_IsData(wom)) {
91 if (hWave != wom->u.out.hInnerWave && uMsg != WOM_OPEN)
92 ERR("Shouldn't happen (%p %p)\n", hWave, wom->u.out.hInnerWave);
97 /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
100 if (wom->hAcmStream) {
101 LPWAVEHDR lpWaveHdrDst = (LPWAVEHDR)dwParam1;
102 PACMSTREAMHEADER ash = (PACMSTREAMHEADER)((LPSTR)lpWaveHdrDst - sizeof(ACMSTREAMHEADER));
103 LPWAVEHDR lpWaveHdrSrc = (LPWAVEHDR)ash->dwUser;
105 lpWaveHdrSrc->dwFlags &= ~WHDR_INQUEUE;
106 lpWaveHdrSrc->dwFlags |= WHDR_DONE;
107 dwParam1 = (DWORD)lpWaveHdrSrc;
111 ERR("Unknown msg %u\n", uMsg);
114 DriverCallback(wom->dwCallback, HIWORD(wom->dwFlags), (HDRVR)wom->u.out.hOuterWave,
115 uMsg, wom->dwClientInstance, dwParam1, dwParam2);
118 /******************************************************************
123 static DWORD wodOpenHelper(WAVEMAPDATA* wom, UINT idx,
124 LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
129 TRACE("(%p %04x %p %p %08lx)\n", wom, idx, lpDesc, lpwfx, dwFlags);
131 /* destination is always PCM, so the formulas below apply */
132 lpwfx->nBlockAlign = (lpwfx->nChannels * lpwfx->wBitsPerSample) / 8;
133 lpwfx->nAvgBytesPerSec = lpwfx->nSamplesPerSec * lpwfx->nBlockAlign;
134 if (dwFlags & WAVE_FORMAT_QUERY) {
135 ret = acmStreamOpen(NULL, 0, lpDesc->lpFormat, lpwfx, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY);
137 ret = acmStreamOpen(&wom->hAcmStream, 0, lpDesc->lpFormat, lpwfx, NULL, 0L, 0L, 0L);
139 if (ret == MMSYSERR_NOERROR) {
140 ret = waveOutOpen(&wom->u.out.hInnerWave, idx, lpwfx, (DWORD)wodCallback,
141 (DWORD)wom, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION);
142 if (ret != MMSYSERR_NOERROR && !(dwFlags & WAVE_FORMAT_QUERY)) {
143 acmStreamClose(wom->hAcmStream, 0);
147 TRACE("ret = %08lx\n", ret);
151 static DWORD wodOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
155 WAVEMAPDATA* wom = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
158 TRACE("(%p %p %08lx)\n", lpdwUser, lpDesc, dwFlags);
162 return MMSYSERR_NOMEM;
165 ndhi = waveOutGetNumDevs();
166 if (dwFlags & WAVE_MAPPED) {
167 if (lpDesc->uMappedDeviceID >= ndhi) {
168 WARN("invalid parameter: dwFlags WAVE_MAPPED\n");
169 HeapFree(GetProcessHeap(), 0, wom);
170 return MMSYSERR_INVALPARAM;
172 ndlo = lpDesc->uMappedDeviceID;
174 dwFlags &= ~WAVE_MAPPED;
179 wom->dwCallback = lpDesc->dwCallback;
180 wom->dwFlags = dwFlags;
181 wom->dwClientInstance = lpDesc->dwInstance;
182 wom->u.out.hOuterWave = (HWAVEOUT)lpDesc->hWave;
183 wom->avgSpeedOuter = wom->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec;
184 wom->nSamplesPerSecOuter = wom->nSamplesPerSecInner = lpDesc->lpFormat->nSamplesPerSec;
186 for (i = ndlo; i < ndhi; i++) {
187 /* if no ACM stuff is involved, no need to handle callbacks at this
188 * level, this will be done transparently
190 if (waveOutOpen(&wom->u.out.hInnerWave, i, lpDesc->lpFormat, (DWORD)wodCallback,
191 (DWORD)wom, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) {
197 if ((dwFlags & WAVE_FORMAT_DIRECT) == 0 && lpDesc->lpFormat->wFormatTag == WAVE_FORMAT_PCM) {
200 wfx.wFormatTag = WAVE_FORMAT_PCM;
201 wfx.cbSize = 0; /* normally, this field is not used for PCM format, just in case */
202 /* try some ACM stuff */
204 #define TRY(sps,bps) wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
205 switch (res=wodOpenHelper(wom, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT)) { \
206 case MMSYSERR_NOERROR: wom->avgSpeedInner = wfx.nAvgBytesPerSec; wom->nSamplesPerSecInner = wfx.nSamplesPerSec; goto found; \
207 case WAVERR_BADFORMAT: break; \
208 default: goto error; \
211 /* Our resampling algorithm is quite primitive so first try
212 * to just change the bit depth and number of channels
214 for (i = ndlo; i < ndhi; i++) {
215 wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
216 wfx.nChannels = lpDesc->lpFormat->nChannels;
217 TRY(wfx.nSamplesPerSec, 16);
218 TRY(wfx.nSamplesPerSec, 8);
220 TRY(wfx.nSamplesPerSec, 16);
221 TRY(wfx.nSamplesPerSec, 8);
224 for (i = ndlo; i < ndhi; i++) {
225 /* first try with same stereo/mono option as source */
226 wfx.nChannels = lpDesc->lpFormat->nChannels;
233 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
241 /* first try with same stereo/mono option as source */
242 wfx.nChannels = lpDesc->lpFormat->nChannels;
249 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
260 HeapFree(GetProcessHeap(), 0, wom);
261 WARN("ret = WAVERR_BADFORMAT\n");
262 return WAVERR_BADFORMAT;
265 if (dwFlags & WAVE_FORMAT_QUERY) {
267 HeapFree(GetProcessHeap(), 0, wom);
269 *lpdwUser = (DWORD)wom;
271 return MMSYSERR_NOERROR;
273 HeapFree(GetProcessHeap(), 0, wom);
274 if (res==ACMERR_NOTPOSSIBLE) {
275 WARN("ret = WAVERR_BADFORMAT\n");
276 return WAVERR_BADFORMAT;
278 WARN("ret = 0x%08lx\n", res);
282 static DWORD wodClose(WAVEMAPDATA* wom)
286 TRACE("(%p)\n", wom);
288 ret = waveOutClose(wom->u.out.hInnerWave);
289 if (ret == MMSYSERR_NOERROR) {
290 if (wom->hAcmStream) {
291 ret = acmStreamClose(wom->hAcmStream, 0);
293 if (ret == MMSYSERR_NOERROR) {
294 HeapFree(GetProcessHeap(), 0, wom);
300 static DWORD wodWrite(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
302 PACMSTREAMHEADER ash;
303 LPWAVEHDR lpWaveHdrDst;
305 TRACE("(%p %p %08lx)\n", wom, lpWaveHdrSrc, dwParam2);
307 if (!wom->hAcmStream) {
308 return waveOutWrite(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
311 lpWaveHdrSrc->dwFlags |= WHDR_INQUEUE;
312 ash = (PACMSTREAMHEADER)lpWaveHdrSrc->reserved;
313 /* acmStreamConvert will actually check that the new size is less than initial size */
314 ash->cbSrcLength = lpWaveHdrSrc->dwBufferLength;
315 if (acmStreamConvert(wom->hAcmStream, ash, 0L) != MMSYSERR_NOERROR) {
316 WARN("acmStreamConvert failed\n");
317 return MMSYSERR_ERROR;
320 lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
321 if (ash->cbSrcLength > ash->cbSrcLengthUsed)
322 FIXME("Not all src buffer has been written, expect bogus sound\n");
323 else if (ash->cbSrcLength < ash->cbSrcLengthUsed)
324 ERR("CoDec has read more data than it is allowed to\n");
326 if (ash->cbDstLengthUsed == 0) {
327 /* something went wrong in decoding */
328 FIXME("Got 0 length\n");
329 return MMSYSERR_ERROR;
331 lpWaveHdrDst->dwBufferLength = ash->cbDstLengthUsed;
332 return waveOutWrite(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
335 static DWORD wodPrepare(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
337 PACMSTREAMHEADER ash;
340 LPWAVEHDR lpWaveHdrDst;
342 TRACE("(%p %p %08lx)\n", wom, lpWaveHdrSrc, dwParam2);
344 if (!wom->hAcmStream)
345 return waveOutPrepareHeader(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
347 if (acmStreamSize(wom->hAcmStream, lpWaveHdrSrc->dwBufferLength, &size, ACM_STREAMSIZEF_SOURCE) != MMSYSERR_NOERROR) {
348 WARN("acmStreamSize failed\n");
349 return MMSYSERR_ERROR;
352 ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
355 return MMSYSERR_NOMEM;
358 ash->cbStruct = sizeof(*ash);
360 ash->dwUser = (DWORD)lpWaveHdrSrc;
361 ash->pbSrc = lpWaveHdrSrc->lpData;
362 ash->cbSrcLength = lpWaveHdrSrc->dwBufferLength;
363 /* ash->cbSrcLengthUsed */
364 ash->dwSrcUser = lpWaveHdrSrc->dwUser; /* FIXME ? */
365 ash->pbDst = (LPSTR)ash + sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR);
366 ash->cbDstLength = size;
367 /* ash->cbDstLengthUsed */
368 ash->dwDstUser = 0; /* FIXME ? */
369 dwRet = acmStreamPrepareHeader(wom->hAcmStream, ash, 0L);
370 if (dwRet != MMSYSERR_NOERROR) {
371 WARN("acmStreamPrepareHeader failed\n");
375 lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
376 lpWaveHdrDst->lpData = ash->pbDst;
377 lpWaveHdrDst->dwBufferLength = size; /* conversion is not done yet */
378 lpWaveHdrDst->dwFlags = 0;
379 lpWaveHdrDst->dwLoops = 0;
380 dwRet = waveOutPrepareHeader(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
381 if (dwRet != MMSYSERR_NOERROR) {
382 WARN("waveOutPrepareHeader failed\n");
386 lpWaveHdrSrc->reserved = (DWORD)ash;
387 lpWaveHdrSrc->dwFlags = WHDR_PREPARED;
389 return MMSYSERR_NOERROR;
391 TRACE("=> (%ld)\n", dwRet);
392 HeapFree(GetProcessHeap(), 0, ash);
396 static DWORD wodUnprepare(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
398 PACMSTREAMHEADER ash;
399 LPWAVEHDR lpWaveHdrDst;
400 DWORD dwRet1, dwRet2;
402 TRACE("(%p %p %08lx)\n", wom, lpWaveHdrSrc, dwParam2);
404 if (!wom->hAcmStream) {
405 return waveOutUnprepareHeader(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
407 ash = (PACMSTREAMHEADER)lpWaveHdrSrc->reserved;
408 dwRet1 = acmStreamUnprepareHeader(wom->hAcmStream, ash, 0L);
410 lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
411 dwRet2 = waveOutUnprepareHeader(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
413 HeapFree(GetProcessHeap(), 0, ash);
415 lpWaveHdrSrc->dwFlags &= ~WHDR_PREPARED;
416 return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
419 static DWORD wodGetPosition(WAVEMAPDATA* wom, LPMMTIME lpTime, DWORD dwParam2)
423 TRACE("(%p %p %08lx)\n", wom, lpTime, dwParam2);
425 memcpy(&timepos, lpTime, sizeof(timepos));
427 /* For TIME_MS, we're going to recalculate using TIME_BYTES */
428 if (lpTime->wType == TIME_MS)
429 timepos.wType = TIME_BYTES;
431 /* This can change timepos.wType if the requested type is not supported */
432 val = waveOutGetPosition(wom->u.out.hInnerWave, &timepos, dwParam2);
434 if (timepos.wType == TIME_BYTES)
436 DWORD dwInnerSamplesPerOuter = wom->nSamplesPerSecInner / wom->nSamplesPerSecOuter;
437 if (dwInnerSamplesPerOuter > 0)
439 DWORD dwInnerBytesPerSample = wom->avgSpeedInner / wom->nSamplesPerSecInner;
440 DWORD dwInnerBytesPerOuterSample = dwInnerBytesPerSample * dwInnerSamplesPerOuter;
443 /* If we are up sampling (going from lower sample rate to higher),
444 ** we need to make a special accomodation for times when we've
445 ** written a partial output sample. This happens frequently
446 ** to us because we use msacm to do our up sampling, and it
447 ** will up sample on an unaligned basis.
448 ** For example, if you convert a 2 byte wide 8,000 'outer'
449 ** buffer to a 2 byte wide 48,000 inner device, you would
450 ** expect 2 bytes of input to produce 12 bytes of output.
451 ** Instead, msacm will produce 8 bytes of output.
452 ** But reporting our position as 1 byte of output is
453 ** nonsensical; the output buffer position needs to be
454 ** aligned on outer sample size, and aggressively rounded up.
456 remainder = timepos.u.cb % dwInnerBytesPerOuterSample;
459 timepos.u.cb -= remainder;
460 timepos.u.cb += dwInnerBytesPerOuterSample;
464 lpTime->u.cb = MulDiv(timepos.u.cb, wom->avgSpeedOuter, wom->avgSpeedInner);
466 /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */
467 if (lpTime->wType == TIME_MS)
468 lpTime->u.ms = MulDiv(lpTime->u.cb, 1000, wom->avgSpeedOuter);
470 lpTime->wType = TIME_BYTES;
472 else if (lpTime->wType == TIME_SAMPLES && timepos.wType == TIME_SAMPLES)
473 lpTime->u.sample = MulDiv(timepos.u.sample, wom->nSamplesPerSecOuter, wom->nSamplesPerSecInner);
475 /* other time types don't require conversion */
476 lpTime->u = timepos.u;
481 static DWORD wodGetDevCaps(UINT wDevID, WAVEMAPDATA* wom, LPWAVEOUTCAPSW lpWaveCaps, DWORD dwParam2)
483 static const WCHAR name[] = {'W','i','n','e',' ','w','a','v','e',' ','o','u','t',' ','m','a','p','p','e','r',0};
485 TRACE("(%04x %p %p %08lx)\n",wDevID, wom, lpWaveCaps, dwParam2);
487 /* if opened low driver, forward message */
488 if (WAVEMAP_IsData(wom))
489 return waveOutGetDevCapsW((UINT)wom->u.out.hInnerWave, lpWaveCaps, dwParam2);
490 /* else if no drivers, nothing to map so return bad device */
491 if (waveOutGetNumDevs() == 0) {
492 WARN("bad device id\n");
493 return MMSYSERR_BADDEVICEID;
495 /* otherwise, return caps of mapper itself */
496 if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
500 woc.vDriverVersion = 0x0100;
501 lstrcpyW(woc.szPname, name);
503 WAVE_FORMAT_96M08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16 |
504 WAVE_FORMAT_48M08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 |
505 WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
506 WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
507 WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
509 woc.dwSupport = WAVECAPS_VOLUME | WAVECAPS_LRVOLUME;
510 memcpy(lpWaveCaps, &woc, min(dwParam2, sizeof(woc)));
512 return MMSYSERR_NOERROR;
514 ERR("This shouldn't happen\n");
515 return MMSYSERR_ERROR;
518 static DWORD wodGetVolume(UINT wDevID, WAVEMAPDATA* wom, LPDWORD lpVol)
520 TRACE("(%04x %p %p)\n",wDevID, wom, lpVol);
522 if (WAVEMAP_IsData(wom))
523 return waveOutGetVolume(wom->u.out.hInnerWave, lpVol);
524 return MMSYSERR_NOERROR;
527 static DWORD wodSetVolume(UINT wDevID, WAVEMAPDATA* wom, DWORD vol)
529 TRACE("(%04x %p %08lx)\n",wDevID, wom, vol);
531 if (WAVEMAP_IsData(wom))
532 return waveOutSetVolume(wom->u.out.hInnerWave, vol);
533 return MMSYSERR_NOERROR;
536 static DWORD wodPause(WAVEMAPDATA* wom)
540 return waveOutPause(wom->u.out.hInnerWave);
543 static DWORD wodRestart(WAVEMAPDATA* wom)
547 return waveOutRestart(wom->u.out.hInnerWave);
550 static DWORD wodReset(WAVEMAPDATA* wom)
554 return waveOutReset(wom->u.out.hInnerWave);
557 static DWORD wodBreakLoop(WAVEMAPDATA* wom)
561 return waveOutBreakLoop(wom->u.out.hInnerWave);
564 static DWORD wodMapperStatus(WAVEMAPDATA* wom, DWORD flags, LPVOID ptr)
567 DWORD ret = MMSYSERR_NOTSUPPORTED;
569 TRACE("(%p %08lx %p)\n",wom, flags, ptr);
572 case WAVEOUT_MAPPER_STATUS_DEVICE:
573 ret = waveOutGetID(wom->u.out.hInnerWave, &id);
576 case WAVEOUT_MAPPER_STATUS_MAPPED:
577 FIXME("Unsupported flag=%ld\n", flags);
578 *(LPDWORD)ptr = 0; /* FIXME ?? */
580 case WAVEOUT_MAPPER_STATUS_FORMAT:
581 FIXME("Unsupported flag=%ld\n", flags);
582 /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
586 FIXME("Unsupported flag=%ld\n", flags);
593 static DWORD wodMapperReconfigure(WAVEMAPDATA* wom, DWORD dwParam1, DWORD dwParam2)
595 FIXME("(%p %08lx %08lx) stub!\n", wom, dwParam1, dwParam2);
597 return MMSYSERR_NOERROR;
600 /**************************************************************************
601 * wodMessage (MSACM.@)
603 DWORD WINAPI WAVEMAP_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
604 DWORD dwParam1, DWORD dwParam2)
606 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
607 wDevID, wMsg, dwUser, dwParam1, dwParam2);
614 /* FIXME: Pretend this is supported */
616 case WODM_OPEN: return wodOpen ((LPDWORD)dwUser, (LPWAVEOPENDESC)dwParam1,dwParam2);
617 case WODM_CLOSE: return wodClose ((WAVEMAPDATA*)dwUser);
618 case WODM_WRITE: return wodWrite ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
619 case WODM_PAUSE: return wodPause ((WAVEMAPDATA*)dwUser);
620 case WODM_GETPOS: return wodGetPosition ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1, dwParam2);
621 case WODM_BREAKLOOP: return wodBreakLoop ((WAVEMAPDATA*)dwUser);
622 case WODM_PREPARE: return wodPrepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
623 case WODM_UNPREPARE: return wodUnprepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
624 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEOUTCAPSW)dwParam1,dwParam2);
625 case WODM_GETNUMDEVS: return 1;
626 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
627 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
628 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
629 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
630 case WODM_GETVOLUME: return wodGetVolume (wDevID, (WAVEMAPDATA*)dwUser, (LPDWORD)dwParam1);
631 case WODM_SETVOLUME: return wodSetVolume (wDevID, (WAVEMAPDATA*)dwUser, dwParam1);
632 case WODM_RESTART: return wodRestart ((WAVEMAPDATA*)dwUser);
633 case WODM_RESET: return wodReset ((WAVEMAPDATA*)dwUser);
634 case WODM_MAPPER_STATUS: return wodMapperStatus ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
635 case DRVM_MAPPER_RECONFIGURE: return wodMapperReconfigure((WAVEMAPDATA*)dwUser, dwParam1, dwParam2);
636 /* known but not supported */
637 case DRV_QUERYDEVICEINTERFACESIZE:
638 case DRV_QUERYDEVICEINTERFACE:
639 return MMSYSERR_NOTSUPPORTED;
641 FIXME("unknown message %d!\n", wMsg);
643 return MMSYSERR_NOTSUPPORTED;
646 /*======================================================================*
648 *======================================================================*/
650 static void CALLBACK widCallback(HWAVEIN hWave, UINT uMsg, DWORD dwInstance,
651 DWORD dwParam1, DWORD dwParam2)
653 WAVEMAPDATA* wim = (WAVEMAPDATA*)dwInstance;
655 TRACE("(%p %u %ld %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
657 if (!WAVEMAP_IsData(wim)) {
662 if (hWave != wim->u.in.hInnerWave && uMsg != WIM_OPEN)
663 ERR("Shouldn't happen (%p %p)\n", hWave, wim->u.in.hInnerWave);
668 /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
671 if (wim->hAcmStream) {
672 LPWAVEHDR lpWaveHdrSrc = (LPWAVEHDR)dwParam1;
673 PACMSTREAMHEADER ash = (PACMSTREAMHEADER)((LPSTR)lpWaveHdrSrc - sizeof(ACMSTREAMHEADER));
674 LPWAVEHDR lpWaveHdrDst = (LPWAVEHDR)ash->dwUser;
676 /* convert data just gotten from waveIn into requested format */
677 if (acmStreamConvert(wim->hAcmStream, ash, 0L) != MMSYSERR_NOERROR) {
678 ERR("ACM conversion failed\n");
681 TRACE("Converted %ld bytes into %ld\n", ash->cbSrcLengthUsed, ash->cbDstLengthUsed);
683 /* and setup the wavehdr to return accordingly */
684 lpWaveHdrDst->dwFlags &= ~WHDR_INQUEUE;
685 lpWaveHdrDst->dwFlags |= WHDR_DONE;
686 lpWaveHdrDst->dwBytesRecorded = ash->cbDstLengthUsed;
687 dwParam1 = (DWORD)lpWaveHdrDst;
691 ERR("Unknown msg %u\n", uMsg);
694 DriverCallback(wim->dwCallback, HIWORD(wim->dwFlags), (HDRVR)wim->u.in.hOuterWave,
695 uMsg, wim->dwClientInstance, dwParam1, dwParam2);
698 static DWORD widOpenHelper(WAVEMAPDATA* wim, UINT idx,
699 LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
704 TRACE("(%p %04x %p %p %08lx)\n", wim, idx, lpDesc, lpwfx, dwFlags);
706 /* source is always PCM, so the formulas below apply */
707 lpwfx->nBlockAlign = (lpwfx->nChannels * lpwfx->wBitsPerSample) / 8;
708 lpwfx->nAvgBytesPerSec = lpwfx->nSamplesPerSec * lpwfx->nBlockAlign;
709 if (dwFlags & WAVE_FORMAT_QUERY) {
710 ret = acmStreamOpen(NULL, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY);
712 ret = acmStreamOpen(&wim->hAcmStream, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, 0L);
714 if (ret == MMSYSERR_NOERROR) {
715 ret = waveInOpen(&wim->u.in.hInnerWave, idx, lpwfx, (DWORD)widCallback,
716 (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION);
717 if (ret != MMSYSERR_NOERROR && !(dwFlags & WAVE_FORMAT_QUERY)) {
718 acmStreamClose(wim->hAcmStream, 0);
722 TRACE("ret = %08lx\n", ret);
726 static DWORD widOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
730 WAVEMAPDATA* wim = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
733 TRACE("(%p %p %08lx)\n", lpdwUser, lpDesc, dwFlags);
737 return MMSYSERR_NOMEM;
741 wim->dwCallback = lpDesc->dwCallback;
742 wim->dwFlags = dwFlags;
743 wim->dwClientInstance = lpDesc->dwInstance;
744 wim->u.in.hOuterWave = (HWAVEIN)lpDesc->hWave;
746 ndhi = waveInGetNumDevs();
747 if (dwFlags & WAVE_MAPPED) {
748 if (lpDesc->uMappedDeviceID >= ndhi) return MMSYSERR_INVALPARAM;
749 ndlo = lpDesc->uMappedDeviceID;
751 dwFlags &= ~WAVE_MAPPED;
756 wim->avgSpeedOuter = wim->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec;
757 wim->nSamplesPerSecOuter = wim->nSamplesPerSecInner = lpDesc->lpFormat->nSamplesPerSec;
759 for (i = ndlo; i < ndhi; i++) {
760 if (waveInOpen(&wim->u.in.hInnerWave, i, lpDesc->lpFormat, (DWORD)widCallback,
761 (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) {
767 if ((dwFlags & WAVE_FORMAT_DIRECT) == 0)
771 wfx.wFormatTag = WAVE_FORMAT_PCM;
772 wfx.cbSize = 0; /* normally, this field is not used for PCM format, just in case */
773 /* try some ACM stuff */
775 #define TRY(sps,bps) wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
776 switch (res=widOpenHelper(wim, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT)) { \
777 case MMSYSERR_NOERROR: wim->avgSpeedInner = wfx.nAvgBytesPerSec; wim->nSamplesPerSecInner = wfx.nSamplesPerSec; goto found; \
778 case WAVERR_BADFORMAT: break; \
779 default: goto error; \
782 for (i = ndlo; i < ndhi; i++) {
783 wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
784 /* first try with same stereo/mono option as source */
785 wfx.nChannels = lpDesc->lpFormat->nChannels;
786 TRY(wfx.nSamplesPerSec, 16);
787 TRY(wfx.nSamplesPerSec, 8);
789 TRY(wfx.nSamplesPerSec, 16);
790 TRY(wfx.nSamplesPerSec, 8);
793 for (i = ndlo; i < ndhi; i++) {
794 wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
795 /* first try with same stereo/mono option as source */
796 wfx.nChannels = lpDesc->lpFormat->nChannels;
803 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
811 /* first try with same stereo/mono option as source */
812 wfx.nChannels = lpDesc->lpFormat->nChannels;
819 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
830 HeapFree(GetProcessHeap(), 0, wim);
831 WARN("ret = WAVERR_BADFORMAT\n");
832 return WAVERR_BADFORMAT;
834 if (dwFlags & WAVE_FORMAT_QUERY) {
836 HeapFree(GetProcessHeap(), 0, wim);
838 *lpdwUser = (DWORD)wim;
840 TRACE("Ok (stream=%08lx)\n", (DWORD)wim->hAcmStream);
841 return MMSYSERR_NOERROR;
843 HeapFree(GetProcessHeap(), 0, wim);
844 if (res==ACMERR_NOTPOSSIBLE) {
845 WARN("ret = WAVERR_BADFORMAT\n");
846 return WAVERR_BADFORMAT;
848 WARN("ret = 0x%08lx\n", res);
852 static DWORD widClose(WAVEMAPDATA* wim)
856 TRACE("(%p)\n", wim);
858 ret = waveInClose(wim->u.in.hInnerWave);
859 if (ret == MMSYSERR_NOERROR) {
860 if (wim->hAcmStream) {
861 ret = acmStreamClose(wim->hAcmStream, 0);
863 if (ret == MMSYSERR_NOERROR) {
864 HeapFree(GetProcessHeap(), 0, wim);
870 static DWORD widAddBuffer(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
872 PACMSTREAMHEADER ash;
873 LPWAVEHDR lpWaveHdrSrc;
875 TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
877 if (!wim->hAcmStream) {
878 return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
881 lpWaveHdrDst->dwFlags |= WHDR_INQUEUE;
882 ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
884 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
885 return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
888 static DWORD widPrepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
890 PACMSTREAMHEADER ash;
893 LPWAVEHDR lpWaveHdrSrc;
895 TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
897 if (!wim->hAcmStream) {
898 return waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
900 if (acmStreamSize(wim->hAcmStream, lpWaveHdrDst->dwBufferLength, &size,
901 ACM_STREAMSIZEF_DESTINATION) != MMSYSERR_NOERROR) {
902 WARN("acmStreamSize failed\n");
903 return MMSYSERR_ERROR;
906 ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
909 return MMSYSERR_NOMEM;
912 ash->cbStruct = sizeof(*ash);
914 ash->dwUser = (DWORD)lpWaveHdrDst;
915 ash->pbSrc = (LPSTR)ash + sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR);
916 ash->cbSrcLength = size;
917 /* ash->cbSrcLengthUsed */
918 ash->dwSrcUser = 0L; /* FIXME ? */
919 ash->pbDst = lpWaveHdrDst->lpData;
920 ash->cbDstLength = lpWaveHdrDst->dwBufferLength;
921 /* ash->cbDstLengthUsed */
922 ash->dwDstUser = lpWaveHdrDst->dwUser; /* FIXME ? */
923 dwRet = acmStreamPrepareHeader(wim->hAcmStream, ash, 0L);
924 if (dwRet != MMSYSERR_NOERROR) {
925 WARN("acmStreamPrepareHeader failed\n");
929 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
930 lpWaveHdrSrc->lpData = ash->pbSrc;
931 lpWaveHdrSrc->dwBufferLength = size; /* conversion is not done yet */
932 lpWaveHdrSrc->dwFlags = 0;
933 lpWaveHdrSrc->dwLoops = 0;
934 dwRet = waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
935 if (dwRet != MMSYSERR_NOERROR) {
936 WARN("waveInPrepareHeader failed\n");
940 lpWaveHdrDst->reserved = (DWORD)ash;
941 lpWaveHdrDst->dwFlags = WHDR_PREPARED;
943 return MMSYSERR_NOERROR;
945 TRACE("=> (%ld)\n", dwRet);
946 HeapFree(GetProcessHeap(), 0, ash);
950 static DWORD widUnprepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
952 PACMSTREAMHEADER ash;
953 LPWAVEHDR lpWaveHdrSrc;
954 DWORD dwRet1, dwRet2;
956 TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
958 if (!wim->hAcmStream) {
959 return waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
961 ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
962 dwRet1 = acmStreamUnprepareHeader(wim->hAcmStream, ash, 0L);
964 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
965 dwRet2 = waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
967 HeapFree(GetProcessHeap(), 0, ash);
969 lpWaveHdrDst->dwFlags &= ~WHDR_PREPARED;
970 return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
973 static DWORD widGetPosition(WAVEMAPDATA* wim, LPMMTIME lpTime, DWORD dwParam2)
977 TRACE("(%p %p %08lx)\n", wim, lpTime, dwParam2);
979 memcpy(&timepos, lpTime, sizeof(timepos));
981 /* For TIME_MS, we're going to recalculate using TIME_BYTES */
982 if (lpTime->wType == TIME_MS)
983 timepos.wType = TIME_BYTES;
985 /* This can change timepos.wType if the requested type is not supported */
986 val = waveInGetPosition(wim->u.in.hInnerWave, &timepos, dwParam2);
988 if (timepos.wType == TIME_BYTES)
990 DWORD dwInnerSamplesPerOuter = wim->nSamplesPerSecInner / wim->nSamplesPerSecOuter;
991 if (dwInnerSamplesPerOuter > 0)
993 DWORD dwInnerBytesPerSample = wim->avgSpeedInner / wim->nSamplesPerSecInner;
994 DWORD dwInnerBytesPerOuterSample = dwInnerBytesPerSample * dwInnerSamplesPerOuter;
997 /* If we are up sampling (going from lower sample rate to higher),
998 ** we need to make a special accomodation for times when we've
999 ** written a partial output sample. This happens frequently
1000 ** to us because we use msacm to do our up sampling, and it
1001 ** will up sample on an unaligned basis.
1002 ** For example, if you convert a 2 byte wide 8,000 'outer'
1003 ** buffer to a 2 byte wide 48,000 inner device, you would
1004 ** expect 2 bytes of input to produce 12 bytes of output.
1005 ** Instead, msacm will produce 8 bytes of output.
1006 ** But reporting our position as 1 byte of output is
1007 ** nonsensical; the output buffer position needs to be
1008 ** aligned on outer sample size, and aggressively rounded up.
1010 remainder = timepos.u.cb % dwInnerBytesPerOuterSample;
1013 timepos.u.cb -= remainder;
1014 timepos.u.cb += dwInnerBytesPerOuterSample;
1018 lpTime->u.cb = MulDiv(timepos.u.cb, wim->avgSpeedOuter, wim->avgSpeedInner);
1020 /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */
1021 if (lpTime->wType == TIME_MS)
1022 lpTime->u.ms = MulDiv(lpTime->u.cb, 1000, wim->avgSpeedOuter);
1024 lpTime->wType = TIME_BYTES;
1026 else if (lpTime->wType == TIME_SAMPLES && timepos.wType == TIME_SAMPLES)
1027 lpTime->u.sample = MulDiv(timepos.u.sample, wim->nSamplesPerSecOuter, wim->nSamplesPerSecInner);
1029 /* other time types don't require conversion */
1030 lpTime->u = timepos.u;
1035 static DWORD widGetDevCaps(UINT wDevID, WAVEMAPDATA* wim, LPWAVEINCAPSW lpWaveCaps, DWORD dwParam2)
1037 TRACE("(%04x, %p %p %08lx)\n", wDevID, wim, lpWaveCaps, dwParam2);
1039 /* if opened low driver, forward message */
1040 if (WAVEMAP_IsData(wim))
1041 return waveInGetDevCapsW((UINT)wim->u.in.hInnerWave, lpWaveCaps, dwParam2);
1042 /* else if no drivers, nothing to map so return bad device */
1043 if (waveInGetNumDevs() == 0) {
1044 WARN("bad device id\n");
1045 return MMSYSERR_BADDEVICEID;
1047 /* otherwise, return caps of mapper itself */
1048 if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
1050 static const WCHAR init[] = {'W','i','n','e',' ','w','a','v','e',' ','i','n',' ','m','a','p','p','e','r',0};
1053 wic.vDriverVersion = 0x0001;
1054 strcpyW(wic.szPname, init);
1056 WAVE_FORMAT_96M08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16 |
1057 WAVE_FORMAT_48M08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 |
1058 WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
1059 WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
1060 WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
1062 memcpy(lpWaveCaps, &wic, min(dwParam2, sizeof(wic)));
1064 return MMSYSERR_NOERROR;
1066 ERR("This shouldn't happen\n");
1067 return MMSYSERR_ERROR;
1070 static DWORD widStop(WAVEMAPDATA* wim)
1072 TRACE("(%p)\n", wim);
1074 return waveInStop(wim->u.in.hInnerWave);
1077 static DWORD widStart(WAVEMAPDATA* wim)
1079 TRACE("(%p)\n", wim);
1081 return waveInStart(wim->u.in.hInnerWave);
1084 static DWORD widReset(WAVEMAPDATA* wim)
1086 TRACE("(%p)\n", wim);
1088 return waveInReset(wim->u.in.hInnerWave);
1091 static DWORD widMapperStatus(WAVEMAPDATA* wim, DWORD flags, LPVOID ptr)
1094 DWORD ret = MMSYSERR_NOTSUPPORTED;
1096 TRACE("(%p %08lx %p)\n", wim, flags, ptr);
1099 case WAVEIN_MAPPER_STATUS_DEVICE:
1100 ret = waveInGetID(wim->u.in.hInnerWave, &id);
1103 case WAVEIN_MAPPER_STATUS_MAPPED:
1104 FIXME("Unsupported yet flag=%ld\n", flags);
1105 *(LPDWORD)ptr = 0; /* FIXME ?? */
1107 case WAVEIN_MAPPER_STATUS_FORMAT:
1108 FIXME("Unsupported flag=%ld\n", flags);
1109 /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
1110 *(LPDWORD)ptr = 0; /* FIXME ?? */
1113 FIXME("Unsupported flag=%ld\n", flags);
1120 static DWORD widMapperReconfigure(WAVEMAPDATA* wim, DWORD dwParam1, DWORD dwParam2)
1122 FIXME("(%p %08lx %08lx) stub!\n", wim, dwParam1, dwParam2);
1124 return MMSYSERR_NOERROR;
1127 /**************************************************************************
1128 * widMessage (MSACM.@)
1130 DWORD WINAPI WAVEMAP_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1131 DWORD dwParam1, DWORD dwParam2)
1133 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1134 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1141 /* FIXME: Pretend this is supported */
1144 case WIDM_OPEN: return widOpen ((LPDWORD)dwUser, (LPWAVEOPENDESC)dwParam1, dwParam2);
1145 case WIDM_CLOSE: return widClose ((WAVEMAPDATA*)dwUser);
1147 case WIDM_ADDBUFFER: return widAddBuffer ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
1148 case WIDM_PREPARE: return widPrepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
1149 case WIDM_UNPREPARE: return widUnprepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
1150 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEINCAPSW)dwParam1, dwParam2);
1151 case WIDM_GETNUMDEVS: return 1;
1152 case WIDM_GETPOS: return widGetPosition ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1, dwParam2);
1153 case WIDM_RESET: return widReset ((WAVEMAPDATA*)dwUser);
1154 case WIDM_START: return widStart ((WAVEMAPDATA*)dwUser);
1155 case WIDM_STOP: return widStop ((WAVEMAPDATA*)dwUser);
1156 case WIDM_MAPPER_STATUS: return widMapperStatus ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
1157 case DRVM_MAPPER_RECONFIGURE: return widMapperReconfigure((WAVEMAPDATA*)dwUser, dwParam1, dwParam2);
1158 /* known but not supported */
1159 case DRV_QUERYDEVICEINTERFACESIZE:
1160 case DRV_QUERYDEVICEINTERFACE:
1161 return MMSYSERR_NOTSUPPORTED;
1163 FIXME("unknown message %u!\n", wMsg);
1165 return MMSYSERR_NOTSUPPORTED;
1168 /*======================================================================*
1170 *======================================================================*/
1172 static struct WINE_WAVEMAP* oss = NULL;
1174 /**************************************************************************
1175 * WAVEMAP_drvOpen [internal]
1177 static DWORD WAVEMAP_drvOpen(LPSTR str)
1179 TRACE("(%p)\n", str);
1184 /* I know, this is ugly, but who cares... */
1185 oss = (struct WINE_WAVEMAP*)1;
1189 /**************************************************************************
1190 * WAVEMAP_drvClose [internal]
1192 static DWORD WAVEMAP_drvClose(DWORD dwDevID)
1194 TRACE("(%08lx)\n", dwDevID);
1203 /**************************************************************************
1204 * DriverProc (MSACM.@)
1206 LONG CALLBACK WAVEMAP_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1207 DWORD dwParam1, DWORD dwParam2)
1209 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1210 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1213 case DRV_LOAD: return 1;
1214 case DRV_FREE: return 1;
1215 case DRV_OPEN: return WAVEMAP_drvOpen((LPSTR)dwParam1);
1216 case DRV_CLOSE: return WAVEMAP_drvClose(dwDevID);
1217 case DRV_ENABLE: return 1;
1218 case DRV_DISABLE: return 1;
1219 case DRV_QUERYCONFIGURE: return 1;
1220 case DRV_CONFIGURE: MessageBoxA(0, "WAVEMAP MultiMedia Driver !", "Wave mapper Driver", MB_OK); return 1;
1221 case DRV_INSTALL: return DRVCNF_RESTART;
1222 case DRV_REMOVE: return DRVCNF_RESTART;
1224 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);