Return correct values for wave(In|Out)GetPosition even if playing non
[wine/multimedia.git] / dlls / winmm / wavemap / wavemap.c
blobae580116d2156099fd347c8123a187ec0fa39fc4
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 <string.h>
32 #include "windef.h"
33 #include "winbase.h"
34 #include "wingdi.h"
35 #include "winuser.h"
36 #include "mmddk.h"
37 #include "msacm.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(msacm);
42 typedef struct tagWAVEMAPDATA {
43 struct tagWAVEMAPDATA* self;
44 union {
45 struct {
46 HWAVEOUT hOuterWave;
47 HWAVEOUT hInnerWave;
48 } out;
49 struct {
50 HWAVEIN hOuterWave;
51 HWAVEIN hInnerWave;
52 } in;
53 } u;
54 HACMSTREAM hAcmStream;
55 /* needed data to filter callbacks. Only needed when hAcmStream is not 0 */
56 DWORD dwCallback;
57 DWORD dwClientInstance;
58 DWORD dwFlags;
59 /* ratio to compute position from a PCM playback to any format */
60 DWORD avgSpeedOuter;
61 DWORD avgSpeedInner;
62 } WAVEMAPDATA;
64 static BOOL WAVEMAP_IsData(WAVEMAPDATA* wm)
66 return (!IsBadReadPtr(wm, sizeof(WAVEMAPDATA)) && wm->self == wm);
69 /*======================================================================*
70 * WAVE OUT part *
71 *======================================================================*/
73 static void CALLBACK wodCallback(HWAVEOUT hWave, UINT uMsg, DWORD dwInstance,
74 DWORD dwParam1, DWORD dwParam2)
76 WAVEMAPDATA* wom = (WAVEMAPDATA*)dwInstance;
78 TRACE("(%p %u %ld %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
80 if (!WAVEMAP_IsData(wom)) {
81 ERR("Bad data\n");
82 return;
85 if (hWave != wom->u.out.hInnerWave && uMsg != WOM_OPEN)
86 ERR("Shouldn't happen (%p %p)\n", hWave, wom->u.out.hInnerWave);
88 switch (uMsg) {
89 case WOM_OPEN:
90 case WOM_CLOSE:
91 /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
92 break;
93 case WOM_DONE:
94 if (wom->hAcmStream) {
95 LPWAVEHDR lpWaveHdrDst = (LPWAVEHDR)dwParam1;
96 PACMSTREAMHEADER ash = (PACMSTREAMHEADER)((LPSTR)lpWaveHdrDst - sizeof(ACMSTREAMHEADER));
97 LPWAVEHDR lpWaveHdrSrc = (LPWAVEHDR)ash->dwUser;
99 lpWaveHdrSrc->dwFlags &= ~WHDR_INQUEUE;
100 lpWaveHdrSrc->dwFlags |= WHDR_DONE;
101 dwParam1 = (DWORD)lpWaveHdrSrc;
103 break;
104 default:
105 ERR("Unknown msg %u\n", uMsg);
108 DriverCallback(wom->dwCallback, HIWORD(wom->dwFlags), (HDRVR)wom->u.out.hOuterWave,
109 uMsg, wom->dwClientInstance, dwParam1, dwParam2);
112 /******************************************************************
113 * wodOpenHelper
117 static DWORD wodOpenHelper(WAVEMAPDATA* wom, UINT idx,
118 LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
119 DWORD dwFlags)
121 DWORD ret;
123 /* destination is always PCM, so the formulas below apply */
124 lpwfx->nBlockAlign = (lpwfx->nChannels * lpwfx->wBitsPerSample) / 8;
125 lpwfx->nAvgBytesPerSec = lpwfx->nSamplesPerSec * lpwfx->nBlockAlign;
126 if (dwFlags & WAVE_FORMAT_QUERY) {
127 ret = acmStreamOpen(NULL, 0, lpDesc->lpFormat, lpwfx, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY);
128 } else {
129 ret = acmStreamOpen(&wom->hAcmStream, 0, lpDesc->lpFormat, lpwfx, NULL, 0L, 0L, 0L);
131 if (ret == MMSYSERR_NOERROR) {
132 ret = waveOutOpen(&wom->u.out.hInnerWave, idx, lpwfx, (DWORD)wodCallback,
133 (DWORD)wom, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION);
134 if (ret != MMSYSERR_NOERROR && !(dwFlags & WAVE_FORMAT_QUERY)) {
135 acmStreamClose(wom->hAcmStream, 0);
136 wom->hAcmStream = 0;
139 return ret;
142 static DWORD wodOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
144 UINT ndlo, ndhi;
145 UINT i;
146 WAVEMAPDATA* wom = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
148 TRACE("(%p %p %08lx\n", lpdwUser, lpDesc, dwFlags);
150 if (!wom)
151 return MMSYSERR_NOMEM;
153 ndhi = waveOutGetNumDevs();
154 if (dwFlags & WAVE_MAPPED) {
155 if (lpDesc->uMappedDeviceID >= ndhi) return MMSYSERR_INVALPARAM;
156 ndlo = lpDesc->uMappedDeviceID;
157 ndhi = ndlo + 1;
158 dwFlags &= ~WAVE_MAPPED;
159 } else {
160 ndlo = 0;
162 wom->self = wom;
163 wom->dwCallback = lpDesc->dwCallback;
164 wom->dwFlags = dwFlags;
165 wom->dwClientInstance = lpDesc->dwInstance;
166 wom->u.out.hOuterWave = (HWAVEOUT)lpDesc->hWave;
167 wom->avgSpeedOuter = wom->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec;
169 for (i = ndlo; i < ndhi; i++) {
170 /* if no ACM stuff is involved, no need to handle callbacks at this
171 * level, this will be done transparently
173 if (waveOutOpen(&wom->u.out.hInnerWave, i, lpDesc->lpFormat, (DWORD)wodCallback,
174 (DWORD)wom, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) {
175 wom->hAcmStream = 0;
176 goto found;
180 if ((dwFlags & WAVE_FORMAT_DIRECT) == 0) {
181 WAVEFORMATEX wfx;
183 wfx.wFormatTag = WAVE_FORMAT_PCM;
184 wfx.cbSize = 0; /* normally, this field is not used for PCM format, just in case */
185 /* try some ACM stuff */
187 #define TRY(sps,bps) wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
188 if (wodOpenHelper(wom, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) \
189 {wom->avgSpeedInner = wfx.nAvgBytesPerSec; goto found;}
191 for (i = ndlo; i < ndhi; i++) {
192 /* first try with same stereo/mono option as source */
193 wfx.nChannels = lpDesc->lpFormat->nChannels;
194 TRY(44100, 16);
195 TRY(22050, 16);
196 TRY(11025, 16);
198 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
199 wfx.nChannels ^= 3;
200 TRY(44100, 16);
201 TRY(22050, 16);
202 TRY(11025, 16);
204 /* first try with same stereo/mono option as source */
205 wfx.nChannels = lpDesc->lpFormat->nChannels;
206 TRY(44100, 8);
207 TRY(22050, 8);
208 TRY(11025, 8);
210 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
211 wfx.nChannels ^= 3;
212 TRY(44100, 8);
213 TRY(22050, 8);
214 TRY(11025, 8);
216 #undef TRY
219 HeapFree(GetProcessHeap(), 0, wom);
220 return MMSYSERR_ALLOCATED;
221 found:
222 if (dwFlags & WAVE_FORMAT_QUERY) {
223 *lpdwUser = 0L;
224 HeapFree(GetProcessHeap(), 0, wom);
225 } else {
226 *lpdwUser = (DWORD)wom;
228 return MMSYSERR_NOERROR;
231 static DWORD wodClose(WAVEMAPDATA* wom)
233 DWORD ret = waveOutClose(wom->u.out.hInnerWave);
235 if (ret == MMSYSERR_NOERROR) {
236 if (wom->hAcmStream) {
237 ret = acmStreamClose(wom->hAcmStream, 0);
239 if (ret == MMSYSERR_NOERROR) {
240 HeapFree(GetProcessHeap(), 0, wom);
243 return ret;
246 static DWORD wodWrite(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
248 PACMSTREAMHEADER ash;
249 LPWAVEHDR lpWaveHdrDst;
251 if (!wom->hAcmStream) {
252 return waveOutWrite(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
255 lpWaveHdrSrc->dwFlags |= WHDR_INQUEUE;
256 ash = (PACMSTREAMHEADER)lpWaveHdrSrc->reserved;
257 /* acmStreamConvert will actually check that the new size is less than initial size */
258 ash->cbSrcLength = lpWaveHdrSrc->dwBufferLength;
259 if (acmStreamConvert(wom->hAcmStream, ash, 0L) != MMSYSERR_NOERROR)
260 return MMSYSERR_ERROR;
262 lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
263 if (ash->cbSrcLength > ash->cbSrcLengthUsed)
264 FIXME("Not all src buffer has been written, expect bogus sound\n");
265 else if (ash->cbSrcLength < ash->cbSrcLengthUsed)
266 ERR("CoDec has read more data than it is allowed to\n");
268 if (ash->cbDstLengthUsed == 0)
270 /* something went wrong in decoding */
271 FIXME("Got 0 length\n");
272 return MMSYSERR_ERROR;
274 lpWaveHdrDst->dwBufferLength = ash->cbDstLengthUsed;
275 return waveOutWrite(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
278 static DWORD wodPrepare(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
280 PACMSTREAMHEADER ash;
281 DWORD size;
282 DWORD dwRet;
283 LPWAVEHDR lpWaveHdrDst;
285 if (!wom->hAcmStream) {
286 return waveOutPrepareHeader(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
288 if (acmStreamSize(wom->hAcmStream, lpWaveHdrSrc->dwBufferLength, &size, ACM_STREAMSIZEF_SOURCE) != MMSYSERR_NOERROR)
289 return MMSYSERR_ERROR;
291 ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
292 if (ash == NULL)
293 return MMSYSERR_NOMEM;
295 ash->cbStruct = sizeof(*ash);
296 ash->fdwStatus = 0L;
297 ash->dwUser = (DWORD)lpWaveHdrSrc;
298 ash->pbSrc = lpWaveHdrSrc->lpData;
299 ash->cbSrcLength = lpWaveHdrSrc->dwBufferLength;
300 /* ash->cbSrcLengthUsed */
301 ash->dwSrcUser = lpWaveHdrSrc->dwUser; /* FIXME ? */
302 ash->pbDst = (LPSTR)ash + sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR);
303 ash->cbDstLength = size;
304 /* ash->cbDstLengthUsed */
305 ash->dwDstUser = 0; /* FIXME ? */
306 dwRet = acmStreamPrepareHeader(wom->hAcmStream, ash, 0L);
307 if (dwRet != MMSYSERR_NOERROR)
308 goto errCleanUp;
310 lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
311 lpWaveHdrDst->lpData = ash->pbDst;
312 lpWaveHdrDst->dwBufferLength = size; /* conversion is not done yet */
313 lpWaveHdrDst->dwFlags = 0;
314 lpWaveHdrDst->dwLoops = 0;
315 dwRet = waveOutPrepareHeader(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
316 if (dwRet != MMSYSERR_NOERROR)
317 goto errCleanUp;
319 lpWaveHdrSrc->reserved = (DWORD)ash;
320 lpWaveHdrSrc->dwFlags = WHDR_PREPARED;
321 TRACE("=> (0)\n");
322 return MMSYSERR_NOERROR;
323 errCleanUp:
324 TRACE("=> (%ld)\n", dwRet);
325 HeapFree(GetProcessHeap(), 0, ash);
326 return dwRet;
329 static DWORD wodUnprepare(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
331 PACMSTREAMHEADER ash;
332 LPWAVEHDR lpWaveHdrDst;
333 DWORD dwRet1, dwRet2;
335 if (!wom->hAcmStream) {
336 return waveOutUnprepareHeader(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
338 ash = (PACMSTREAMHEADER)lpWaveHdrSrc->reserved;
339 dwRet1 = acmStreamUnprepareHeader(wom->hAcmStream, ash, 0L);
341 lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
342 dwRet2 = waveOutUnprepareHeader(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
344 HeapFree(GetProcessHeap(), 0, ash);
346 lpWaveHdrSrc->dwFlags &= ~WHDR_PREPARED;
347 return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
350 static DWORD wodGetPosition(WAVEMAPDATA* wom, LPMMTIME lpTime, DWORD dwParam2)
352 DWORD val = waveOutGetPosition(wom->u.out.hInnerWave, lpTime, dwParam2);
353 if (lpTime->wType == TIME_BYTES)
354 val = MulDiv(val, wom->avgSpeedOuter, wom->avgSpeedInner);
355 /* other time types don't require conversion */
356 return val;
359 static DWORD wodGetDevCaps(UINT wDevID, WAVEMAPDATA* wom, LPWAVEOUTCAPSA lpWaveCaps, DWORD dwParam2)
361 /* if opened low driver, forward message */
362 if (WAVEMAP_IsData(wom))
363 return waveOutGetDevCapsA((UINT)wom->u.out.hInnerWave, lpWaveCaps, dwParam2);
364 /* otherwise, return caps of mapper itself */
365 if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
366 lpWaveCaps->wMid = 0x00FF;
367 lpWaveCaps->wPid = 0x0001;
368 lpWaveCaps->vDriverVersion = 0x0100;
369 strcpy(lpWaveCaps->szPname, "Wine wave out mapper");
370 lpWaveCaps->dwFormats =
371 WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
372 WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
373 WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
374 lpWaveCaps->wChannels = 2;
375 lpWaveCaps->dwSupport = WAVECAPS_VOLUME | WAVECAPS_LRVOLUME;
377 return MMSYSERR_NOERROR;
379 ERR("This shouldn't happen\n");
380 return MMSYSERR_ERROR;
383 static DWORD wodGetVolume(UINT wDevID, WAVEMAPDATA* wom, LPDWORD lpVol)
385 if (WAVEMAP_IsData(wom))
386 return waveOutGetVolume((UINT)wom->u.out.hInnerWave, lpVol);
387 return MMSYSERR_NOERROR;
390 static DWORD wodSetVolume(UINT wDevID, WAVEMAPDATA* wom, DWORD vol)
392 if (WAVEMAP_IsData(wom))
393 return waveOutSetVolume((UINT)wom->u.out.hInnerWave, vol);
394 return MMSYSERR_NOERROR;
397 static DWORD wodPause(WAVEMAPDATA* wom)
399 return waveOutPause(wom->u.out.hInnerWave);
402 static DWORD wodRestart(WAVEMAPDATA* wom)
404 return waveOutRestart(wom->u.out.hInnerWave);
407 static DWORD wodReset(WAVEMAPDATA* wom)
409 return waveOutReset(wom->u.out.hInnerWave);
412 static DWORD wodBreakLoop(WAVEMAPDATA* wom)
414 return waveOutBreakLoop(wom->u.out.hInnerWave);
417 static DWORD wodMapperStatus(WAVEMAPDATA* wom, DWORD flags, LPVOID ptr)
419 UINT id;
420 DWORD ret = MMSYSERR_NOTSUPPORTED;
422 switch (flags) {
423 case WAVEOUT_MAPPER_STATUS_DEVICE:
424 ret = waveOutGetID(wom->u.out.hInnerWave, &id);
425 *(LPDWORD)ptr = id;
426 break;
427 case WAVEOUT_MAPPER_STATUS_MAPPED:
428 FIXME("Unsupported flag=%ld\n", flags);
429 *(LPDWORD)ptr = 0; /* FIXME ?? */
430 break;
431 case WAVEOUT_MAPPER_STATUS_FORMAT:
432 FIXME("Unsupported flag=%ld\n", flags);
433 /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
434 *(LPDWORD)ptr = 0;
435 break;
436 default:
437 FIXME("Unsupported flag=%ld\n", flags);
438 *(LPDWORD)ptr = 0;
439 break;
441 return ret;
444 /**************************************************************************
445 * wodMessage (MSACM.@)
447 DWORD WINAPI WAVEMAP_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
448 DWORD dwParam1, DWORD dwParam2)
450 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
451 wDevID, wMsg, dwUser, dwParam1, dwParam2);
453 switch (wMsg) {
454 case DRVM_INIT:
455 case DRVM_EXIT:
456 case DRVM_ENABLE:
457 case DRVM_DISABLE:
458 /* FIXME: Pretend this is supported */
459 return 0;
460 case WODM_OPEN: return wodOpen ((LPDWORD)dwUser, (LPWAVEOPENDESC)dwParam1,dwParam2);
461 case WODM_CLOSE: return wodClose ((WAVEMAPDATA*)dwUser);
462 case WODM_WRITE: return wodWrite ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
463 case WODM_PAUSE: return wodPause ((WAVEMAPDATA*)dwUser);
464 case WODM_GETPOS: return wodGetPosition ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1, dwParam2);
465 case WODM_BREAKLOOP: return wodBreakLoop ((WAVEMAPDATA*)dwUser);
466 case WODM_PREPARE: return wodPrepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
467 case WODM_UNPREPARE: return wodUnprepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
468 case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEOUTCAPSA)dwParam1,dwParam2);
469 case WODM_GETNUMDEVS: return 1;
470 case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
471 case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
472 case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
473 case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
474 case WODM_GETVOLUME: return wodGetVolume (wDevID, (WAVEMAPDATA*)dwUser, (LPDWORD)dwParam1);
475 case WODM_SETVOLUME: return wodSetVolume (wDevID, (WAVEMAPDATA*)dwUser, dwParam1);
476 case WODM_RESTART: return wodRestart ((WAVEMAPDATA*)dwUser);
477 case WODM_RESET: return wodReset ((WAVEMAPDATA*)dwUser);
478 case WODM_MAPPER_STATUS: return wodMapperStatus ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
479 default:
480 FIXME("unknown message %d!\n", wMsg);
482 return MMSYSERR_NOTSUPPORTED;
485 /*======================================================================*
486 * WAVE IN part *
487 *======================================================================*/
489 static void CALLBACK widCallback(HWAVEIN hWave, UINT uMsg, DWORD dwInstance,
490 DWORD dwParam1, DWORD dwParam2)
492 WAVEMAPDATA* wim = (WAVEMAPDATA*)dwInstance;
494 TRACE("(%p %u %ld %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
496 if (!WAVEMAP_IsData(wim)) {
497 ERR("Bad data\n");
498 return;
501 if (hWave != wim->u.in.hInnerWave && uMsg != WIM_OPEN)
502 ERR("Shouldn't happen (%p %p)\n", hWave, wim->u.in.hInnerWave);
504 switch (uMsg) {
505 case WIM_OPEN:
506 case WIM_CLOSE:
507 /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
508 break;
509 case WIM_DATA:
510 if (wim->hAcmStream) {
511 LPWAVEHDR lpWaveHdrSrc = (LPWAVEHDR)dwParam1;
512 PACMSTREAMHEADER ash = (PACMSTREAMHEADER)((LPSTR)lpWaveHdrSrc - sizeof(ACMSTREAMHEADER));
513 LPWAVEHDR lpWaveHdrDst = (LPWAVEHDR)ash->dwUser;
515 /* convert data just gotten from waveIn into requested format */
516 if (acmStreamConvert(wim->hAcmStream, ash, 0L) != MMSYSERR_NOERROR) {
517 ERR("ACM conversion failed\n");
518 return;
519 } else {
520 TRACE("Converted %ld bytes into %ld\n", ash->cbSrcLengthUsed, ash->cbDstLengthUsed);
522 /* and setup the wavehdr to return accordingly */
523 lpWaveHdrDst->dwFlags &= ~WHDR_INQUEUE;
524 lpWaveHdrDst->dwFlags |= WHDR_DONE;
525 lpWaveHdrDst->dwBytesRecorded = ash->cbDstLengthUsed;
526 dwParam1 = (DWORD)lpWaveHdrDst;
528 break;
529 default:
530 ERR("Unknown msg %u\n", uMsg);
533 DriverCallback(wim->dwCallback, HIWORD(wim->dwFlags), (HDRVR)wim->u.in.hOuterWave,
534 uMsg, wim->dwClientInstance, dwParam1, dwParam2);
537 static DWORD widOpenHelper(WAVEMAPDATA* wim, UINT idx,
538 LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
539 DWORD dwFlags)
541 DWORD ret;
543 /* source is always PCM, so the formulas below apply */
544 lpwfx->nBlockAlign = (lpwfx->nChannels * lpwfx->wBitsPerSample) / 8;
545 lpwfx->nAvgBytesPerSec = lpwfx->nSamplesPerSec * lpwfx->nBlockAlign;
546 if (dwFlags & WAVE_FORMAT_QUERY) {
547 ret = acmStreamOpen(NULL, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY);
548 } else {
549 ret = acmStreamOpen(&wim->hAcmStream, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, 0L);
551 if (ret == MMSYSERR_NOERROR) {
552 ret = waveInOpen(&wim->u.in.hInnerWave, idx, lpwfx, (DWORD)widCallback,
553 (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION);
554 if (ret != MMSYSERR_NOERROR && !(dwFlags & WAVE_FORMAT_QUERY)) {
555 acmStreamClose(wim->hAcmStream, 0);
556 wim->hAcmStream = 0;
559 return ret;
562 static DWORD widOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
564 UINT ndlo, ndhi;
565 UINT i;
566 WAVEMAPDATA* wim = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
568 TRACE("(%p %p %08lx)\n", lpdwUser, lpDesc, dwFlags);
570 if (!wim)
571 return MMSYSERR_NOMEM;
573 wim->self = wim;
574 wim->dwCallback = lpDesc->dwCallback;
575 wim->dwFlags = dwFlags;
576 wim->dwClientInstance = lpDesc->dwInstance;
577 wim->u.in.hOuterWave = (HWAVEIN)lpDesc->hWave;
579 ndhi = waveOutGetNumDevs();
580 if (dwFlags & WAVE_MAPPED) {
581 if (lpDesc->uMappedDeviceID >= ndhi) return MMSYSERR_INVALPARAM;
582 ndlo = lpDesc->uMappedDeviceID;
583 ndhi = ndlo + 1;
584 dwFlags &= ~WAVE_MAPPED;
585 } else {
586 ndlo = 0;
589 wim->avgSpeedOuter = wim->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec;
591 for (i = ndlo; i < ndhi; i++) {
592 if (waveInOpen(&wim->u.in.hInnerWave, i, lpDesc->lpFormat, (DWORD)widCallback,
593 (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) {
594 wim->hAcmStream = 0;
595 goto found;
599 if ((dwFlags & WAVE_FORMAT_DIRECT) == 0)
601 WAVEFORMATEX wfx;
603 wfx.wFormatTag = WAVE_FORMAT_PCM;
604 wfx.cbSize = 0; /* normally, this field is not used for PCM format, just in case */
605 /* try some ACM stuff */
607 #define TRY(sps,bps) wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
608 if (widOpenHelper(wim, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) \
609 {wim->avgSpeedInner = wfx.nAvgBytesPerSec; goto found;}
611 for (i = ndlo; i < ndhi; i++) {
612 /* first try with same stereo/mono option as source */
613 wfx.nChannels = lpDesc->lpFormat->nChannels;
614 TRY(44100, 8);
615 TRY(22050, 8);
616 TRY(11025, 8);
618 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
619 wfx.nChannels ^= 3;
620 TRY(44100, 8);
621 TRY(22050, 8);
622 TRY(11025, 8);
624 #undef TRY
627 HeapFree(GetProcessHeap(), 0, wim);
628 return MMSYSERR_ALLOCATED;
629 found:
630 if (dwFlags & WAVE_FORMAT_QUERY) {
631 *lpdwUser = 0L;
632 HeapFree(GetProcessHeap(), 0, wim);
633 } else {
634 *lpdwUser = (DWORD)wim;
636 TRACE("Ok (stream=%08lx)\n", (DWORD)wim->hAcmStream);
637 return MMSYSERR_NOERROR;
640 static DWORD widClose(WAVEMAPDATA* wim)
642 DWORD ret = waveInClose(wim->u.in.hInnerWave);
644 if (ret == MMSYSERR_NOERROR) {
645 if (wim->hAcmStream) {
646 ret = acmStreamClose(wim->hAcmStream, 0);
648 if (ret == MMSYSERR_NOERROR) {
649 HeapFree(GetProcessHeap(), 0, wim);
652 return ret;
655 static DWORD widAddBuffer(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
657 PACMSTREAMHEADER ash;
658 LPWAVEHDR lpWaveHdrSrc;
660 if (!wim->hAcmStream) {
661 return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
664 lpWaveHdrDst->dwFlags |= WHDR_INQUEUE;
665 ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
667 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
668 return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
671 static DWORD widPrepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
673 PACMSTREAMHEADER ash;
674 DWORD size;
675 DWORD dwRet;
676 LPWAVEHDR lpWaveHdrSrc;
678 if (!wim->hAcmStream) {
679 return waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
681 if (acmStreamSize(wim->hAcmStream, lpWaveHdrDst->dwBufferLength, &size,
682 ACM_STREAMSIZEF_DESTINATION) != MMSYSERR_NOERROR)
683 return MMSYSERR_ERROR;
685 ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
686 if (ash == NULL)
687 return MMSYSERR_NOMEM;
689 ash->cbStruct = sizeof(*ash);
690 ash->fdwStatus = 0L;
691 ash->dwUser = (DWORD)lpWaveHdrDst;
692 ash->pbSrc = (LPSTR)ash + sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR);
693 ash->cbSrcLength = size;
694 /* ash->cbSrcLengthUsed */
695 ash->dwSrcUser = 0L; /* FIXME ? */
696 ash->pbDst = lpWaveHdrDst->lpData;
697 ash->cbDstLength = lpWaveHdrDst->dwBufferLength;
698 /* ash->cbDstLengthUsed */
699 ash->dwDstUser = lpWaveHdrDst->dwUser; /* FIXME ? */
700 dwRet = acmStreamPrepareHeader(wim->hAcmStream, ash, 0L);
701 if (dwRet != MMSYSERR_NOERROR)
702 goto errCleanUp;
704 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
705 lpWaveHdrSrc->lpData = ash->pbSrc;
706 lpWaveHdrSrc->dwBufferLength = size; /* conversion is not done yet */
707 lpWaveHdrSrc->dwFlags = 0;
708 lpWaveHdrSrc->dwLoops = 0;
709 dwRet = waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
710 if (dwRet != MMSYSERR_NOERROR)
711 goto errCleanUp;
713 lpWaveHdrDst->reserved = (DWORD)ash;
714 lpWaveHdrDst->dwFlags = WHDR_PREPARED;
715 TRACE("=> (0)\n");
716 return MMSYSERR_NOERROR;
717 errCleanUp:
718 TRACE("=> (%ld)\n", dwRet);
719 HeapFree(GetProcessHeap(), 0, ash);
720 return dwRet;
723 static DWORD widUnprepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
725 PACMSTREAMHEADER ash;
726 LPWAVEHDR lpWaveHdrSrc;
727 DWORD dwRet1, dwRet2;
729 if (!wim->hAcmStream) {
730 return waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
732 ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
733 dwRet1 = acmStreamUnprepareHeader(wim->hAcmStream, ash, 0L);
735 lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
736 dwRet2 = waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
738 HeapFree(GetProcessHeap(), 0, ash);
740 lpWaveHdrDst->dwFlags &= ~WHDR_PREPARED;
741 return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
744 static DWORD widGetPosition(WAVEMAPDATA* wim, LPMMTIME lpTime, DWORD dwParam2)
746 DWORD val = waveInGetPosition(wim->u.in.hInnerWave, lpTime, dwParam2);
747 if (lpTime->wType == TIME_BYTES)
748 val = MulDiv(val, wim->avgSpeedOuter, wim->avgSpeedInner);
749 /* other time types don't require conversion */
750 return val;
753 static DWORD widGetDevCaps(UINT wDevID, WAVEMAPDATA* wim, LPWAVEINCAPSA lpWaveCaps, DWORD dwParam2)
755 /* if opened low driver, forward message */
756 if (WAVEMAP_IsData(wim))
757 return waveInGetDevCapsA((UINT)wim->u.in.hInnerWave, lpWaveCaps, dwParam2);
758 /* otherwise, return caps of mapper itself */
759 if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
760 lpWaveCaps->wMid = 0x00FF;
761 lpWaveCaps->wPid = 0x0001;
762 lpWaveCaps->vDriverVersion = 0x0001;
763 strcpy(lpWaveCaps->szPname, "Wine wave in mapper");
764 lpWaveCaps->dwFormats =
765 WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
766 WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
767 WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
768 lpWaveCaps->wChannels = 2;
769 return MMSYSERR_NOERROR;
771 ERR("This shouldn't happen\n");
772 return MMSYSERR_ERROR;
775 static DWORD widStop(WAVEMAPDATA* wim)
777 return waveInStop(wim->u.in.hInnerWave);
780 static DWORD widStart(WAVEMAPDATA* wim)
782 return waveInStart(wim->u.in.hInnerWave);
785 static DWORD widReset(WAVEMAPDATA* wim)
787 return waveInReset(wim->u.in.hInnerWave);
790 static DWORD widMapperStatus(WAVEMAPDATA* wim, DWORD flags, LPVOID ptr)
792 UINT id;
793 DWORD ret = MMSYSERR_NOTSUPPORTED;
795 switch (flags) {
796 case WAVEIN_MAPPER_STATUS_DEVICE:
797 ret = waveInGetID(wim->u.in.hInnerWave, &id);
798 *(LPDWORD)ptr = id;
799 break;
800 case WAVEIN_MAPPER_STATUS_MAPPED:
801 FIXME("Unsupported yet flag=%ld\n", flags);
802 *(LPDWORD)ptr = 0; /* FIXME ?? */
803 break;
804 case WAVEIN_MAPPER_STATUS_FORMAT:
805 FIXME("Unsupported flag=%ld\n", flags);
806 /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
807 *(LPDWORD)ptr = 0; /* FIXME ?? */
808 break;
809 default:
810 FIXME("Unsupported flag=%ld\n", flags);
811 *(LPDWORD)ptr = 0;
812 break;
814 return ret;
817 /**************************************************************************
818 * widMessage (MSACM.@)
820 DWORD WINAPI WAVEMAP_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
821 DWORD dwParam1, DWORD dwParam2)
823 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
824 wDevID, wMsg, dwUser, dwParam1, dwParam2);
826 switch (wMsg) {
827 case DRVM_INIT:
828 case DRVM_EXIT:
829 case DRVM_ENABLE:
830 case DRVM_DISABLE:
831 /* FIXME: Pretend this is supported */
832 return 0;
834 case WIDM_OPEN: return widOpen ((LPDWORD)dwUser, (LPWAVEOPENDESC)dwParam1, dwParam2);
835 case WIDM_CLOSE: return widClose ((WAVEMAPDATA*)dwUser);
837 case WIDM_ADDBUFFER: return widAddBuffer ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
838 case WIDM_PREPARE: return widPrepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
839 case WIDM_UNPREPARE: return widUnprepare ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1, dwParam2);
840 case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEINCAPSA)dwParam1, dwParam2);
841 case WIDM_GETNUMDEVS: return 1;
842 case WIDM_GETPOS: return widGetPosition ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1, dwParam2);
843 case WIDM_RESET: return widReset ((WAVEMAPDATA*)dwUser);
844 case WIDM_START: return widStart ((WAVEMAPDATA*)dwUser);
845 case WIDM_STOP: return widStop ((WAVEMAPDATA*)dwUser);
846 case WIDM_MAPPER_STATUS: return widMapperStatus ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
847 default:
848 FIXME("unknown message %u!\n", wMsg);
850 return MMSYSERR_NOTSUPPORTED;
853 /*======================================================================*
854 * Driver part *
855 *======================================================================*/
857 static struct WINE_WAVEMAP* oss = NULL;
859 /**************************************************************************
860 * WAVEMAP_drvOpen [internal]
862 static DWORD WAVEMAP_drvOpen(LPSTR str)
864 if (oss)
865 return 0;
867 /* I know, this is ugly, but who cares... */
868 oss = (struct WINE_WAVEMAP*)1;
869 return 1;
872 /**************************************************************************
873 * WAVEMAP_drvClose [internal]
875 static DWORD WAVEMAP_drvClose(DWORD dwDevID)
877 if (oss) {
878 oss = NULL;
879 return 1;
881 return 0;
884 /**************************************************************************
885 * DriverProc (MSACM.@)
887 LONG CALLBACK WAVEMAP_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
888 DWORD dwParam1, DWORD dwParam2)
890 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
891 dwDevID, hDriv, wMsg, dwParam1, dwParam2);
893 switch(wMsg) {
894 case DRV_LOAD: return 1;
895 case DRV_FREE: return 1;
896 case DRV_OPEN: return WAVEMAP_drvOpen((LPSTR)dwParam1);
897 case DRV_CLOSE: return WAVEMAP_drvClose(dwDevID);
898 case DRV_ENABLE: return 1;
899 case DRV_DISABLE: return 1;
900 case DRV_QUERYCONFIGURE: return 1;
901 case DRV_CONFIGURE: MessageBoxA(0, "WAVEMAP MultiMedia Driver !", "Wave mapper Driver", MB_OK); return 1;
902 case DRV_INSTALL: return DRVCNF_RESTART;
903 case DRV_REMOVE: return DRVCNF_RESTART;
904 default:
905 return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);