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 val = waveOutGetPosition(wom->u.out.hInnerWave, &timepos, dwParam2);
433 if (lpTime->wType == TIME_BYTES || lpTime->wType == TIME_MS)
435 DWORD dwInnerSamplesPerOuter = wom->nSamplesPerSecInner / wom->nSamplesPerSecOuter;
436 if (dwInnerSamplesPerOuter > 0)
438 DWORD dwInnerBytesPerSample = wom->avgSpeedInner / wom->nSamplesPerSecInner;
439 DWORD dwInnerBytesPerOuterSample = dwInnerBytesPerSample * dwInnerSamplesPerOuter;
442 /* If we are up sampling (going from lower sample rate to higher),
443 ** we need to make a special accomodation for times when we've
444 ** written a partial output sample. This happens frequently
445 ** to us because we use msacm to do our up sampling, and it
446 ** will up sample on an unaligned basis.
447 ** For example, if you convert a 2 byte wide 8,000 'outer'
448 ** buffer to a 2 byte wide 48,000 inner device, you would
449 ** expect 2 bytes of input to produce 12 bytes of output.
450 ** Instead, msacm will produce 8 bytes of output.
451 ** But reporting our position as 1 byte of output is
452 ** nonsensical; the output buffer position needs to be
453 ** aligned on outer sample size, and aggressively rounded up.
455 remainder = timepos.u.cb % dwInnerBytesPerOuterSample;
458 timepos.u.cb -= remainder;
459 timepos.u.cb += dwInnerBytesPerOuterSample;
463 lpTime->u.cb = MulDiv(timepos.u.cb, wom->avgSpeedOuter, wom->avgSpeedInner);
465 /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */
466 if (lpTime->wType == TIME_MS)
467 lpTime->u.cb = MulDiv(lpTime->u.cb, 1000, wom->avgSpeedOuter);
469 else if (lpTime->wType == TIME_SAMPLES)
470 lpTime->u.cb = MulDiv(timepos.u.cb, wom->nSamplesPerSecOuter, wom->nSamplesPerSecInner);
472 /* other time types don't require conversion */
473 lpTime->u = timepos.u;
478 static DWORD wodGetDevCaps(UINT wDevID, WAVEMAPDATA* wom, LPWAVEOUTCAPSW lpWaveCaps, DWORD dwParam2)
480 static const WCHAR name[] = {'W','i','n','e',' ','w','a','v','e',' ','o','u','t',' ','m','a','p','p','e','r',0};
482 TRACE("(%04x %p %p %08lx)\n",wDevID, wom, lpWaveCaps, dwParam2);
484 /* if opened low driver, forward message */
485 if (WAVEMAP_IsData(wom))
486 return waveOutGetDevCapsW((UINT)wom->u.out.hInnerWave, lpWaveCaps, dwParam2);
487 /* else if no drivers, nothing to map so return bad device */
488 if (waveOutGetNumDevs() == 0) {
489 WARN("bad device id\n");
490 return MMSYSERR_BADDEVICEID;
492 /* otherwise, return caps of mapper itself */
493 if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
497 woc.vDriverVersion = 0x0100;
498 lstrcpyW(woc.szPname, name);
500 WAVE_FORMAT_96M08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16 |
501 WAVE_FORMAT_48M08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 |
502 WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
503 WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
504 WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
506 woc.dwSupport = WAVECAPS_VOLUME | WAVECAPS_LRVOLUME;
507 memcpy(lpWaveCaps, &woc, min(dwParam2, sizeof(woc)));
509 return MMSYSERR_NOERROR;
511 ERR("This shouldn't happen\n");
512 return MMSYSERR_ERROR;
515 static DWORD wodGetVolume(UINT wDevID, WAVEMAPDATA* wom, LPDWORD lpVol)
517 TRACE("(%04x %p %p)\n",wDevID, wom, lpVol);
519 if (WAVEMAP_IsData(wom))
520 return waveOutGetVolume(wom->u.out.hInnerWave, lpVol);
521 return MMSYSERR_NOERROR;
524 static DWORD wodSetVolume(UINT wDevID, WAVEMAPDATA* wom, DWORD vol)
526 TRACE("(%04x %p %08lx)\n",wDevID, wom, vol);
528 if (WAVEMAP_IsData(wom))
529 return waveOutSetVolume(wom->u.out.hInnerWave, vol);
530 return MMSYSERR_NOERROR;
533 static DWORD wodPause(WAVEMAPDATA* wom)
537 return waveOutPause(wom->u.out.hInnerWave);
540 static DWORD wodRestart(WAVEMAPDATA* wom)
544 return waveOutRestart(wom->u.out.hInnerWave);
547 static DWORD wodReset(WAVEMAPDATA* wom)
551 return waveOutReset(wom->u.out.hInnerWave);
554 static DWORD wodBreakLoop(WAVEMAPDATA* wom)
558 return waveOutBreakLoop(wom->u.out.hInnerWave);
561 static DWORD wodMapperStatus(WAVEMAPDATA* wom, DWORD flags, LPVOID ptr)
564 DWORD ret = MMSYSERR_NOTSUPPORTED;
566 TRACE("(%p %08lx %p)\n",wom, flags, ptr);
569 case WAVEOUT_MAPPER_STATUS_DEVICE:
570 ret = waveOutGetID(wom->u.out.hInnerWave, &id);
573 case WAVEOUT_MAPPER_STATUS_MAPPED:
574 FIXME("Unsupported flag=%ld\n", flags);
575 *(LPDWORD)ptr = 0; /* FIXME ?? */
577 case WAVEOUT_MAPPER_STATUS_FORMAT:
578 FIXME("Unsupported flag=%ld\n", flags);
579 /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
583 FIXME("Unsupported flag=%ld\n", flags);
590 /**************************************************************************
591 * wodMessage (MSACM.@)
593 DWORD WINAPI WAVEMAP_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
594 DWORD dwParam1, DWORD dwParam2)
596 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
597 wDevID, wMsg, dwUser, dwParam1, dwParam2);
604 /* FIXME: Pretend this is supported */
606 case WODM_OPEN: return wodOpen ((LPDWORD)dwUser, (LPWAVEOPENDESC)dwParam1,dwParam2);
607 case WODM_CLOSE: return wodClose ((WAVEMAPDATA*)dwUser);
608 case WODM_WRITE: return wodWrite ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
609 case WODM_PAUSE: return wodPause ((WAVEMAPDATA*)dwUser);
610 case WODM_GETPOS: return wodGetPosition ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1, dwParam2);
611 case WODM_BREAKLOOP: return wodBreakLoop ((WAVEMAPDATA*)dwUser);
612 case WODM_PREPARE: return wodPrepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
613 case WODM_UNPREPARE: return wodUnprepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
614 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEOUTCAPSW)dwParam1,dwParam2);
615 case WODM_GETNUMDEVS: return 1;
616 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
617 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
618 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
619 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
620 case WODM_GETVOLUME: return wodGetVolume (wDevID, (WAVEMAPDATA*)dwUser, (LPDWORD)dwParam1);
621 case WODM_SETVOLUME: return wodSetVolume (wDevID, (WAVEMAPDATA*)dwUser, dwParam1);
622 case WODM_RESTART: return wodRestart ((WAVEMAPDATA*)dwUser);
623 case WODM_RESET: return wodReset ((WAVEMAPDATA*)dwUser);
624 case WODM_MAPPER_STATUS: return wodMapperStatus ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
625 /* known but not supported */
626 case DRV_QUERYDEVICEINTERFACESIZE:
627 case DRV_QUERYDEVICEINTERFACE:
628 return MMSYSERR_NOTSUPPORTED;
630 FIXME("unknown message %d!\n", wMsg);
632 return MMSYSERR_NOTSUPPORTED;
635 /*======================================================================*
637 *======================================================================*/
639 static void CALLBACK widCallback(HWAVEIN hWave, UINT uMsg, DWORD dwInstance,
640 DWORD dwParam1, DWORD dwParam2)
642 WAVEMAPDATA* wim = (WAVEMAPDATA*)dwInstance;
644 TRACE("(%p %u %ld %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
646 if (!WAVEMAP_IsData(wim)) {
651 if (hWave != wim->u.in.hInnerWave && uMsg != WIM_OPEN)
652 ERR("Shouldn't happen (%p %p)\n", hWave, wim->u.in.hInnerWave);
657 /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
660 if (wim->hAcmStream) {
661 LPWAVEHDR lpWaveHdrSrc = (LPWAVEHDR)dwParam1;
662 PACMSTREAMHEADER ash = (PACMSTREAMHEADER)((LPSTR)lpWaveHdrSrc - sizeof(ACMSTREAMHEADER));
663 LPWAVEHDR lpWaveHdrDst = (LPWAVEHDR)ash->dwUser;
665 /* convert data just gotten from waveIn into requested format */
666 if (acmStreamConvert(wim->hAcmStream, ash, 0L) != MMSYSERR_NOERROR) {
667 ERR("ACM conversion failed\n");
670 TRACE("Converted %ld bytes into %ld\n", ash->cbSrcLengthUsed, ash->cbDstLengthUsed);
672 /* and setup the wavehdr to return accordingly */
673 lpWaveHdrDst->dwFlags &= ~WHDR_INQUEUE;
674 lpWaveHdrDst->dwFlags |= WHDR_DONE;
675 lpWaveHdrDst->dwBytesRecorded = ash->cbDstLengthUsed;
676 dwParam1 = (DWORD)lpWaveHdrDst;
680 ERR("Unknown msg %u\n", uMsg);
683 DriverCallback(wim->dwCallback, HIWORD(wim->dwFlags), (HDRVR)wim->u.in.hOuterWave,
684 uMsg, wim->dwClientInstance, dwParam1, dwParam2);
687 static DWORD widOpenHelper(WAVEMAPDATA* wim, UINT idx,
688 LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
693 TRACE("(%p %04x %p %p %08lx)\n", wim, idx, lpDesc, lpwfx, dwFlags);
695 /* source is always PCM, so the formulas below apply */
696 lpwfx->nBlockAlign = (lpwfx->nChannels * lpwfx->wBitsPerSample) / 8;
697 lpwfx->nAvgBytesPerSec = lpwfx->nSamplesPerSec * lpwfx->nBlockAlign;
698 if (dwFlags & WAVE_FORMAT_QUERY) {
699 ret = acmStreamOpen(NULL, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY);
701 ret = acmStreamOpen(&wim->hAcmStream, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, 0L);
703 if (ret == MMSYSERR_NOERROR) {
704 ret = waveInOpen(&wim->u.in.hInnerWave, idx, lpwfx, (DWORD)widCallback,
705 (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION);
706 if (ret != MMSYSERR_NOERROR && !(dwFlags & WAVE_FORMAT_QUERY)) {
707 acmStreamClose(wim->hAcmStream, 0);
711 TRACE("ret = %08lx\n", ret);
715 static DWORD widOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
719 WAVEMAPDATA* wim = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
722 TRACE("(%p %p %08lx)\n", lpdwUser, lpDesc, dwFlags);
726 return MMSYSERR_NOMEM;
730 wim->dwCallback = lpDesc->dwCallback;
731 wim->dwFlags = dwFlags;
732 wim->dwClientInstance = lpDesc->dwInstance;
733 wim->u.in.hOuterWave = (HWAVEIN)lpDesc->hWave;
735 ndhi = waveInGetNumDevs();
736 if (dwFlags & WAVE_MAPPED) {
737 if (lpDesc->uMappedDeviceID >= ndhi) return MMSYSERR_INVALPARAM;
738 ndlo = lpDesc->uMappedDeviceID;
740 dwFlags &= ~WAVE_MAPPED;
745 wim->avgSpeedOuter = wim->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec;
746 wim->nSamplesPerSecOuter = wim->nSamplesPerSecInner = lpDesc->lpFormat->nSamplesPerSec;
748 for (i = ndlo; i < ndhi; i++) {
749 if (waveInOpen(&wim->u.in.hInnerWave, i, lpDesc->lpFormat, (DWORD)widCallback,
750 (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) {
756 if ((dwFlags & WAVE_FORMAT_DIRECT) == 0)
760 wfx.wFormatTag = WAVE_FORMAT_PCM;
761 wfx.cbSize = 0; /* normally, this field is not used for PCM format, just in case */
762 /* try some ACM stuff */
764 #define TRY(sps,bps) wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
765 switch (res=widOpenHelper(wim, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT)) { \
766 case MMSYSERR_NOERROR: wim->avgSpeedInner = wfx.nAvgBytesPerSec; wim->nSamplesPerSecInner = wfx.nSamplesPerSec; goto found; \
767 case WAVERR_BADFORMAT: break; \
768 default: goto error; \
771 for (i = ndlo; i < ndhi; i++) {
772 wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
773 /* first try with same stereo/mono option as source */
774 wfx.nChannels = lpDesc->lpFormat->nChannels;
775 TRY(wfx.nSamplesPerSec, 16);
776 TRY(wfx.nSamplesPerSec, 8);
778 TRY(wfx.nSamplesPerSec, 16);
779 TRY(wfx.nSamplesPerSec, 8);
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;
792 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
800 /* first try with same stereo/mono option as source */
801 wfx.nChannels = lpDesc->lpFormat->nChannels;
808 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
819 HeapFree(GetProcessHeap(), 0, wim);
820 WARN("ret = WAVERR_BADFORMAT\n");
821 return WAVERR_BADFORMAT;
823 if (dwFlags & WAVE_FORMAT_QUERY) {
825 HeapFree(GetProcessHeap(), 0, wim);
827 *lpdwUser = (DWORD)wim;
829 TRACE("Ok (stream=%08lx)\n", (DWORD)wim->hAcmStream);
830 return MMSYSERR_NOERROR;
832 HeapFree(GetProcessHeap(), 0, wim);
833 if (res==ACMERR_NOTPOSSIBLE) {
834 WARN("ret = WAVERR_BADFORMAT\n");
835 return WAVERR_BADFORMAT;
837 WARN("ret = 0x%08lx\n", res);
841 static DWORD widClose(WAVEMAPDATA* wim)
845 TRACE("(%p)\n", wim);
847 ret = waveInClose(wim->u.in.hInnerWave);
848 if (ret == MMSYSERR_NOERROR) {
849 if (wim->hAcmStream) {
850 ret = acmStreamClose(wim->hAcmStream, 0);
852 if (ret == MMSYSERR_NOERROR) {
853 HeapFree(GetProcessHeap(), 0, wim);
859 static DWORD widAddBuffer(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
861 PACMSTREAMHEADER ash;
862 LPWAVEHDR lpWaveHdrSrc;
864 TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
866 if (!wim->hAcmStream) {
867 return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
870 lpWaveHdrDst->dwFlags |= WHDR_INQUEUE;
871 ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
873 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
874 return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
877 static DWORD widPrepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
879 PACMSTREAMHEADER ash;
882 LPWAVEHDR lpWaveHdrSrc;
884 TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
886 if (!wim->hAcmStream) {
887 return waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
889 if (acmStreamSize(wim->hAcmStream, lpWaveHdrDst->dwBufferLength, &size,
890 ACM_STREAMSIZEF_DESTINATION) != MMSYSERR_NOERROR) {
891 WARN("acmStreamSize failed\n");
892 return MMSYSERR_ERROR;
895 ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
898 return MMSYSERR_NOMEM;
901 ash->cbStruct = sizeof(*ash);
903 ash->dwUser = (DWORD)lpWaveHdrDst;
904 ash->pbSrc = (LPSTR)ash + sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR);
905 ash->cbSrcLength = size;
906 /* ash->cbSrcLengthUsed */
907 ash->dwSrcUser = 0L; /* FIXME ? */
908 ash->pbDst = lpWaveHdrDst->lpData;
909 ash->cbDstLength = lpWaveHdrDst->dwBufferLength;
910 /* ash->cbDstLengthUsed */
911 ash->dwDstUser = lpWaveHdrDst->dwUser; /* FIXME ? */
912 dwRet = acmStreamPrepareHeader(wim->hAcmStream, ash, 0L);
913 if (dwRet != MMSYSERR_NOERROR) {
914 WARN("acmStreamPrepareHeader failed\n");
918 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
919 lpWaveHdrSrc->lpData = ash->pbSrc;
920 lpWaveHdrSrc->dwBufferLength = size; /* conversion is not done yet */
921 lpWaveHdrSrc->dwFlags = 0;
922 lpWaveHdrSrc->dwLoops = 0;
923 dwRet = waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
924 if (dwRet != MMSYSERR_NOERROR) {
925 WARN("waveInPrepareHeader failed\n");
929 lpWaveHdrDst->reserved = (DWORD)ash;
930 lpWaveHdrDst->dwFlags = WHDR_PREPARED;
932 return MMSYSERR_NOERROR;
934 TRACE("=> (%ld)\n", dwRet);
935 HeapFree(GetProcessHeap(), 0, ash);
939 static DWORD widUnprepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
941 PACMSTREAMHEADER ash;
942 LPWAVEHDR lpWaveHdrSrc;
943 DWORD dwRet1, dwRet2;
945 TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
947 if (!wim->hAcmStream) {
948 return waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
950 ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
951 dwRet1 = acmStreamUnprepareHeader(wim->hAcmStream, ash, 0L);
953 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
954 dwRet2 = waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
956 HeapFree(GetProcessHeap(), 0, ash);
958 lpWaveHdrDst->dwFlags &= ~WHDR_PREPARED;
959 return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
962 static DWORD widGetPosition(WAVEMAPDATA* wim, LPMMTIME lpTime, DWORD dwParam2)
966 TRACE("(%p %p %08lx)\n", wim, lpTime, dwParam2);
968 val = waveInGetPosition(wim->u.in.hInnerWave, lpTime, dwParam2);
969 if (lpTime->wType == TIME_BYTES)
970 lpTime->u.cb = MulDiv(lpTime->u.cb, wim->avgSpeedOuter, wim->avgSpeedInner);
971 if (lpTime->wType == TIME_SAMPLES)
972 lpTime->u.cb = MulDiv(lpTime->u.cb, wim->nSamplesPerSecOuter, wim->nSamplesPerSecInner);
973 /* other time types don't require conversion */
977 static DWORD widGetDevCaps(UINT wDevID, WAVEMAPDATA* wim, LPWAVEINCAPSW lpWaveCaps, DWORD dwParam2)
979 TRACE("(%04x, %p %p %08lx)\n", wDevID, wim, lpWaveCaps, dwParam2);
981 /* if opened low driver, forward message */
982 if (WAVEMAP_IsData(wim))
983 return waveInGetDevCapsW((UINT)wim->u.in.hInnerWave, lpWaveCaps, dwParam2);
984 /* else if no drivers, nothing to map so return bad device */
985 if (waveInGetNumDevs() == 0) {
986 WARN("bad device id\n");
987 return MMSYSERR_BADDEVICEID;
989 /* otherwise, return caps of mapper itself */
990 if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
992 static const WCHAR init[] = {'W','i','n','e',' ','w','a','v','e',' ','i','n',' ','m','a','p','p','e','r',0};
995 wic.vDriverVersion = 0x0001;
996 strcpyW(wic.szPname, init);
998 WAVE_FORMAT_96M08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16 |
999 WAVE_FORMAT_48M08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 |
1000 WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
1001 WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
1002 WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
1004 memcpy(lpWaveCaps, &wic, min(dwParam2, sizeof(wic)));
1006 return MMSYSERR_NOERROR;
1008 ERR("This shouldn't happen\n");
1009 return MMSYSERR_ERROR;
1012 static DWORD widStop(WAVEMAPDATA* wim)
1014 TRACE("(%p)\n", wim);
1016 return waveInStop(wim->u.in.hInnerWave);
1019 static DWORD widStart(WAVEMAPDATA* wim)
1021 TRACE("(%p)\n", wim);
1023 return waveInStart(wim->u.in.hInnerWave);
1026 static DWORD widReset(WAVEMAPDATA* wim)
1028 TRACE("(%p)\n", wim);
1030 return waveInReset(wim->u.in.hInnerWave);
1033 static DWORD widMapperStatus(WAVEMAPDATA* wim, DWORD flags, LPVOID ptr)
1036 DWORD ret = MMSYSERR_NOTSUPPORTED;
1038 TRACE("(%p %08lx %p)\n", wim, flags, ptr);
1041 case WAVEIN_MAPPER_STATUS_DEVICE:
1042 ret = waveInGetID(wim->u.in.hInnerWave, &id);
1045 case WAVEIN_MAPPER_STATUS_MAPPED:
1046 FIXME("Unsupported yet flag=%ld\n", flags);
1047 *(LPDWORD)ptr = 0; /* FIXME ?? */
1049 case WAVEIN_MAPPER_STATUS_FORMAT:
1050 FIXME("Unsupported flag=%ld\n", flags);
1051 /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
1052 *(LPDWORD)ptr = 0; /* FIXME ?? */
1055 FIXME("Unsupported flag=%ld\n", flags);
1062 /**************************************************************************
1063 * widMessage (MSACM.@)
1065 DWORD WINAPI WAVEMAP_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1066 DWORD dwParam1, DWORD dwParam2)
1068 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1069 wDevID, wMsg, dwUser, dwParam1, dwParam2);
1076 /* FIXME: Pretend this is supported */
1079 case WIDM_OPEN: return widOpen ((LPDWORD)dwUser, (LPWAVEOPENDESC)dwParam1, dwParam2);
1080 case WIDM_CLOSE: return widClose ((WAVEMAPDATA*)dwUser);
1082 case WIDM_ADDBUFFER: return widAddBuffer ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
1083 case WIDM_PREPARE: return widPrepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
1084 case WIDM_UNPREPARE: return widUnprepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
1085 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEINCAPSW)dwParam1, dwParam2);
1086 case WIDM_GETNUMDEVS: return 1;
1087 case WIDM_GETPOS: return widGetPosition ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1, dwParam2);
1088 case WIDM_RESET: return widReset ((WAVEMAPDATA*)dwUser);
1089 case WIDM_START: return widStart ((WAVEMAPDATA*)dwUser);
1090 case WIDM_STOP: return widStop ((WAVEMAPDATA*)dwUser);
1091 case WIDM_MAPPER_STATUS: return widMapperStatus ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
1092 /* known but not supported */
1093 case DRV_QUERYDEVICEINTERFACESIZE:
1094 case DRV_QUERYDEVICEINTERFACE:
1095 return MMSYSERR_NOTSUPPORTED;
1097 FIXME("unknown message %u!\n", wMsg);
1099 return MMSYSERR_NOTSUPPORTED;
1102 /*======================================================================*
1104 *======================================================================*/
1106 static struct WINE_WAVEMAP* oss = NULL;
1108 /**************************************************************************
1109 * WAVEMAP_drvOpen [internal]
1111 static DWORD WAVEMAP_drvOpen(LPSTR str)
1113 TRACE("(%p)\n", str);
1118 /* I know, this is ugly, but who cares... */
1119 oss = (struct WINE_WAVEMAP*)1;
1123 /**************************************************************************
1124 * WAVEMAP_drvClose [internal]
1126 static DWORD WAVEMAP_drvClose(DWORD dwDevID)
1128 TRACE("(%08lx)\n", dwDevID);
1137 /**************************************************************************
1138 * DriverProc (MSACM.@)
1140 LONG CALLBACK WAVEMAP_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1141 DWORD dwParam1, DWORD dwParam2)
1143 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1144 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1147 case DRV_LOAD: return 1;
1148 case DRV_FREE: return 1;
1149 case DRV_OPEN: return WAVEMAP_drvOpen((LPSTR)dwParam1);
1150 case DRV_CLOSE: return WAVEMAP_drvClose(dwDevID);
1151 case DRV_ENABLE: return 1;
1152 case DRV_DISABLE: return 1;
1153 case DRV_QUERYCONFIGURE: return 1;
1154 case DRV_CONFIGURE: MessageBoxA(0, "WAVEMAP MultiMedia Driver !", "Wave mapper Driver", MB_OK); return 1;
1155 case DRV_INSTALL: return DRVCNF_RESTART;
1156 case DRV_REMOVE: return DRVCNF_RESTART;
1158 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);