Register the Product Version also.
[wine] / dlls / winmm / wavemap / wavemap.c
1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
2 /*
3  * Wine Wave mapper driver
4  *
5  * Copyright    1999,2001 Eric Pouech
6  *
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.
11  *
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.
16  *
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
20  */
21
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...
29  */
30
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"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(wavemap);
44
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;
69
70 static  BOOL    WAVEMAP_IsData(WAVEMAPDATA* wm)
71 {
72     return (!IsBadReadPtr(wm, sizeof(WAVEMAPDATA)) && wm->self == wm);
73 }
74
75 /*======================================================================*
76  *                  WAVE OUT part                                       *
77  *======================================================================*/
78
79 static void     CALLBACK wodCallback(HWAVEOUT hWave, UINT uMsg, DWORD dwInstance,
80                                      DWORD dwParam1, DWORD dwParam2)
81 {
82     WAVEMAPDATA*        wom = (WAVEMAPDATA*)dwInstance;
83
84     TRACE("(%p %u %ld %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
85
86     if (!WAVEMAP_IsData(wom)) {
87         ERR("Bad data\n");
88         return;
89     }
90
91     if (hWave != wom->u.out.hInnerWave && uMsg != WOM_OPEN)
92         ERR("Shouldn't happen (%p %p)\n", hWave, wom->u.out.hInnerWave);
93
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;
104
105             lpWaveHdrSrc->dwFlags &= ~WHDR_INQUEUE;
106             lpWaveHdrSrc->dwFlags |= WHDR_DONE;
107             dwParam1 = (DWORD)lpWaveHdrSrc;
108         }
109         break;
110     default:
111         ERR("Unknown msg %u\n", uMsg);
112     }
113
114     DriverCallback(wom->dwCallback, HIWORD(wom->dwFlags), (HDRVR)wom->u.out.hOuterWave,
115                    uMsg, wom->dwClientInstance, dwParam1, dwParam2);
116 }
117
118 /******************************************************************
119  *              wodOpenHelper
120  *
121  *
122  */
123 static  DWORD   wodOpenHelper(WAVEMAPDATA* wom, UINT idx,
124                               LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
125                               DWORD dwFlags)
126 {
127     DWORD       ret;
128
129     TRACE("(%p %04x %p %p %08lx)\n", wom, idx, lpDesc, lpwfx, dwFlags);
130
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);
138     }
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);
144             wom->hAcmStream = 0;
145         }
146     }
147     TRACE("ret = %08lx\n", ret);
148     return ret;
149 }
150
151 static  DWORD   wodOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
152 {
153     UINT                ndlo, ndhi;
154     UINT                i;
155     WAVEMAPDATA*        wom = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
156     DWORD               res;
157
158     TRACE("(%p %p %08lx)\n", lpdwUser, lpDesc, dwFlags);
159
160     if (!wom) {
161         WARN("no memory\n");
162         return MMSYSERR_NOMEM;
163     }
164
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;
171         }
172         ndlo = lpDesc->uMappedDeviceID;
173         ndhi = ndlo + 1;
174         dwFlags &= ~WAVE_MAPPED;
175     } else {
176         ndlo = 0;
177     }
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;
185
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
189          */
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) {
192             wom->hAcmStream = 0;
193             goto found;
194         }
195     }
196
197     if ((dwFlags & WAVE_FORMAT_DIRECT) == 0 && lpDesc->lpFormat->wFormatTag == WAVE_FORMAT_PCM) {
198         WAVEFORMATEX    wfx;
199
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 */
203
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; \
209                         }
210
211         /* Our resampling algorithm is quite primitive so first try
212          * to just change the bit depth and number of channels
213          */
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);
219             wfx.nChannels ^= 3;
220             TRY(wfx.nSamplesPerSec, 16);
221             TRY(wfx.nSamplesPerSec, 8);
222         }
223
224         for (i = ndlo; i < ndhi; i++) {
225             /* first try with same stereo/mono option as source */
226             wfx.nChannels = lpDesc->lpFormat->nChannels;
227             TRY(96000, 16);
228             TRY(48000, 16);
229             TRY(44100, 16);
230             TRY(22050, 16);
231             TRY(11025, 16);
232
233             /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
234             wfx.nChannels ^= 3;
235             TRY(96000, 16);
236             TRY(48000, 16);
237             TRY(44100, 16);
238             TRY(22050, 16);
239             TRY(11025, 16);
240
241             /* first try with same stereo/mono option as source */
242             wfx.nChannels = lpDesc->lpFormat->nChannels;
243             TRY(96000, 8);
244             TRY(48000, 8);
245             TRY(44100, 8);
246             TRY(22050, 8);
247             TRY(11025, 8);
248
249             /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
250             wfx.nChannels ^= 3;
251             TRY(96000, 8);
252             TRY(48000, 8);
253             TRY(44100, 8);
254             TRY(22050, 8);
255             TRY(11025, 8);
256         }
257 #undef TRY
258     }
259
260     HeapFree(GetProcessHeap(), 0, wom);
261     WARN("ret = WAVERR_BADFORMAT\n");
262     return WAVERR_BADFORMAT;
263
264 found:
265     if (dwFlags & WAVE_FORMAT_QUERY) {
266         *lpdwUser = 0L;
267         HeapFree(GetProcessHeap(), 0, wom);
268     } else {
269         *lpdwUser = (DWORD)wom;
270     }
271     return MMSYSERR_NOERROR;
272 error:
273     HeapFree(GetProcessHeap(), 0, wom);
274     if (res==ACMERR_NOTPOSSIBLE) {
275         WARN("ret = WAVERR_BADFORMAT\n");
276         return WAVERR_BADFORMAT;
277     }
278     WARN("ret = 0x%08lx\n", res);
279     return res;
280 }
281
282 static  DWORD   wodClose(WAVEMAPDATA* wom)
283 {
284     DWORD ret;
285
286     TRACE("(%p)\n", wom);
287
288     ret = waveOutClose(wom->u.out.hInnerWave);
289     if (ret == MMSYSERR_NOERROR) {
290         if (wom->hAcmStream) {
291             ret = acmStreamClose(wom->hAcmStream, 0);
292         }
293         if (ret == MMSYSERR_NOERROR) {
294             HeapFree(GetProcessHeap(), 0, wom);
295         }
296     }
297     return ret;
298 }
299
300 static  DWORD   wodWrite(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
301 {
302     PACMSTREAMHEADER    ash;
303     LPWAVEHDR           lpWaveHdrDst;
304
305     TRACE("(%p %p %08lx)\n", wom, lpWaveHdrSrc, dwParam2);
306
307     if (!wom->hAcmStream) {
308         return waveOutWrite(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
309     }
310
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;
318     }
319
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");
325
326     if (ash->cbDstLengthUsed == 0) {
327         /* something went wrong in decoding */
328         FIXME("Got 0 length\n");
329         return MMSYSERR_ERROR;
330     }
331     lpWaveHdrDst->dwBufferLength = ash->cbDstLengthUsed;
332     return waveOutWrite(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
333 }
334
335 static  DWORD   wodPrepare(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
336 {
337     PACMSTREAMHEADER    ash;
338     DWORD               size;
339     DWORD               dwRet;
340     LPWAVEHDR           lpWaveHdrDst;
341
342     TRACE("(%p %p %08lx)\n", wom, lpWaveHdrSrc, dwParam2);
343
344     if (!wom->hAcmStream)
345         return waveOutPrepareHeader(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
346
347     if (acmStreamSize(wom->hAcmStream, lpWaveHdrSrc->dwBufferLength, &size, ACM_STREAMSIZEF_SOURCE) != MMSYSERR_NOERROR) {
348         WARN("acmStreamSize failed\n");
349         return MMSYSERR_ERROR;
350     }
351
352     ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
353     if (ash == NULL) {
354         WARN("no memory\n");
355         return MMSYSERR_NOMEM;
356     }
357
358     ash->cbStruct = sizeof(*ash);
359     ash->fdwStatus = 0L;
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");
372         goto errCleanUp;
373     }
374
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");
383         goto errCleanUp;
384     }
385
386     lpWaveHdrSrc->reserved = (DWORD)ash;
387     lpWaveHdrSrc->dwFlags = WHDR_PREPARED;
388     TRACE("=> (0)\n");
389     return MMSYSERR_NOERROR;
390 errCleanUp:
391     TRACE("=> (%ld)\n", dwRet);
392     HeapFree(GetProcessHeap(), 0, ash);
393     return dwRet;
394 }
395
396 static  DWORD   wodUnprepare(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
397 {
398     PACMSTREAMHEADER    ash;
399     LPWAVEHDR           lpWaveHdrDst;
400     DWORD               dwRet1, dwRet2;
401
402     TRACE("(%p %p %08lx)\n", wom, lpWaveHdrSrc, dwParam2);
403
404     if (!wom->hAcmStream) {
405         return waveOutUnprepareHeader(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
406     }
407     ash = (PACMSTREAMHEADER)lpWaveHdrSrc->reserved;
408     dwRet1 = acmStreamUnprepareHeader(wom->hAcmStream, ash, 0L);
409
410     lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
411     dwRet2 = waveOutUnprepareHeader(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
412
413     HeapFree(GetProcessHeap(), 0, ash);
414
415     lpWaveHdrSrc->dwFlags &= ~WHDR_PREPARED;
416     return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
417 }
418
419 static  DWORD   wodGetPosition(WAVEMAPDATA* wom, LPMMTIME lpTime, DWORD dwParam2)
420 {
421     DWORD       val;
422     MMTIME      timepos;
423     TRACE("(%p %p %08lx)\n", wom, lpTime, dwParam2);
424
425     memcpy(&timepos, lpTime, sizeof(timepos));
426
427     /* For TIME_MS, we're going to recalculate using TIME_BYTES */
428     if (lpTime->wType == TIME_MS)
429         timepos.wType = TIME_BYTES;
430
431     /* This can change timepos.wType if the requested type is not supported */
432     val = waveOutGetPosition(wom->u.out.hInnerWave, &timepos, dwParam2);
433
434     if (timepos.wType == TIME_BYTES)
435     {
436         DWORD dwInnerSamplesPerOuter = wom->nSamplesPerSecInner / wom->nSamplesPerSecOuter;
437         if (dwInnerSamplesPerOuter > 0)
438         {
439             DWORD dwInnerBytesPerSample = wom->avgSpeedInner / wom->nSamplesPerSecInner;
440             DWORD dwInnerBytesPerOuterSample = dwInnerBytesPerSample * dwInnerSamplesPerOuter;
441             DWORD remainder = 0;
442
443             /* If we are up sampling (going from lower sample rate to higher),
444             **   we need to make a special accomodation for times when we've
445             **   written a partial output sample.  This happens frequently
446             **   to us because we use msacm to do our up sampling, and it
447             **   will up sample on an unaligned basis.
448             ** For example, if you convert a 2 byte wide 8,000 'outer'
449             **   buffer to a 2 byte wide 48,000 inner device, you would
450             **   expect 2 bytes of input to produce 12 bytes of output.
451             **   Instead, msacm will produce 8 bytes of output.
452             **   But reporting our position as 1 byte of output is
453             **   nonsensical; the output buffer position needs to be
454             **   aligned on outer sample size, and aggressively rounded up.
455             */
456             remainder = timepos.u.cb % dwInnerBytesPerOuterSample;
457             if (remainder > 0)
458             {
459                 timepos.u.cb -= remainder;
460                 timepos.u.cb += dwInnerBytesPerOuterSample;
461             }
462         }
463
464         lpTime->u.cb = MulDiv(timepos.u.cb, wom->avgSpeedOuter, wom->avgSpeedInner);
465
466         /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */
467         if (lpTime->wType == TIME_MS)
468             lpTime->u.ms = MulDiv(lpTime->u.cb, 1000, wom->avgSpeedOuter);
469         else
470             lpTime->wType = TIME_BYTES;
471     }
472     else if (lpTime->wType == TIME_SAMPLES && timepos.wType == TIME_SAMPLES)
473         lpTime->u.sample = MulDiv(timepos.u.sample, wom->nSamplesPerSecOuter, wom->nSamplesPerSecInner);
474     else
475         /* other time types don't require conversion */
476         lpTime->u = timepos.u;
477
478     return val;
479 }
480
481 static  DWORD   wodGetDevCaps(UINT wDevID, WAVEMAPDATA* wom, LPWAVEOUTCAPSW lpWaveCaps, DWORD dwParam2)
482 {
483     static const WCHAR name[] = {'W','i','n','e',' ','w','a','v','e',' ','o','u','t',' ','m','a','p','p','e','r',0};
484
485     TRACE("(%04x %p %p %08lx)\n",wDevID, wom, lpWaveCaps, dwParam2);
486
487     /* if opened low driver, forward message */
488     if (WAVEMAP_IsData(wom))
489         return waveOutGetDevCapsW((UINT)wom->u.out.hInnerWave, lpWaveCaps, dwParam2);
490     /* else if no drivers, nothing to map so return bad device */
491     if (waveOutGetNumDevs() == 0) {
492         WARN("bad device id\n");
493         return MMSYSERR_BADDEVICEID;
494     }
495     /* otherwise, return caps of mapper itself */
496     if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
497         WAVEOUTCAPSW woc;
498         woc.wMid = 0x00FF;
499         woc.wPid = 0x0001;
500         woc.vDriverVersion = 0x0100;
501         lstrcpyW(woc.szPname, name);
502         woc.dwFormats =
503             WAVE_FORMAT_96M08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16 |
504             WAVE_FORMAT_48M08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 |
505             WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
506             WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
507             WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
508         woc.wChannels = 2;
509         woc.dwSupport = WAVECAPS_VOLUME | WAVECAPS_LRVOLUME;
510         memcpy(lpWaveCaps, &woc, min(dwParam2, sizeof(woc)));
511
512         return MMSYSERR_NOERROR;
513     }
514     ERR("This shouldn't happen\n");
515     return MMSYSERR_ERROR;
516 }
517
518 static  DWORD   wodGetVolume(UINT wDevID, WAVEMAPDATA* wom, LPDWORD lpVol)
519 {
520     TRACE("(%04x %p %p)\n",wDevID, wom, lpVol);
521
522     if (WAVEMAP_IsData(wom))
523         return waveOutGetVolume(wom->u.out.hInnerWave, lpVol);
524     return MMSYSERR_NOERROR;
525 }
526
527 static  DWORD   wodSetVolume(UINT wDevID, WAVEMAPDATA* wom, DWORD vol)
528 {
529     TRACE("(%04x %p %08lx)\n",wDevID, wom, vol);
530
531     if (WAVEMAP_IsData(wom))
532         return waveOutSetVolume(wom->u.out.hInnerWave, vol);
533     return MMSYSERR_NOERROR;
534 }
535
536 static  DWORD   wodPause(WAVEMAPDATA* wom)
537 {
538     TRACE("(%p)\n",wom);
539
540     return waveOutPause(wom->u.out.hInnerWave);
541 }
542
543 static  DWORD   wodRestart(WAVEMAPDATA* wom)
544 {
545     TRACE("(%p)\n",wom);
546
547     return waveOutRestart(wom->u.out.hInnerWave);
548 }
549
550 static  DWORD   wodReset(WAVEMAPDATA* wom)
551 {
552     TRACE("(%p)\n",wom);
553
554     return waveOutReset(wom->u.out.hInnerWave);
555 }
556
557 static  DWORD   wodBreakLoop(WAVEMAPDATA* wom)
558 {
559     TRACE("(%p)\n",wom);
560
561     return waveOutBreakLoop(wom->u.out.hInnerWave);
562 }
563
564 static  DWORD   wodMapperStatus(WAVEMAPDATA* wom, DWORD flags, LPVOID ptr)
565 {
566     UINT        id;
567     DWORD       ret = MMSYSERR_NOTSUPPORTED;
568
569     TRACE("(%p %08lx %p)\n",wom, flags, ptr);
570
571     switch (flags) {
572     case WAVEOUT_MAPPER_STATUS_DEVICE:
573         ret = waveOutGetID(wom->u.out.hInnerWave, &id);
574         *(LPDWORD)ptr = id;
575         break;
576     case WAVEOUT_MAPPER_STATUS_MAPPED:
577         FIXME("Unsupported flag=%ld\n", flags);
578         *(LPDWORD)ptr = 0; /* FIXME ?? */
579         break;
580     case WAVEOUT_MAPPER_STATUS_FORMAT:
581         FIXME("Unsupported flag=%ld\n", flags);
582         /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
583         *(LPDWORD)ptr = 0;
584         break;
585     default:
586         FIXME("Unsupported flag=%ld\n", flags);
587         *(LPDWORD)ptr = 0;
588         break;
589     }
590     return ret;
591 }
592
593 static  DWORD   wodMapperReconfigure(WAVEMAPDATA* wom, DWORD dwParam1, DWORD dwParam2)
594 {
595     FIXME("(%p %08lx %08lx) stub!\n", wom, dwParam1, dwParam2);
596
597     return MMSYSERR_NOERROR;
598 }
599
600 /**************************************************************************
601  *                              wodMessage (MSACM.@)
602  */
603 DWORD WINAPI WAVEMAP_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
604                                 DWORD dwParam1, DWORD dwParam2)
605 {
606     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
607           wDevID, wMsg, dwUser, dwParam1, dwParam2);
608
609     switch (wMsg) {
610     case DRVM_INIT:
611     case DRVM_EXIT:
612     case DRVM_ENABLE:
613     case DRVM_DISABLE:
614         /* FIXME: Pretend this is supported */
615         return 0;
616     case WODM_OPEN:             return wodOpen          ((LPDWORD)dwUser,      (LPWAVEOPENDESC)dwParam1,dwParam2);
617     case WODM_CLOSE:            return wodClose         ((WAVEMAPDATA*)dwUser);
618     case WODM_WRITE:            return wodWrite         ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
619     case WODM_PAUSE:            return wodPause         ((WAVEMAPDATA*)dwUser);
620     case WODM_GETPOS:           return wodGetPosition   ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1,      dwParam2);
621     case WODM_BREAKLOOP:        return wodBreakLoop     ((WAVEMAPDATA*)dwUser);
622     case WODM_PREPARE:          return wodPrepare       ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
623     case WODM_UNPREPARE:        return wodUnprepare     ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
624     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEOUTCAPSW)dwParam1,dwParam2);
625     case WODM_GETNUMDEVS:       return 1;
626     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
627     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
628     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
629     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
630     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (WAVEMAPDATA*)dwUser, (LPDWORD)dwParam1);
631     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, (WAVEMAPDATA*)dwUser, dwParam1);
632     case WODM_RESTART:          return wodRestart       ((WAVEMAPDATA*)dwUser);
633     case WODM_RESET:            return wodReset         ((WAVEMAPDATA*)dwUser);
634     case WODM_MAPPER_STATUS:    return wodMapperStatus  ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
635     case DRVM_MAPPER_RECONFIGURE: return wodMapperReconfigure((WAVEMAPDATA*)dwUser, dwParam1, dwParam2);
636     /* known but not supported */
637     case DRV_QUERYDEVICEINTERFACESIZE:
638     case DRV_QUERYDEVICEINTERFACE:
639         return MMSYSERR_NOTSUPPORTED;
640     default:
641         FIXME("unknown message %d!\n", wMsg);
642     }
643     return MMSYSERR_NOTSUPPORTED;
644 }
645
646 /*======================================================================*
647  *                  WAVE IN part                                        *
648  *======================================================================*/
649
650 static void     CALLBACK widCallback(HWAVEIN hWave, UINT uMsg, DWORD dwInstance,
651                                      DWORD dwParam1, DWORD dwParam2)
652 {
653     WAVEMAPDATA*        wim = (WAVEMAPDATA*)dwInstance;
654
655     TRACE("(%p %u %ld %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
656
657     if (!WAVEMAP_IsData(wim)) {
658         ERR("Bad data\n");
659         return;
660     }
661
662     if (hWave != wim->u.in.hInnerWave && uMsg != WIM_OPEN)
663         ERR("Shouldn't happen (%p %p)\n", hWave, wim->u.in.hInnerWave);
664
665     switch (uMsg) {
666     case WIM_OPEN:
667     case WIM_CLOSE:
668         /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
669         break;
670     case WIM_DATA:
671         if (wim->hAcmStream) {
672             LPWAVEHDR           lpWaveHdrSrc = (LPWAVEHDR)dwParam1;
673             PACMSTREAMHEADER    ash = (PACMSTREAMHEADER)((LPSTR)lpWaveHdrSrc - sizeof(ACMSTREAMHEADER));
674             LPWAVEHDR           lpWaveHdrDst = (LPWAVEHDR)ash->dwUser;
675
676             /* convert data just gotten from waveIn into requested format */
677             if (acmStreamConvert(wim->hAcmStream, ash, 0L) != MMSYSERR_NOERROR) {
678                 ERR("ACM conversion failed\n");
679                 return;
680             } else {
681                 TRACE("Converted %ld bytes into %ld\n", ash->cbSrcLengthUsed, ash->cbDstLengthUsed);
682             }
683             /* and setup the wavehdr to return accordingly */
684             lpWaveHdrDst->dwFlags &= ~WHDR_INQUEUE;
685             lpWaveHdrDst->dwFlags |= WHDR_DONE;
686             lpWaveHdrDst->dwBytesRecorded = ash->cbDstLengthUsed;
687             dwParam1 = (DWORD)lpWaveHdrDst;
688         }
689         break;
690     default:
691         ERR("Unknown msg %u\n", uMsg);
692     }
693
694     DriverCallback(wim->dwCallback, HIWORD(wim->dwFlags), (HDRVR)wim->u.in.hOuterWave,
695                    uMsg, wim->dwClientInstance, dwParam1, dwParam2);
696 }
697
698 static  DWORD   widOpenHelper(WAVEMAPDATA* wim, UINT idx,
699                               LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
700                               DWORD dwFlags)
701 {
702     DWORD       ret;
703
704     TRACE("(%p %04x %p %p %08lx)\n", wim, idx, lpDesc, lpwfx, dwFlags);
705
706     /* source is always PCM, so the formulas below apply */
707     lpwfx->nBlockAlign = (lpwfx->nChannels * lpwfx->wBitsPerSample) / 8;
708     lpwfx->nAvgBytesPerSec = lpwfx->nSamplesPerSec * lpwfx->nBlockAlign;
709     if (dwFlags & WAVE_FORMAT_QUERY) {
710         ret = acmStreamOpen(NULL, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY);
711     } else {
712         ret = acmStreamOpen(&wim->hAcmStream, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, 0L);
713     }
714     if (ret == MMSYSERR_NOERROR) {
715         ret = waveInOpen(&wim->u.in.hInnerWave, idx, lpwfx, (DWORD)widCallback,
716                          (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION);
717         if (ret != MMSYSERR_NOERROR && !(dwFlags & WAVE_FORMAT_QUERY)) {
718             acmStreamClose(wim->hAcmStream, 0);
719             wim->hAcmStream = 0;
720         }
721     }
722     TRACE("ret = %08lx\n", ret);
723     return ret;
724 }
725
726 static  DWORD   widOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
727 {
728     UINT                ndlo, ndhi;
729     UINT                i;
730     WAVEMAPDATA*        wim = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
731     DWORD               res;
732
733     TRACE("(%p %p %08lx)\n", lpdwUser, lpDesc, dwFlags);
734
735     if (!wim) {
736         WARN("no memory\n");
737         return MMSYSERR_NOMEM;
738     }
739
740     wim->self = wim;
741     wim->dwCallback = lpDesc->dwCallback;
742     wim->dwFlags = dwFlags;
743     wim->dwClientInstance = lpDesc->dwInstance;
744     wim->u.in.hOuterWave = (HWAVEIN)lpDesc->hWave;
745
746     ndhi = waveInGetNumDevs();
747     if (dwFlags & WAVE_MAPPED) {
748         if (lpDesc->uMappedDeviceID >= ndhi) return MMSYSERR_INVALPARAM;
749         ndlo = lpDesc->uMappedDeviceID;
750         ndhi = ndlo + 1;
751         dwFlags &= ~WAVE_MAPPED;
752     } else {
753         ndlo = 0;
754     }
755
756     wim->avgSpeedOuter = wim->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec;
757     wim->nSamplesPerSecOuter = wim->nSamplesPerSecInner = lpDesc->lpFormat->nSamplesPerSec;
758
759     for (i = ndlo; i < ndhi; i++) {
760         if (waveInOpen(&wim->u.in.hInnerWave, i, lpDesc->lpFormat, (DWORD)widCallback,
761                        (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) {
762             wim->hAcmStream = 0;
763             goto found;
764         }
765     }
766
767     if ((dwFlags & WAVE_FORMAT_DIRECT) == 0)
768     {
769         WAVEFORMATEX    wfx;
770
771         wfx.wFormatTag = WAVE_FORMAT_PCM;
772         wfx.cbSize = 0; /* normally, this field is not used for PCM format, just in case */
773         /* try some ACM stuff */
774
775 #define TRY(sps,bps)    wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
776                         switch (res=widOpenHelper(wim, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT)) { \
777                         case MMSYSERR_NOERROR: wim->avgSpeedInner = wfx.nAvgBytesPerSec; wim->nSamplesPerSecInner = wfx.nSamplesPerSec; goto found; \
778                         case WAVERR_BADFORMAT: break; \
779                         default: goto error; \
780                         }
781
782         for (i = ndlo; i < ndhi; i++) {
783             wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
784             /* first try with same stereo/mono option as source */
785             wfx.nChannels = lpDesc->lpFormat->nChannels;
786                         TRY(wfx.nSamplesPerSec, 16);
787                         TRY(wfx.nSamplesPerSec, 8);
788                         wfx.nChannels ^= 3;
789                         TRY(wfx.nSamplesPerSec, 16);
790                         TRY(wfx.nSamplesPerSec, 8);
791         }
792
793         for (i = ndlo; i < ndhi; i++) {
794             wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
795             /* first try with same stereo/mono option as source */
796             wfx.nChannels = lpDesc->lpFormat->nChannels;
797             TRY(96000, 16);
798             TRY(48000, 16);
799             TRY(44100, 16);
800             TRY(22050, 16);
801             TRY(11025, 16);
802
803             /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
804             wfx.nChannels ^= 3;
805             TRY(96000, 16);
806             TRY(48000, 16);
807             TRY(44100, 16);
808             TRY(22050, 16);
809             TRY(11025, 16);
810
811             /* first try with same stereo/mono option as source */
812             wfx.nChannels = lpDesc->lpFormat->nChannels;
813             TRY(96000, 8);
814             TRY(48000, 8);
815             TRY(44100, 8);
816             TRY(22050, 8);
817             TRY(11025, 8);
818
819             /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
820             wfx.nChannels ^= 3;
821             TRY(96000, 8);
822             TRY(48000, 8);
823             TRY(44100, 8);
824             TRY(22050, 8);
825             TRY(11025, 8);
826         }
827 #undef TRY
828     }
829
830     HeapFree(GetProcessHeap(), 0, wim);
831     WARN("ret = WAVERR_BADFORMAT\n");
832     return WAVERR_BADFORMAT;
833 found:
834     if (dwFlags & WAVE_FORMAT_QUERY) {
835         *lpdwUser = 0L;
836         HeapFree(GetProcessHeap(), 0, wim);
837     } else {
838         *lpdwUser = (DWORD)wim;
839     }
840     TRACE("Ok (stream=%08lx)\n", (DWORD)wim->hAcmStream);
841     return MMSYSERR_NOERROR;
842 error:
843     HeapFree(GetProcessHeap(), 0, wim);
844     if (res==ACMERR_NOTPOSSIBLE) {
845         WARN("ret = WAVERR_BADFORMAT\n");
846         return WAVERR_BADFORMAT;
847     }
848     WARN("ret = 0x%08lx\n", res);
849     return res;
850 }
851
852 static  DWORD   widClose(WAVEMAPDATA* wim)
853 {
854     DWORD ret;
855
856     TRACE("(%p)\n", wim);
857
858     ret = waveInClose(wim->u.in.hInnerWave);
859     if (ret == MMSYSERR_NOERROR) {
860         if (wim->hAcmStream) {
861             ret = acmStreamClose(wim->hAcmStream, 0);
862         }
863         if (ret == MMSYSERR_NOERROR) {
864             HeapFree(GetProcessHeap(), 0, wim);
865         }
866     }
867     return ret;
868 }
869
870 static  DWORD   widAddBuffer(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
871 {
872     PACMSTREAMHEADER    ash;
873     LPWAVEHDR           lpWaveHdrSrc;
874
875     TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
876
877     if (!wim->hAcmStream) {
878         return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
879     }
880
881     lpWaveHdrDst->dwFlags |= WHDR_INQUEUE;
882     ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
883
884     lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
885     return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
886 }
887
888 static  DWORD   widPrepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
889 {
890     PACMSTREAMHEADER    ash;
891     DWORD               size;
892     DWORD               dwRet;
893     LPWAVEHDR           lpWaveHdrSrc;
894
895     TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
896
897     if (!wim->hAcmStream) {
898         return waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
899     }
900     if (acmStreamSize(wim->hAcmStream, lpWaveHdrDst->dwBufferLength, &size,
901                       ACM_STREAMSIZEF_DESTINATION) != MMSYSERR_NOERROR) {
902         WARN("acmStreamSize failed\n");
903         return MMSYSERR_ERROR;
904     }
905
906     ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
907     if (ash == NULL) {
908         WARN("no memory\n");
909         return MMSYSERR_NOMEM;
910     }
911
912     ash->cbStruct = sizeof(*ash);
913     ash->fdwStatus = 0L;
914     ash->dwUser = (DWORD)lpWaveHdrDst;
915     ash->pbSrc = (LPSTR)ash + sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR);
916     ash->cbSrcLength = size;
917     /* ash->cbSrcLengthUsed */
918     ash->dwSrcUser = 0L; /* FIXME ? */
919     ash->pbDst = lpWaveHdrDst->lpData;
920     ash->cbDstLength = lpWaveHdrDst->dwBufferLength;
921     /* ash->cbDstLengthUsed */
922     ash->dwDstUser = lpWaveHdrDst->dwUser; /* FIXME ? */
923     dwRet = acmStreamPrepareHeader(wim->hAcmStream, ash, 0L);
924     if (dwRet != MMSYSERR_NOERROR) {
925         WARN("acmStreamPrepareHeader failed\n");
926         goto errCleanUp;
927     }
928
929     lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
930     lpWaveHdrSrc->lpData = ash->pbSrc;
931     lpWaveHdrSrc->dwBufferLength = size; /* conversion is not done yet */
932     lpWaveHdrSrc->dwFlags = 0;
933     lpWaveHdrSrc->dwLoops = 0;
934     dwRet = waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
935     if (dwRet != MMSYSERR_NOERROR) {
936         WARN("waveInPrepareHeader failed\n");
937         goto errCleanUp;
938     }
939
940     lpWaveHdrDst->reserved = (DWORD)ash;
941     lpWaveHdrDst->dwFlags = WHDR_PREPARED;
942     TRACE("=> (0)\n");
943     return MMSYSERR_NOERROR;
944 errCleanUp:
945     TRACE("=> (%ld)\n", dwRet);
946     HeapFree(GetProcessHeap(), 0, ash);
947     return dwRet;
948 }
949
950 static  DWORD   widUnprepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
951 {
952     PACMSTREAMHEADER    ash;
953     LPWAVEHDR           lpWaveHdrSrc;
954     DWORD               dwRet1, dwRet2;
955
956     TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
957
958     if (!wim->hAcmStream) {
959         return waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
960     }
961     ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
962     dwRet1 = acmStreamUnprepareHeader(wim->hAcmStream, ash, 0L);
963
964     lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
965     dwRet2 = waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
966
967     HeapFree(GetProcessHeap(), 0, ash);
968
969     lpWaveHdrDst->dwFlags &= ~WHDR_PREPARED;
970     return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
971 }
972
973 static  DWORD   widGetPosition(WAVEMAPDATA* wim, LPMMTIME lpTime, DWORD dwParam2)
974 {
975     DWORD       val;
976     MMTIME      timepos;
977     TRACE("(%p %p %08lx)\n", wim, lpTime, dwParam2);
978
979     memcpy(&timepos, lpTime, sizeof(timepos));
980
981     /* For TIME_MS, we're going to recalculate using TIME_BYTES */
982     if (lpTime->wType == TIME_MS)
983         timepos.wType = TIME_BYTES;
984
985     /* This can change timepos.wType if the requested type is not supported */
986     val = waveInGetPosition(wim->u.in.hInnerWave, &timepos, dwParam2);
987
988     if (timepos.wType == TIME_BYTES)
989     {
990         DWORD dwInnerSamplesPerOuter = wim->nSamplesPerSecInner / wim->nSamplesPerSecOuter;
991         if (dwInnerSamplesPerOuter > 0)
992         {
993             DWORD dwInnerBytesPerSample = wim->avgSpeedInner / wim->nSamplesPerSecInner;
994             DWORD dwInnerBytesPerOuterSample = dwInnerBytesPerSample * dwInnerSamplesPerOuter;
995             DWORD remainder = 0;
996
997             /* If we are up sampling (going from lower sample rate to higher),
998             **   we need to make a special accomodation for times when we've
999             **   written a partial output sample.  This happens frequently
1000             **   to us because we use msacm to do our up sampling, and it
1001             **   will up sample on an unaligned basis.
1002             ** For example, if you convert a 2 byte wide 8,000 'outer'
1003             **   buffer to a 2 byte wide 48,000 inner device, you would
1004             **   expect 2 bytes of input to produce 12 bytes of output.
1005             **   Instead, msacm will produce 8 bytes of output.
1006             **   But reporting our position as 1 byte of output is
1007             **   nonsensical; the output buffer position needs to be
1008             **   aligned on outer sample size, and aggressively rounded up.
1009             */
1010             remainder = timepos.u.cb % dwInnerBytesPerOuterSample;
1011             if (remainder > 0)
1012             {
1013                 timepos.u.cb -= remainder;
1014                 timepos.u.cb += dwInnerBytesPerOuterSample;
1015             }
1016         }
1017
1018         lpTime->u.cb = MulDiv(timepos.u.cb, wim->avgSpeedOuter, wim->avgSpeedInner);
1019
1020         /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */
1021         if (lpTime->wType == TIME_MS)
1022             lpTime->u.ms = MulDiv(lpTime->u.cb, 1000, wim->avgSpeedOuter);
1023         else
1024             lpTime->wType = TIME_BYTES;
1025     }
1026     else if (lpTime->wType == TIME_SAMPLES && timepos.wType == TIME_SAMPLES)
1027         lpTime->u.sample = MulDiv(timepos.u.sample, wim->nSamplesPerSecOuter, wim->nSamplesPerSecInner);
1028     else
1029         /* other time types don't require conversion */
1030         lpTime->u = timepos.u;
1031
1032     return val;
1033 }
1034
1035 static  DWORD   widGetDevCaps(UINT wDevID, WAVEMAPDATA* wim, LPWAVEINCAPSW lpWaveCaps, DWORD dwParam2)
1036 {
1037     TRACE("(%04x, %p %p %08lx)\n", wDevID, wim, lpWaveCaps, dwParam2);
1038
1039     /* if opened low driver, forward message */
1040     if (WAVEMAP_IsData(wim))
1041         return waveInGetDevCapsW((UINT)wim->u.in.hInnerWave, lpWaveCaps, dwParam2);
1042     /* else if no drivers, nothing to map so return bad device */
1043     if (waveInGetNumDevs() == 0) {
1044         WARN("bad device id\n");
1045         return MMSYSERR_BADDEVICEID;
1046     }
1047     /* otherwise, return caps of mapper itself */
1048     if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
1049         WAVEINCAPSW wic;
1050         static const WCHAR init[] = {'W','i','n','e',' ','w','a','v','e',' ','i','n',' ','m','a','p','p','e','r',0};
1051         wic.wMid = 0x00FF;
1052         wic.wPid = 0x0001;
1053         wic.vDriverVersion = 0x0001;
1054         strcpyW(wic.szPname, init);
1055         wic.dwFormats =
1056             WAVE_FORMAT_96M08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16 |
1057             WAVE_FORMAT_48M08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 |
1058             WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
1059             WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
1060             WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
1061         wic.wChannels = 2;
1062         memcpy(lpWaveCaps, &wic, min(dwParam2, sizeof(wic)));
1063
1064         return MMSYSERR_NOERROR;
1065     }
1066     ERR("This shouldn't happen\n");
1067     return MMSYSERR_ERROR;
1068 }
1069
1070 static  DWORD   widStop(WAVEMAPDATA* wim)
1071 {
1072     TRACE("(%p)\n", wim);
1073
1074     return waveInStop(wim->u.in.hInnerWave);
1075 }
1076
1077 static  DWORD   widStart(WAVEMAPDATA* wim)
1078 {
1079     TRACE("(%p)\n", wim);
1080
1081     return waveInStart(wim->u.in.hInnerWave);
1082 }
1083
1084 static  DWORD   widReset(WAVEMAPDATA* wim)
1085 {
1086     TRACE("(%p)\n", wim);
1087
1088     return waveInReset(wim->u.in.hInnerWave);
1089 }
1090
1091 static  DWORD   widMapperStatus(WAVEMAPDATA* wim, DWORD flags, LPVOID ptr)
1092 {
1093     UINT        id;
1094     DWORD       ret = MMSYSERR_NOTSUPPORTED;
1095
1096     TRACE("(%p %08lx %p)\n", wim, flags, ptr);
1097
1098     switch (flags) {
1099     case WAVEIN_MAPPER_STATUS_DEVICE:
1100         ret = waveInGetID(wim->u.in.hInnerWave, &id);
1101         *(LPDWORD)ptr = id;
1102         break;
1103     case WAVEIN_MAPPER_STATUS_MAPPED:
1104         FIXME("Unsupported yet flag=%ld\n", flags);
1105         *(LPDWORD)ptr = 0; /* FIXME ?? */
1106         break;
1107     case WAVEIN_MAPPER_STATUS_FORMAT:
1108         FIXME("Unsupported flag=%ld\n", flags);
1109         /* ptr points to a WAVEFORMATEX struct  - before or after streaming ? */
1110         *(LPDWORD)ptr = 0; /* FIXME ?? */
1111         break;
1112     default:
1113         FIXME("Unsupported flag=%ld\n", flags);
1114         *(LPDWORD)ptr = 0;
1115         break;
1116     }
1117     return ret;
1118 }
1119
1120 static  DWORD   widMapperReconfigure(WAVEMAPDATA* wim, DWORD dwParam1, DWORD dwParam2)
1121 {
1122     FIXME("(%p %08lx %08lx) stub!\n", wim, dwParam1, dwParam2);
1123
1124     return MMSYSERR_NOERROR;
1125 }
1126
1127 /**************************************************************************
1128  *                              widMessage (MSACM.@)
1129  */
1130 DWORD WINAPI WAVEMAP_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1131                                 DWORD dwParam1, DWORD dwParam2)
1132 {
1133     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1134           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1135
1136     switch (wMsg) {
1137     case DRVM_INIT:
1138     case DRVM_EXIT:
1139     case DRVM_ENABLE:
1140     case DRVM_DISABLE:
1141         /* FIXME: Pretend this is supported */
1142         return 0;
1143
1144     case WIDM_OPEN:             return widOpen          ((LPDWORD)dwUser,     (LPWAVEOPENDESC)dwParam1, dwParam2);
1145     case WIDM_CLOSE:            return widClose         ((WAVEMAPDATA*)dwUser);
1146
1147     case WIDM_ADDBUFFER:        return widAddBuffer     ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
1148     case WIDM_PREPARE:          return widPrepare       ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
1149     case WIDM_UNPREPARE:        return widUnprepare     ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
1150     case WIDM_GETDEVCAPS:       return widGetDevCaps    (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEINCAPSW)dwParam1, dwParam2);
1151     case WIDM_GETNUMDEVS:       return 1;
1152     case WIDM_GETPOS:           return widGetPosition   ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1,      dwParam2);
1153     case WIDM_RESET:            return widReset         ((WAVEMAPDATA*)dwUser);
1154     case WIDM_START:            return widStart         ((WAVEMAPDATA*)dwUser);
1155     case WIDM_STOP:             return widStop          ((WAVEMAPDATA*)dwUser);
1156     case WIDM_MAPPER_STATUS:    return widMapperStatus  ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
1157     case DRVM_MAPPER_RECONFIGURE: return widMapperReconfigure((WAVEMAPDATA*)dwUser, dwParam1, dwParam2);
1158     /* known but not supported */
1159     case DRV_QUERYDEVICEINTERFACESIZE:
1160     case DRV_QUERYDEVICEINTERFACE:
1161         return MMSYSERR_NOTSUPPORTED;
1162     default:
1163         FIXME("unknown message %u!\n", wMsg);
1164     }
1165     return MMSYSERR_NOTSUPPORTED;
1166 }
1167
1168 /*======================================================================*
1169  *                  Driver part                                         *
1170  *======================================================================*/
1171
1172 static  struct WINE_WAVEMAP* oss = NULL;
1173
1174 /**************************************************************************
1175  *                              WAVEMAP_drvOpen                 [internal]
1176  */
1177 static  DWORD   WAVEMAP_drvOpen(LPSTR str)
1178 {
1179     TRACE("(%p)\n", str);
1180
1181     if (oss)
1182         return 0;
1183
1184     /* I know, this is ugly, but who cares... */
1185     oss = (struct WINE_WAVEMAP*)1;
1186     return 1;
1187 }
1188
1189 /**************************************************************************
1190  *                              WAVEMAP_drvClose                [internal]
1191  */
1192 static  DWORD   WAVEMAP_drvClose(DWORD dwDevID)
1193 {
1194     TRACE("(%08lx)\n", dwDevID);
1195
1196     if (oss) {
1197         oss = NULL;
1198         return 1;
1199     }
1200     return 0;
1201 }
1202
1203 /**************************************************************************
1204  *                              DriverProc (MSACM.@)
1205  */
1206 LONG CALLBACK   WAVEMAP_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1207                                    DWORD dwParam1, DWORD dwParam2)
1208 {
1209     TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1210           dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1211
1212     switch(wMsg) {
1213     case DRV_LOAD:              return 1;
1214     case DRV_FREE:              return 1;
1215     case DRV_OPEN:              return WAVEMAP_drvOpen((LPSTR)dwParam1);
1216     case DRV_CLOSE:             return WAVEMAP_drvClose(dwDevID);
1217     case DRV_ENABLE:            return 1;
1218     case DRV_DISABLE:           return 1;
1219     case DRV_QUERYCONFIGURE:    return 1;
1220     case DRV_CONFIGURE:         MessageBoxA(0, "WAVEMAP MultiMedia Driver !", "Wave mapper Driver", MB_OK);     return 1;
1221     case DRV_INSTALL:           return DRVCNF_RESTART;
1222     case DRV_REMOVE:            return DRVCNF_RESTART;
1223     default:
1224         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1225     }
1226 }