stash
[wine/wine64.git] / dlls / msacm32.drv / wavemap.c
blob2467970e7971b3e01acca8998f68cd183c4bea13
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 /* TODOs
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...
31 #include <stdarg.h>
32 #include <string.h>
33 #include "windef.h"
34 #include "winbase.h"
35 #include "wingdi.h"
36 #include "winuser.h"
37 #include "mmddk.h"
38 #include "mmreg.h"
39 #include "msacm.h"
40 #include "wine/unicode.h"
41 #include "wine/debug.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(wavemap);
45 typedef struct tagWAVEMAPDATA {
46 struct tagWAVEMAPDATA* self;
47 union {
48 struct {
49 HWAVEOUT hOuterWave;
50 HWAVEOUT hInnerWave;
51 } out;
52 struct {
53 HWAVEIN hOuterWave;
54 HWAVEIN hInnerWave;
55 } in;
56 } u;
57 HACMSTREAM hAcmStream;
58 /* needed data to filter callbacks. Only needed when hAcmStream is not 0 */
59 DWORD dwCallback;
60 DWORD dwClientInstance;
61 DWORD dwFlags;
62 /* ratio to compute position from a PCM playback to any format */
63 DWORD avgSpeedOuter;
64 DWORD avgSpeedInner;
65 /* channel size of inner and outer */
66 DWORD nSamplesPerSecOuter;
67 DWORD nSamplesPerSecInner;
68 } WAVEMAPDATA;
70 static BOOL WAVEMAP_IsData(const WAVEMAPDATA* wm)
72 return (!IsBadReadPtr(wm, sizeof(WAVEMAPDATA)) && wm->self == wm);
75 /*======================================================================*
76 * WAVE OUT part *
77 *======================================================================*/
79 static void CALLBACK wodCallback(HWAVEOUT hWave, UINT uMsg, DWORD_PTR dwInstance,
80 LPARAM dwParam1, LPARAM dwParam2)
82 WAVEMAPDATA* wom = (WAVEMAPDATA*)dwInstance;
84 TRACE("(%p %u %ld %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
86 if (!WAVEMAP_IsData(wom)) {
87 ERR("Bad data\n");
88 return;
91 if (uMsg != WOM_OPEN && hWave != wom->u.out.hInnerWave)
92 ERR("Shouldn't happen (%p %p)\n", hWave, wom->u.out.hInnerWave);
94 switch (uMsg) {
95 case WOM_OPEN:
96 case WOM_CLOSE:
97 /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
98 break;
99 case WOM_DONE:
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_PTR)lpWaveHdrSrc;
109 break;
110 default:
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 /******************************************************************
119 * wodOpenHelper
123 static DWORD wodOpenHelper(WAVEMAPDATA* wom, UINT idx,
124 LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
125 DWORD dwFlags)
127 DWORD ret;
129 TRACE("(%p %04x %p %p %08x)\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);
136 } else {
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_PTR)wodCallback,
141 (DWORD_PTR)wom, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION);
142 if (ret != MMSYSERR_NOERROR && !(dwFlags & WAVE_FORMAT_QUERY)) {
143 acmStreamClose(wom->hAcmStream, 0);
144 wom->hAcmStream = 0;
147 TRACE("ret = %08x\n", ret);
148 return ret;
151 static DWORD wodOpen(DWORD_PTR *lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
153 UINT ndlo, ndhi;
154 UINT i;
155 WAVEMAPDATA* wom = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
156 DWORD res;
158 TRACE("(%p %p %08x)\n", lpdwUser, lpDesc, dwFlags);
160 if (!wom) {
161 WARN("no memory\n");
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;
173 ndhi = ndlo + 1;
174 dwFlags &= ~WAVE_MAPPED;
175 } else {
176 ndlo = 0;
178 wom->self = wom;
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_PTR)wodCallback,
191 (DWORD_PTR)wom, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) {
192 wom->hAcmStream = 0;
193 goto found;
197 if ((dwFlags & WAVE_FORMAT_DIRECT) == 0) {
198 WAVEFORMATEX wfx;
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 if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM) {
212 /* Format changed so keep sample rate and number of channels
213 * the same and just change the bit depth
215 for (i = ndlo; i < ndhi; i++) {
216 wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
217 wfx.nChannels = lpDesc->lpFormat->nChannels;
218 TRY(wfx.nSamplesPerSec, 16);
219 TRY(wfx.nSamplesPerSec, 8);
221 } else {
222 /* Our resampling algorithm is quite primitive so first try
223 * to just change the bit depth and number of channels
225 for (i = ndlo; i < ndhi; i++) {
226 wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
227 wfx.nChannels = lpDesc->lpFormat->nChannels;
228 TRY(wfx.nSamplesPerSec, 16);
229 TRY(wfx.nSamplesPerSec, 8);
230 wfx.nChannels ^= 3;
231 TRY(wfx.nSamplesPerSec, 16);
232 TRY(wfx.nSamplesPerSec, 8);
235 for (i = ndlo; i < ndhi; i++) {
236 /* first try with same stereo/mono option as source */
237 wfx.nChannels = lpDesc->lpFormat->nChannels;
238 TRY(96000, 16);
239 TRY(48000, 16);
240 TRY(44100, 16);
241 TRY(22050, 16);
242 TRY(11025, 16);
244 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
245 wfx.nChannels ^= 3;
246 TRY(96000, 16);
247 TRY(48000, 16);
248 TRY(44100, 16);
249 TRY(22050, 16);
250 TRY(11025, 16);
252 /* first try with same stereo/mono option as source */
253 wfx.nChannels = lpDesc->lpFormat->nChannels;
254 TRY(96000, 8);
255 TRY(48000, 8);
256 TRY(44100, 8);
257 TRY(22050, 8);
258 TRY(11025, 8);
260 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
261 wfx.nChannels ^= 3;
262 TRY(96000, 8);
263 TRY(48000, 8);
264 TRY(44100, 8);
265 TRY(22050, 8);
266 TRY(11025, 8);
269 #undef TRY
272 HeapFree(GetProcessHeap(), 0, wom);
273 WARN("ret = WAVERR_BADFORMAT\n");
274 return WAVERR_BADFORMAT;
276 found:
277 if (dwFlags & WAVE_FORMAT_QUERY) {
278 *lpdwUser = 0L;
279 HeapFree(GetProcessHeap(), 0, wom);
280 } else {
281 *lpdwUser = (DWORD_PTR)wom;
283 return MMSYSERR_NOERROR;
284 error:
285 HeapFree(GetProcessHeap(), 0, wom);
286 if (res==ACMERR_NOTPOSSIBLE) {
287 WARN("ret = WAVERR_BADFORMAT\n");
288 return WAVERR_BADFORMAT;
290 WARN("ret = 0x%08x\n", res);
291 return res;
294 static DWORD wodClose(WAVEMAPDATA* wom)
296 DWORD ret;
298 TRACE("(%p)\n", wom);
300 ret = waveOutClose(wom->u.out.hInnerWave);
301 if (ret == MMSYSERR_NOERROR) {
302 if (wom->hAcmStream) {
303 ret = acmStreamClose(wom->hAcmStream, 0);
305 if (ret == MMSYSERR_NOERROR) {
306 HeapFree(GetProcessHeap(), 0, wom);
309 return ret;
312 static DWORD wodWrite(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
314 PACMSTREAMHEADER ash;
315 LPWAVEHDR lpWaveHdrDst;
317 TRACE("(%p %p %08x)\n", wom, lpWaveHdrSrc, dwParam2);
319 if (!wom->hAcmStream) {
320 return waveOutWrite(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
323 lpWaveHdrSrc->dwFlags |= WHDR_INQUEUE;
324 ash = (PACMSTREAMHEADER)lpWaveHdrSrc->reserved;
325 /* acmStreamConvert will actually check that the new size is less than initial size */
326 ash->cbSrcLength = lpWaveHdrSrc->dwBufferLength;
327 if (acmStreamConvert(wom->hAcmStream, ash, 0L) != MMSYSERR_NOERROR) {
328 WARN("acmStreamConvert failed\n");
329 return MMSYSERR_ERROR;
332 lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
333 if (ash->cbSrcLength > ash->cbSrcLengthUsed)
334 FIXME("Not all src buffer has been written, expect bogus sound\n");
335 else if (ash->cbSrcLength < ash->cbSrcLengthUsed)
336 ERR("CoDec has read more data than it is allowed to\n");
338 if (ash->cbDstLengthUsed == 0) {
339 /* something went wrong in decoding */
340 FIXME("Got 0 length\n");
341 return MMSYSERR_ERROR;
343 lpWaveHdrDst->dwBufferLength = ash->cbDstLengthUsed;
344 return waveOutWrite(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
347 static DWORD wodPrepare(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
349 PACMSTREAMHEADER ash;
350 DWORD size;
351 DWORD dwRet;
352 LPWAVEHDR lpWaveHdrDst;
354 TRACE("(%p %p %08x)\n", wom, lpWaveHdrSrc, dwParam2);
356 if (!wom->hAcmStream)
357 return waveOutPrepareHeader(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
359 if (acmStreamSize(wom->hAcmStream, lpWaveHdrSrc->dwBufferLength, &size, ACM_STREAMSIZEF_SOURCE) != MMSYSERR_NOERROR) {
360 WARN("acmStreamSize failed\n");
361 return MMSYSERR_ERROR;
364 ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
365 if (ash == NULL) {
366 WARN("no memory\n");
367 return MMSYSERR_NOMEM;
370 ash->cbStruct = sizeof(*ash);
371 ash->fdwStatus = 0L;
372 ash->dwUser = (DWORD_PTR)lpWaveHdrSrc;
373 ash->pbSrc = (LPBYTE)lpWaveHdrSrc->lpData;
374 ash->cbSrcLength = lpWaveHdrSrc->dwBufferLength;
375 /* ash->cbSrcLengthUsed */
376 ash->dwSrcUser = lpWaveHdrSrc->dwUser; /* FIXME ? */
377 ash->pbDst = (LPBYTE)ash + sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR);
378 ash->cbDstLength = size;
379 /* ash->cbDstLengthUsed */
380 ash->dwDstUser = 0; /* FIXME ? */
381 dwRet = acmStreamPrepareHeader(wom->hAcmStream, ash, 0L);
382 if (dwRet != MMSYSERR_NOERROR) {
383 WARN("acmStreamPrepareHeader failed\n");
384 goto errCleanUp;
387 lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
388 lpWaveHdrDst->lpData = (LPSTR)ash->pbDst;
389 lpWaveHdrDst->dwBufferLength = size; /* conversion is not done yet */
390 lpWaveHdrDst->dwFlags = 0;
391 lpWaveHdrDst->dwLoops = 0;
392 dwRet = waveOutPrepareHeader(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
393 if (dwRet != MMSYSERR_NOERROR) {
394 WARN("waveOutPrepareHeader failed\n");
395 goto errCleanUp;
398 lpWaveHdrSrc->reserved = (DWORD_PTR)ash;
399 lpWaveHdrSrc->dwFlags = WHDR_PREPARED;
400 TRACE("=> (0)\n");
401 return MMSYSERR_NOERROR;
402 errCleanUp:
403 TRACE("=> (%d)\n", dwRet);
404 HeapFree(GetProcessHeap(), 0, ash);
405 return dwRet;
408 static DWORD wodUnprepare(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
410 PACMSTREAMHEADER ash;
411 LPWAVEHDR lpWaveHdrDst;
412 DWORD dwRet1, dwRet2;
414 TRACE("(%p %p %08x)\n", wom, lpWaveHdrSrc, dwParam2);
416 if (!wom->hAcmStream) {
417 return waveOutUnprepareHeader(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
419 ash = (PACMSTREAMHEADER)lpWaveHdrSrc->reserved;
420 dwRet1 = acmStreamUnprepareHeader(wom->hAcmStream, ash, 0L);
422 lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
423 dwRet2 = waveOutUnprepareHeader(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
425 HeapFree(GetProcessHeap(), 0, ash);
427 lpWaveHdrSrc->dwFlags &= ~WHDR_PREPARED;
428 return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
431 static DWORD wodGetPosition(WAVEMAPDATA* wom, LPMMTIME lpTime, DWORD dwParam2)
433 DWORD val;
434 MMTIME timepos;
435 TRACE("(%p %p %08x)\n", wom, lpTime, dwParam2);
437 timepos = *lpTime;
439 /* For TIME_MS, we're going to recalculate using TIME_BYTES */
440 if (lpTime->wType == TIME_MS)
441 timepos.wType = TIME_BYTES;
443 /* This can change timepos.wType if the requested type is not supported */
444 val = waveOutGetPosition(wom->u.out.hInnerWave, &timepos, dwParam2);
446 if (timepos.wType == TIME_BYTES)
448 DWORD dwInnerSamplesPerOuter = wom->nSamplesPerSecInner / wom->nSamplesPerSecOuter;
449 if (dwInnerSamplesPerOuter > 0)
451 DWORD dwInnerBytesPerSample = wom->avgSpeedInner / wom->nSamplesPerSecInner;
452 DWORD dwInnerBytesPerOuterSample = dwInnerBytesPerSample * dwInnerSamplesPerOuter;
453 DWORD remainder = 0;
455 /* If we are up sampling (going from lower sample rate to higher),
456 ** we need to make a special accommodation for times when we've
457 ** written a partial output sample. This happens frequently
458 ** to us because we use msacm to do our up sampling, and it
459 ** will up sample on an unaligned basis.
460 ** For example, if you convert a 2 byte wide 8,000 'outer'
461 ** buffer to a 2 byte wide 48,000 inner device, you would
462 ** expect 2 bytes of input to produce 12 bytes of output.
463 ** Instead, msacm will produce 8 bytes of output.
464 ** But reporting our position as 1 byte of output is
465 ** nonsensical; the output buffer position needs to be
466 ** aligned on outer sample size, and aggressively rounded up.
468 remainder = timepos.u.cb % dwInnerBytesPerOuterSample;
469 if (remainder > 0)
471 timepos.u.cb -= remainder;
472 timepos.u.cb += dwInnerBytesPerOuterSample;
476 lpTime->u.cb = MulDiv(timepos.u.cb, wom->avgSpeedOuter, wom->avgSpeedInner);
478 /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */
479 if (lpTime->wType == TIME_MS)
480 lpTime->u.ms = MulDiv(lpTime->u.cb, 1000, wom->avgSpeedOuter);
481 else
482 lpTime->wType = TIME_BYTES;
484 else if (lpTime->wType == TIME_SAMPLES && timepos.wType == TIME_SAMPLES)
485 lpTime->u.sample = MulDiv(timepos.u.sample, wom->nSamplesPerSecOuter, wom->nSamplesPerSecInner);
486 else
487 /* other time types don't require conversion */
488 lpTime->u = timepos.u;
490 return val;
493 static DWORD wodGetDevCaps(UINT wDevID, WAVEMAPDATA* wom, LPWAVEOUTCAPSW lpWaveCaps, DWORD dwParam2)
495 static const WCHAR name[] = {'W','i','n','e',' ','w','a','v','e',' ','o','u','t',' ','m','a','p','p','e','r',0};
497 TRACE("(%04x %p %p %08x)\n",wDevID, wom, lpWaveCaps, dwParam2);
499 /* if opened low driver, forward message */
500 if (WAVEMAP_IsData(wom))
501 return waveOutGetDevCapsW((UINT_PTR)wom->u.out.hInnerWave, lpWaveCaps, dwParam2);
502 /* else if no drivers, nothing to map so return bad device */
503 if (waveOutGetNumDevs() == 0) {
504 WARN("bad device id\n");
505 return MMSYSERR_BADDEVICEID;
507 /* otherwise, return caps of mapper itself */
508 if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
509 WAVEOUTCAPSW woc;
510 woc.wMid = 0x00FF;
511 woc.wPid = 0x0001;
512 woc.vDriverVersion = 0x0332;
513 lstrcpyW(woc.szPname, name);
514 woc.dwFormats =
515 WAVE_FORMAT_96M08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16 |
516 WAVE_FORMAT_48M08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 |
517 WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
518 WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
519 WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
520 woc.wChannels = 2;
521 woc.dwSupport = WAVECAPS_VOLUME | WAVECAPS_LRVOLUME;
522 memcpy(lpWaveCaps, &woc, min(dwParam2, sizeof(woc)));
524 return MMSYSERR_NOERROR;
526 ERR("This shouldn't happen\n");
527 return MMSYSERR_ERROR;
530 static DWORD wodGetVolume(UINT wDevID, WAVEMAPDATA* wom, LPDWORD lpVol)
532 TRACE("(%04x %p %p)\n",wDevID, wom, lpVol);
534 if (WAVEMAP_IsData(wom))
535 return waveOutGetVolume(wom->u.out.hInnerWave, lpVol);
536 return MMSYSERR_NOERROR;
539 static DWORD wodSetVolume(UINT wDevID, WAVEMAPDATA* wom, DWORD vol)
541 TRACE("(%04x %p %08x)\n",wDevID, wom, vol);
543 if (WAVEMAP_IsData(wom))
544 return waveOutSetVolume(wom->u.out.hInnerWave, vol);
545 return MMSYSERR_NOERROR;
548 static DWORD wodPause(WAVEMAPDATA* wom)
550 TRACE("(%p)\n",wom);
552 return waveOutPause(wom->u.out.hInnerWave);
555 static DWORD wodRestart(WAVEMAPDATA* wom)
557 TRACE("(%p)\n",wom);
559 return waveOutRestart(wom->u.out.hInnerWave);
562 static DWORD wodReset(WAVEMAPDATA* wom)
564 TRACE("(%p)\n",wom);
566 return waveOutReset(wom->u.out.hInnerWave);
569 static DWORD wodBreakLoop(WAVEMAPDATA* wom)
571 TRACE("(%p)\n",wom);
573 return waveOutBreakLoop(wom->u.out.hInnerWave);
576 static DWORD wodMapperStatus(WAVEMAPDATA* wom, DWORD flags, LPVOID ptr)
578 UINT id;
579 DWORD ret = MMSYSERR_NOTSUPPORTED;
581 TRACE("(%p %08x %p)\n",wom, flags, ptr);
583 switch (flags) {
584 case WAVEOUT_MAPPER_STATUS_DEVICE:
585 ret = waveOutGetID(wom->u.out.hInnerWave, &id);
586 *(LPDWORD)ptr = id;
587 break;
588 case WAVEOUT_MAPPER_STATUS_MAPPED:
589 FIXME("Unsupported flag=%d\n", flags);
590 *(LPDWORD)ptr = 0; /* FIXME ?? */
591 break;
592 case WAVEOUT_MAPPER_STATUS_FORMAT:
593 FIXME("Unsupported flag=%d\n", flags);
594 /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
595 *(LPDWORD)ptr = 0;
596 break;
597 default:
598 FIXME("Unsupported flag=%d\n", flags);
599 *(LPDWORD)ptr = 0;
600 break;
602 return ret;
605 static DWORD wodMapperReconfigure(WAVEMAPDATA* wom, DWORD dwParam1, DWORD dwParam2)
607 FIXME("(%p %08x %08x) stub!\n", wom, dwParam1, dwParam2);
609 return MMSYSERR_NOERROR;
612 /**************************************************************************
613 * wodMessage (MSACM.@)
615 DWORD WINAPI WAVEMAP_wodMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
616 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
618 TRACE("(%u, %04X, %08lx, %08lx, %08lx);\n",
619 wDevID, wMsg, dwUser, dwParam1, dwParam2);
621 switch (wMsg) {
622 case DRVM_INIT:
623 case DRVM_EXIT:
624 case DRVM_ENABLE:
625 case DRVM_DISABLE:
626 /* FIXME: Pretend this is supported */
627 return 0;
628 case WODM_OPEN: return wodOpen ((DWORD_PTR *)dwUser, (LPWAVEOPENDESC)dwParam1,dwParam2);
629 case WODM_CLOSE: return wodClose ((WAVEMAPDATA*)dwUser);
630 case WODM_WRITE: return wodWrite ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
631 case WODM_PAUSE: return wodPause ((WAVEMAPDATA*)dwUser);
632 case WODM_GETPOS: return wodGetPosition ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1, dwParam2);
633 case WODM_BREAKLOOP: return wodBreakLoop ((WAVEMAPDATA*)dwUser);
634 case WODM_PREPARE: return wodPrepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
635 case WODM_UNPREPARE: return wodUnprepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
636 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEOUTCAPSW)dwParam1,dwParam2);
637 case WODM_GETNUMDEVS: return 1;
638 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
639 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
640 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
641 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
642 case WODM_GETVOLUME: return wodGetVolume (wDevID, (WAVEMAPDATA*)dwUser, (LPDWORD)dwParam1);
643 case WODM_SETVOLUME: return wodSetVolume (wDevID, (WAVEMAPDATA*)dwUser, dwParam1);
644 case WODM_RESTART: return wodRestart ((WAVEMAPDATA*)dwUser);
645 case WODM_RESET: return wodReset ((WAVEMAPDATA*)dwUser);
646 case WODM_MAPPER_STATUS: return wodMapperStatus ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
647 case DRVM_MAPPER_RECONFIGURE: return wodMapperReconfigure((WAVEMAPDATA*)dwUser, dwParam1, dwParam2);
648 /* known but not supported */
649 case DRV_QUERYDEVICEINTERFACESIZE:
650 case DRV_QUERYDEVICEINTERFACE:
651 return MMSYSERR_NOTSUPPORTED;
652 default:
653 FIXME("unknown message %d!\n", wMsg);
655 return MMSYSERR_NOTSUPPORTED;
658 /*======================================================================*
659 * WAVE IN part *
660 *======================================================================*/
662 static void CALLBACK widCallback(HWAVEIN hWave, UINT uMsg, DWORD_PTR dwInstance,
663 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
665 WAVEMAPDATA* wim = (WAVEMAPDATA*)dwInstance;
667 TRACE("(%p %u %lx %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
669 if (!WAVEMAP_IsData(wim)) {
670 ERR("Bad data\n");
671 return;
674 if (hWave != wim->u.in.hInnerWave && uMsg != WIM_OPEN)
675 ERR("Shouldn't happen (%p %p)\n", hWave, wim->u.in.hInnerWave);
677 switch (uMsg) {
678 case WIM_OPEN:
679 case WIM_CLOSE:
680 /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
681 break;
682 case WIM_DATA:
683 if (wim->hAcmStream) {
684 LPWAVEHDR lpWaveHdrSrc = (LPWAVEHDR)dwParam1;
685 PACMSTREAMHEADER ash = (PACMSTREAMHEADER)((LPSTR)lpWaveHdrSrc - sizeof(ACMSTREAMHEADER));
686 LPWAVEHDR lpWaveHdrDst = (LPWAVEHDR)ash->dwUser;
688 /* convert data just gotten from waveIn into requested format */
689 if (acmStreamConvert(wim->hAcmStream, ash, 0L) != MMSYSERR_NOERROR) {
690 ERR("ACM conversion failed\n");
691 return;
692 } else {
693 TRACE("Converted %d bytes into %d\n", ash->cbSrcLengthUsed, ash->cbDstLengthUsed);
695 /* and setup the wavehdr to return accordingly */
696 lpWaveHdrDst->dwFlags &= ~WHDR_INQUEUE;
697 lpWaveHdrDst->dwFlags |= WHDR_DONE;
698 lpWaveHdrDst->dwBytesRecorded = ash->cbDstLengthUsed;
699 dwParam1 = (DWORD_PTR)lpWaveHdrDst;
701 break;
702 default:
703 ERR("Unknown msg %u\n", uMsg);
706 DriverCallback(wim->dwCallback, HIWORD(wim->dwFlags), (HDRVR)wim->u.in.hOuterWave,
707 uMsg, wim->dwClientInstance, dwParam1, dwParam2);
710 static DWORD widOpenHelper(WAVEMAPDATA* wim, UINT idx,
711 LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
712 DWORD dwFlags)
714 DWORD ret;
716 TRACE("(%p %04x %p %p %08x)\n", wim, idx, lpDesc, lpwfx, dwFlags);
718 /* source is always PCM, so the formulas below apply */
719 lpwfx->nBlockAlign = (lpwfx->nChannels * lpwfx->wBitsPerSample) / 8;
720 lpwfx->nAvgBytesPerSec = lpwfx->nSamplesPerSec * lpwfx->nBlockAlign;
721 if (dwFlags & WAVE_FORMAT_QUERY) {
722 ret = acmStreamOpen(NULL, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY);
723 } else {
724 ret = acmStreamOpen(&wim->hAcmStream, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, 0L);
726 if (ret == MMSYSERR_NOERROR) {
727 ret = waveInOpen(&wim->u.in.hInnerWave, idx, lpwfx, (DWORD_PTR)widCallback,
728 (DWORD_PTR)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION);
729 if (ret != MMSYSERR_NOERROR && !(dwFlags & WAVE_FORMAT_QUERY)) {
730 acmStreamClose(wim->hAcmStream, 0);
731 wim->hAcmStream = 0;
734 TRACE("ret = %08x\n", ret);
735 return ret;
738 static DWORD widOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
740 UINT ndlo, ndhi;
741 UINT i;
742 WAVEMAPDATA* wim = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
743 DWORD res;
745 TRACE("(%p %p %08x)\n", lpdwUser, lpDesc, dwFlags);
747 if (!wim) {
748 WARN("no memory\n");
749 return MMSYSERR_NOMEM;
752 wim->self = wim;
753 wim->dwCallback = lpDesc->dwCallback;
754 wim->dwFlags = dwFlags;
755 wim->dwClientInstance = lpDesc->dwInstance;
756 wim->u.in.hOuterWave = (HWAVEIN)lpDesc->hWave;
758 ndhi = waveInGetNumDevs();
759 if (dwFlags & WAVE_MAPPED) {
760 if (lpDesc->uMappedDeviceID >= ndhi) return MMSYSERR_INVALPARAM;
761 ndlo = lpDesc->uMappedDeviceID;
762 ndhi = ndlo + 1;
763 dwFlags &= ~WAVE_MAPPED;
764 } else {
765 ndlo = 0;
768 wim->avgSpeedOuter = wim->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec;
769 wim->nSamplesPerSecOuter = wim->nSamplesPerSecInner = lpDesc->lpFormat->nSamplesPerSec;
771 for (i = ndlo; i < ndhi; i++) {
772 if (waveInOpen(&wim->u.in.hInnerWave, i, lpDesc->lpFormat, (DWORD_PTR)widCallback,
773 (DWORD_PTR)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) {
774 wim->hAcmStream = 0;
775 goto found;
779 if ((dwFlags & WAVE_FORMAT_DIRECT) == 0)
781 WAVEFORMATEX wfx;
783 wfx.wFormatTag = WAVE_FORMAT_PCM;
784 wfx.cbSize = 0; /* normally, this field is not used for PCM format, just in case */
785 /* try some ACM stuff */
787 #define TRY(sps,bps) wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
788 switch (res=widOpenHelper(wim, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT)) { \
789 case MMSYSERR_NOERROR: wim->avgSpeedInner = wfx.nAvgBytesPerSec; wim->nSamplesPerSecInner = wfx.nSamplesPerSec; goto found; \
790 case WAVERR_BADFORMAT: break; \
791 default: goto error; \
794 for (i = ndlo; i < ndhi; i++) {
795 wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
796 /* first try with same stereo/mono option as source */
797 wfx.nChannels = lpDesc->lpFormat->nChannels;
798 TRY(wfx.nSamplesPerSec, 16);
799 TRY(wfx.nSamplesPerSec, 8);
800 wfx.nChannels ^= 3;
801 TRY(wfx.nSamplesPerSec, 16);
802 TRY(wfx.nSamplesPerSec, 8);
805 for (i = ndlo; i < ndhi; i++) {
806 wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
807 /* first try with same stereo/mono option as source */
808 wfx.nChannels = lpDesc->lpFormat->nChannels;
809 TRY(96000, 16);
810 TRY(48000, 16);
811 TRY(44100, 16);
812 TRY(22050, 16);
813 TRY(11025, 16);
815 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
816 wfx.nChannels ^= 3;
817 TRY(96000, 16);
818 TRY(48000, 16);
819 TRY(44100, 16);
820 TRY(22050, 16);
821 TRY(11025, 16);
823 /* first try with same stereo/mono option as source */
824 wfx.nChannels = lpDesc->lpFormat->nChannels;
825 TRY(96000, 8);
826 TRY(48000, 8);
827 TRY(44100, 8);
828 TRY(22050, 8);
829 TRY(11025, 8);
831 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
832 wfx.nChannels ^= 3;
833 TRY(96000, 8);
834 TRY(48000, 8);
835 TRY(44100, 8);
836 TRY(22050, 8);
837 TRY(11025, 8);
839 #undef TRY
842 HeapFree(GetProcessHeap(), 0, wim);
843 WARN("ret = WAVERR_BADFORMAT\n");
844 return WAVERR_BADFORMAT;
845 found:
846 if (dwFlags & WAVE_FORMAT_QUERY) {
847 *lpdwUser = 0L;
848 HeapFree(GetProcessHeap(), 0, wim);
849 } else {
850 *lpdwUser = (DWORD_PTR)wim;
852 TRACE("Ok (stream=%p)\n", wim->hAcmStream);
853 return MMSYSERR_NOERROR;
854 error:
855 HeapFree(GetProcessHeap(), 0, wim);
856 if (res==ACMERR_NOTPOSSIBLE) {
857 WARN("ret = WAVERR_BADFORMAT\n");
858 return WAVERR_BADFORMAT;
860 WARN("ret = 0x%08x\n", res);
861 return res;
864 static DWORD widClose(WAVEMAPDATA* wim)
866 DWORD ret;
868 TRACE("(%p)\n", wim);
870 ret = waveInClose(wim->u.in.hInnerWave);
871 if (ret == MMSYSERR_NOERROR) {
872 if (wim->hAcmStream) {
873 ret = acmStreamClose(wim->hAcmStream, 0);
875 if (ret == MMSYSERR_NOERROR) {
876 HeapFree(GetProcessHeap(), 0, wim);
879 return ret;
882 static DWORD widAddBuffer(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
884 PACMSTREAMHEADER ash;
885 LPWAVEHDR lpWaveHdrSrc;
887 TRACE("(%p %p %08x)\n", wim, lpWaveHdrDst, dwParam2);
889 if (!wim->hAcmStream) {
890 return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
893 lpWaveHdrDst->dwFlags |= WHDR_INQUEUE;
894 ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
896 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
897 return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
900 static DWORD widPrepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
902 PACMSTREAMHEADER ash;
903 DWORD size;
904 DWORD dwRet;
905 LPWAVEHDR lpWaveHdrSrc;
907 TRACE("(%p %p %08x)\n", wim, lpWaveHdrDst, dwParam2);
909 if (!wim->hAcmStream) {
910 return waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
912 if (acmStreamSize(wim->hAcmStream, lpWaveHdrDst->dwBufferLength, &size,
913 ACM_STREAMSIZEF_DESTINATION) != MMSYSERR_NOERROR) {
914 WARN("acmStreamSize failed\n");
915 return MMSYSERR_ERROR;
918 ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
919 if (ash == NULL) {
920 WARN("no memory\n");
921 return MMSYSERR_NOMEM;
924 ash->cbStruct = sizeof(*ash);
925 ash->fdwStatus = 0L;
926 ash->dwUser = (DWORD_PTR)lpWaveHdrDst;
927 ash->pbSrc = (LPBYTE)ash + sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR);
928 ash->cbSrcLength = size;
929 /* ash->cbSrcLengthUsed */
930 ash->dwSrcUser = 0L; /* FIXME ? */
931 ash->pbDst = (LPBYTE)lpWaveHdrDst->lpData;
932 ash->cbDstLength = lpWaveHdrDst->dwBufferLength;
933 /* ash->cbDstLengthUsed */
934 ash->dwDstUser = lpWaveHdrDst->dwUser; /* FIXME ? */
935 dwRet = acmStreamPrepareHeader(wim->hAcmStream, ash, 0L);
936 if (dwRet != MMSYSERR_NOERROR) {
937 WARN("acmStreamPrepareHeader failed\n");
938 goto errCleanUp;
941 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
942 lpWaveHdrSrc->lpData = (LPSTR)ash->pbSrc;
943 lpWaveHdrSrc->dwBufferLength = size; /* conversion is not done yet */
944 lpWaveHdrSrc->dwFlags = 0;
945 lpWaveHdrSrc->dwLoops = 0;
946 dwRet = waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
947 if (dwRet != MMSYSERR_NOERROR) {
948 WARN("waveInPrepareHeader failed\n");
949 goto errCleanUp;
952 lpWaveHdrDst->reserved = (DWORD_PTR)ash;
953 lpWaveHdrDst->dwFlags = WHDR_PREPARED;
954 TRACE("=> (0)\n");
955 return MMSYSERR_NOERROR;
956 errCleanUp:
957 TRACE("=> (%d)\n", dwRet);
958 HeapFree(GetProcessHeap(), 0, ash);
959 return dwRet;
962 static DWORD widUnprepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
964 PACMSTREAMHEADER ash;
965 LPWAVEHDR lpWaveHdrSrc;
966 DWORD dwRet1, dwRet2;
968 TRACE("(%p %p %08x)\n", wim, lpWaveHdrDst, dwParam2);
970 if (!wim->hAcmStream) {
971 return waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
973 ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
974 dwRet1 = acmStreamUnprepareHeader(wim->hAcmStream, ash, 0L);
976 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
977 dwRet2 = waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
979 HeapFree(GetProcessHeap(), 0, ash);
981 lpWaveHdrDst->dwFlags &= ~WHDR_PREPARED;
982 return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
985 static DWORD widGetPosition(WAVEMAPDATA* wim, LPMMTIME lpTime, DWORD dwParam2)
987 DWORD val;
988 MMTIME timepos;
989 TRACE("(%p %p %08x)\n", wim, lpTime, dwParam2);
991 timepos = *lpTime;
993 /* For TIME_MS, we're going to recalculate using TIME_BYTES */
994 if (lpTime->wType == TIME_MS)
995 timepos.wType = TIME_BYTES;
997 /* This can change timepos.wType if the requested type is not supported */
998 val = waveInGetPosition(wim->u.in.hInnerWave, &timepos, dwParam2);
1000 if (timepos.wType == TIME_BYTES)
1002 DWORD dwInnerSamplesPerOuter = wim->nSamplesPerSecInner / wim->nSamplesPerSecOuter;
1003 if (dwInnerSamplesPerOuter > 0)
1005 DWORD dwInnerBytesPerSample = wim->avgSpeedInner / wim->nSamplesPerSecInner;
1006 DWORD dwInnerBytesPerOuterSample = dwInnerBytesPerSample * dwInnerSamplesPerOuter;
1007 DWORD remainder = 0;
1009 /* If we are up sampling (going from lower sample rate to higher),
1010 ** we need to make a special accommodation for times when we've
1011 ** written a partial output sample. This happens frequently
1012 ** to us because we use msacm to do our up sampling, and it
1013 ** will up sample on an unaligned basis.
1014 ** For example, if you convert a 2 byte wide 8,000 'outer'
1015 ** buffer to a 2 byte wide 48,000 inner device, you would
1016 ** expect 2 bytes of input to produce 12 bytes of output.
1017 ** Instead, msacm will produce 8 bytes of output.
1018 ** But reporting our position as 1 byte of output is
1019 ** nonsensical; the output buffer position needs to be
1020 ** aligned on outer sample size, and aggressively rounded up.
1022 remainder = timepos.u.cb % dwInnerBytesPerOuterSample;
1023 if (remainder > 0)
1025 timepos.u.cb -= remainder;
1026 timepos.u.cb += dwInnerBytesPerOuterSample;
1030 lpTime->u.cb = MulDiv(timepos.u.cb, wim->avgSpeedOuter, wim->avgSpeedInner);
1032 /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */
1033 if (lpTime->wType == TIME_MS)
1034 lpTime->u.ms = MulDiv(lpTime->u.cb, 1000, wim->avgSpeedOuter);
1035 else
1036 lpTime->wType = TIME_BYTES;
1038 else if (lpTime->wType == TIME_SAMPLES && timepos.wType == TIME_SAMPLES)
1039 lpTime->u.sample = MulDiv(timepos.u.sample, wim->nSamplesPerSecOuter, wim->nSamplesPerSecInner);
1040 else
1041 /* other time types don't require conversion */
1042 lpTime->u = timepos.u;
1044 return val;
1047 static DWORD widGetDevCaps(UINT wDevID, WAVEMAPDATA* wim, LPWAVEINCAPSW lpWaveCaps, DWORD dwParam2)
1049 TRACE("(%04x, %p %p %08x)\n", wDevID, wim, lpWaveCaps, dwParam2);
1051 /* if opened low driver, forward message */
1052 if (WAVEMAP_IsData(wim))
1053 return waveInGetDevCapsW((UINT_PTR)wim->u.in.hInnerWave, lpWaveCaps, dwParam2);
1054 /* else if no drivers, nothing to map so return bad device */
1055 if (waveInGetNumDevs() == 0) {
1056 WARN("bad device id\n");
1057 return MMSYSERR_BADDEVICEID;
1059 /* otherwise, return caps of mapper itself */
1060 if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
1061 WAVEINCAPSW wic;
1062 static const WCHAR init[] = {'W','i','n','e',' ','w','a','v','e',' ','i','n',' ','m','a','p','p','e','r',0};
1063 wic.wMid = 0x00FF;
1064 wic.wPid = 0x0001;
1065 wic.vDriverVersion = 0x0001;
1066 strcpyW(wic.szPname, init);
1067 wic.dwFormats =
1068 WAVE_FORMAT_96M08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16 |
1069 WAVE_FORMAT_48M08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 |
1070 WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
1071 WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
1072 WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
1073 wic.wChannels = 2;
1074 memcpy(lpWaveCaps, &wic, min(dwParam2, sizeof(wic)));
1076 return MMSYSERR_NOERROR;
1078 ERR("This shouldn't happen\n");
1079 return MMSYSERR_ERROR;
1082 static DWORD widStop(WAVEMAPDATA* wim)
1084 TRACE("(%p)\n", wim);
1086 return waveInStop(wim->u.in.hInnerWave);
1089 static DWORD widStart(WAVEMAPDATA* wim)
1091 TRACE("(%p)\n", wim);
1093 return waveInStart(wim->u.in.hInnerWave);
1096 static DWORD widReset(WAVEMAPDATA* wim)
1098 TRACE("(%p)\n", wim);
1100 return waveInReset(wim->u.in.hInnerWave);
1103 static DWORD widMapperStatus(WAVEMAPDATA* wim, DWORD flags, LPVOID ptr)
1105 UINT id;
1106 DWORD ret = MMSYSERR_NOTSUPPORTED;
1108 TRACE("(%p %08x %p)\n", wim, flags, ptr);
1110 switch (flags) {
1111 case WAVEIN_MAPPER_STATUS_DEVICE:
1112 ret = waveInGetID(wim->u.in.hInnerWave, &id);
1113 *(LPDWORD)ptr = id;
1114 break;
1115 case WAVEIN_MAPPER_STATUS_MAPPED:
1116 FIXME("Unsupported yet flag=%d\n", flags);
1117 *(LPDWORD)ptr = 0; /* FIXME ?? */
1118 break;
1119 case WAVEIN_MAPPER_STATUS_FORMAT:
1120 FIXME("Unsupported flag=%d\n", flags);
1121 /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
1122 *(LPDWORD)ptr = 0; /* FIXME ?? */
1123 break;
1124 default:
1125 FIXME("Unsupported flag=%d\n", flags);
1126 *(LPDWORD)ptr = 0;
1127 break;
1129 return ret;
1132 static DWORD widMapperReconfigure(WAVEMAPDATA* wim, DWORD dwParam1, DWORD dwParam2)
1134 FIXME("(%p %08x %08x) stub!\n", wim, dwParam1, dwParam2);
1136 return MMSYSERR_NOERROR;
1139 /**************************************************************************
1140 * widMessage (MSACM.@)
1142 DWORD WINAPI WAVEMAP_widMessage(WORD wDevID, WORD wMsg, DWORD_PTR dwUser,
1143 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
1145 TRACE("(%u, %04X, %08lx, %08lx, %08lx);\n",
1146 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1148 switch (wMsg) {
1149 case DRVM_INIT:
1150 case DRVM_EXIT:
1151 case DRVM_ENABLE:
1152 case DRVM_DISABLE:
1153 /* FIXME: Pretend this is supported */
1154 return 0;
1156 case WIDM_OPEN: return widOpen ((LPDWORD)dwUser, (LPWAVEOPENDESC)dwParam1, dwParam2);
1157 case WIDM_CLOSE: return widClose ((WAVEMAPDATA*)dwUser);
1159 case WIDM_ADDBUFFER: return widAddBuffer ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
1160 case WIDM_PREPARE: return widPrepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
1161 case WIDM_UNPREPARE: return widUnprepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
1162 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEINCAPSW)dwParam1, dwParam2);
1163 case WIDM_GETNUMDEVS: return 1;
1164 case WIDM_GETPOS: return widGetPosition ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1, dwParam2);
1165 case WIDM_RESET: return widReset ((WAVEMAPDATA*)dwUser);
1166 case WIDM_START: return widStart ((WAVEMAPDATA*)dwUser);
1167 case WIDM_STOP: return widStop ((WAVEMAPDATA*)dwUser);
1168 case WIDM_MAPPER_STATUS: return widMapperStatus ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
1169 case DRVM_MAPPER_RECONFIGURE: return widMapperReconfigure((WAVEMAPDATA*)dwUser, dwParam1, dwParam2);
1170 /* known but not supported */
1171 case DRV_QUERYDEVICEINTERFACESIZE:
1172 case DRV_QUERYDEVICEINTERFACE:
1173 return MMSYSERR_NOTSUPPORTED;
1174 default:
1175 FIXME("unknown message %u!\n", wMsg);
1177 return MMSYSERR_NOTSUPPORTED;
1180 /*======================================================================*
1181 * Driver part *
1182 *======================================================================*/
1184 static struct WINE_WAVEMAP* oss = NULL;
1186 /**************************************************************************
1187 * WAVEMAP_drvOpen [internal]
1189 static LRESULT WAVEMAP_drvOpen(LPSTR str)
1191 TRACE("(%p)\n", str);
1193 if (oss)
1194 return 0;
1196 /* I know, this is ugly, but who cares... */
1197 oss = (struct WINE_WAVEMAP*)1;
1198 return 1;
1201 /**************************************************************************
1202 * WAVEMAP_drvClose [internal]
1204 static LRESULT WAVEMAP_drvClose(DWORD_PTR dwDevID)
1206 TRACE("(%08lx)\n", dwDevID);
1208 if (oss) {
1209 oss = NULL;
1210 return 1;
1212 return 0;
1215 /**************************************************************************
1216 * DriverProc (MSACM.@)
1218 LRESULT CALLBACK WAVEMAP_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
1219 LPARAM dwParam1, LPARAM dwParam2)
1221 TRACE("(%08lX, %p, %08X, %08lX, %08lX)\n",
1222 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1224 switch(wMsg) {
1225 case DRV_LOAD: return 1;
1226 case DRV_FREE: return 1;
1227 case DRV_OPEN: return WAVEMAP_drvOpen((LPSTR)dwParam1);
1228 case DRV_CLOSE: return WAVEMAP_drvClose(dwDevID);
1229 case DRV_ENABLE: return 1;
1230 case DRV_DISABLE: return 1;
1231 case DRV_QUERYCONFIGURE: return 1;
1232 case DRV_CONFIGURE: MessageBoxA(0, "WAVEMAP MultiMedia Driver !", "Wave mapper Driver", MB_OK); return 1;
1233 case DRV_INSTALL: return DRVCNF_RESTART;
1234 case DRV_REMOVE: return DRVCNF_RESTART;
1235 default:
1236 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);