Correctly handle where waveOutGetPosition changes timepos.wType
[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 /**************************************************************************
594  *                              wodMessage (MSACM.@)
595  */
596 DWORD WINAPI WAVEMAP_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
597                                 DWORD dwParam1, DWORD dwParam2)
598 {
599     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
600           wDevID, wMsg, dwUser, dwParam1, dwParam2);
601
602     switch (wMsg) {
603     case DRVM_INIT:
604     case DRVM_EXIT:
605     case DRVM_ENABLE:
606     case DRVM_DISABLE:
607         /* FIXME: Pretend this is supported */
608         return 0;
609     case WODM_OPEN:             return wodOpen          ((LPDWORD)dwUser,      (LPWAVEOPENDESC)dwParam1,dwParam2);
610     case WODM_CLOSE:            return wodClose         ((WAVEMAPDATA*)dwUser);
611     case WODM_WRITE:            return wodWrite         ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
612     case WODM_PAUSE:            return wodPause         ((WAVEMAPDATA*)dwUser);
613     case WODM_GETPOS:           return wodGetPosition   ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1,      dwParam2);
614     case WODM_BREAKLOOP:        return wodBreakLoop     ((WAVEMAPDATA*)dwUser);
615     case WODM_PREPARE:          return wodPrepare       ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
616     case WODM_UNPREPARE:        return wodUnprepare     ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
617     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEOUTCAPSW)dwParam1,dwParam2);
618     case WODM_GETNUMDEVS:       return 1;
619     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
620     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
621     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
622     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
623     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (WAVEMAPDATA*)dwUser, (LPDWORD)dwParam1);
624     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, (WAVEMAPDATA*)dwUser, dwParam1);
625     case WODM_RESTART:          return wodRestart       ((WAVEMAPDATA*)dwUser);
626     case WODM_RESET:            return wodReset         ((WAVEMAPDATA*)dwUser);
627     case WODM_MAPPER_STATUS:    return wodMapperStatus  ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
628     /* known but not supported */
629     case DRV_QUERYDEVICEINTERFACESIZE:
630     case DRV_QUERYDEVICEINTERFACE:
631         return MMSYSERR_NOTSUPPORTED;
632     default:
633         FIXME("unknown message %d!\n", wMsg);
634     }
635     return MMSYSERR_NOTSUPPORTED;
636 }
637
638 /*======================================================================*
639  *                  WAVE IN part                                        *
640  *======================================================================*/
641
642 static void     CALLBACK widCallback(HWAVEIN hWave, UINT uMsg, DWORD dwInstance,
643                                      DWORD dwParam1, DWORD dwParam2)
644 {
645     WAVEMAPDATA*        wim = (WAVEMAPDATA*)dwInstance;
646
647     TRACE("(%p %u %ld %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
648
649     if (!WAVEMAP_IsData(wim)) {
650         ERR("Bad data\n");
651         return;
652     }
653
654     if (hWave != wim->u.in.hInnerWave && uMsg != WIM_OPEN)
655         ERR("Shouldn't happen (%p %p)\n", hWave, wim->u.in.hInnerWave);
656
657     switch (uMsg) {
658     case WIM_OPEN:
659     case WIM_CLOSE:
660         /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
661         break;
662     case WIM_DATA:
663         if (wim->hAcmStream) {
664             LPWAVEHDR           lpWaveHdrSrc = (LPWAVEHDR)dwParam1;
665             PACMSTREAMHEADER    ash = (PACMSTREAMHEADER)((LPSTR)lpWaveHdrSrc - sizeof(ACMSTREAMHEADER));
666             LPWAVEHDR           lpWaveHdrDst = (LPWAVEHDR)ash->dwUser;
667
668             /* convert data just gotten from waveIn into requested format */
669             if (acmStreamConvert(wim->hAcmStream, ash, 0L) != MMSYSERR_NOERROR) {
670                 ERR("ACM conversion failed\n");
671                 return;
672             } else {
673                 TRACE("Converted %ld bytes into %ld\n", ash->cbSrcLengthUsed, ash->cbDstLengthUsed);
674             }
675             /* and setup the wavehdr to return accordingly */
676             lpWaveHdrDst->dwFlags &= ~WHDR_INQUEUE;
677             lpWaveHdrDst->dwFlags |= WHDR_DONE;
678             lpWaveHdrDst->dwBytesRecorded = ash->cbDstLengthUsed;
679             dwParam1 = (DWORD)lpWaveHdrDst;
680         }
681         break;
682     default:
683         ERR("Unknown msg %u\n", uMsg);
684     }
685
686     DriverCallback(wim->dwCallback, HIWORD(wim->dwFlags), (HDRVR)wim->u.in.hOuterWave,
687                    uMsg, wim->dwClientInstance, dwParam1, dwParam2);
688 }
689
690 static  DWORD   widOpenHelper(WAVEMAPDATA* wim, UINT idx,
691                               LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
692                               DWORD dwFlags)
693 {
694     DWORD       ret;
695
696     TRACE("(%p %04x %p %p %08lx)\n", wim, idx, lpDesc, lpwfx, dwFlags);
697
698     /* source is always PCM, so the formulas below apply */
699     lpwfx->nBlockAlign = (lpwfx->nChannels * lpwfx->wBitsPerSample) / 8;
700     lpwfx->nAvgBytesPerSec = lpwfx->nSamplesPerSec * lpwfx->nBlockAlign;
701     if (dwFlags & WAVE_FORMAT_QUERY) {
702         ret = acmStreamOpen(NULL, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY);
703     } else {
704         ret = acmStreamOpen(&wim->hAcmStream, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, 0L);
705     }
706     if (ret == MMSYSERR_NOERROR) {
707         ret = waveInOpen(&wim->u.in.hInnerWave, idx, lpwfx, (DWORD)widCallback,
708                          (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION);
709         if (ret != MMSYSERR_NOERROR && !(dwFlags & WAVE_FORMAT_QUERY)) {
710             acmStreamClose(wim->hAcmStream, 0);
711             wim->hAcmStream = 0;
712         }
713     }
714     TRACE("ret = %08lx\n", ret);
715     return ret;
716 }
717
718 static  DWORD   widOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
719 {
720     UINT                ndlo, ndhi;
721     UINT                i;
722     WAVEMAPDATA*        wim = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
723     DWORD               res;
724
725     TRACE("(%p %p %08lx)\n", lpdwUser, lpDesc, dwFlags);
726
727     if (!wim) {
728         WARN("no memory\n");
729         return MMSYSERR_NOMEM;
730     }
731
732     wim->self = wim;
733     wim->dwCallback = lpDesc->dwCallback;
734     wim->dwFlags = dwFlags;
735     wim->dwClientInstance = lpDesc->dwInstance;
736     wim->u.in.hOuterWave = (HWAVEIN)lpDesc->hWave;
737
738     ndhi = waveInGetNumDevs();
739     if (dwFlags & WAVE_MAPPED) {
740         if (lpDesc->uMappedDeviceID >= ndhi) return MMSYSERR_INVALPARAM;
741         ndlo = lpDesc->uMappedDeviceID;
742         ndhi = ndlo + 1;
743         dwFlags &= ~WAVE_MAPPED;
744     } else {
745         ndlo = 0;
746     }
747
748     wim->avgSpeedOuter = wim->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec;
749     wim->nSamplesPerSecOuter = wim->nSamplesPerSecInner = lpDesc->lpFormat->nSamplesPerSec;
750
751     for (i = ndlo; i < ndhi; i++) {
752         if (waveInOpen(&wim->u.in.hInnerWave, i, lpDesc->lpFormat, (DWORD)widCallback,
753                        (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) {
754             wim->hAcmStream = 0;
755             goto found;
756         }
757     }
758
759     if ((dwFlags & WAVE_FORMAT_DIRECT) == 0)
760     {
761         WAVEFORMATEX    wfx;
762
763         wfx.wFormatTag = WAVE_FORMAT_PCM;
764         wfx.cbSize = 0; /* normally, this field is not used for PCM format, just in case */
765         /* try some ACM stuff */
766
767 #define TRY(sps,bps)    wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
768                         switch (res=widOpenHelper(wim, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT)) { \
769                         case MMSYSERR_NOERROR: wim->avgSpeedInner = wfx.nAvgBytesPerSec; wim->nSamplesPerSecInner = wfx.nSamplesPerSec; goto found; \
770                         case WAVERR_BADFORMAT: break; \
771                         default: goto error; \
772                         }
773
774         for (i = ndlo; i < ndhi; i++) {
775             wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
776             /* first try with same stereo/mono option as source */
777             wfx.nChannels = lpDesc->lpFormat->nChannels;
778                         TRY(wfx.nSamplesPerSec, 16);
779                         TRY(wfx.nSamplesPerSec, 8);
780                         wfx.nChannels ^= 3;
781                         TRY(wfx.nSamplesPerSec, 16);
782                         TRY(wfx.nSamplesPerSec, 8);
783         }
784
785         for (i = ndlo; i < ndhi; i++) {
786             wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
787             /* first try with same stereo/mono option as source */
788             wfx.nChannels = lpDesc->lpFormat->nChannels;
789             TRY(96000, 16);
790             TRY(48000, 16);
791             TRY(44100, 16);
792             TRY(22050, 16);
793             TRY(11025, 16);
794
795             /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
796             wfx.nChannels ^= 3;
797             TRY(96000, 16);
798             TRY(48000, 16);
799             TRY(44100, 16);
800             TRY(22050, 16);
801             TRY(11025, 16);
802
803             /* first try with same stereo/mono option as source */
804             wfx.nChannels = lpDesc->lpFormat->nChannels;
805             TRY(96000, 8);
806             TRY(48000, 8);
807             TRY(44100, 8);
808             TRY(22050, 8);
809             TRY(11025, 8);
810
811             /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
812             wfx.nChannels ^= 3;
813             TRY(96000, 8);
814             TRY(48000, 8);
815             TRY(44100, 8);
816             TRY(22050, 8);
817             TRY(11025, 8);
818         }
819 #undef TRY
820     }
821
822     HeapFree(GetProcessHeap(), 0, wim);
823     WARN("ret = WAVERR_BADFORMAT\n");
824     return WAVERR_BADFORMAT;
825 found:
826     if (dwFlags & WAVE_FORMAT_QUERY) {
827         *lpdwUser = 0L;
828         HeapFree(GetProcessHeap(), 0, wim);
829     } else {
830         *lpdwUser = (DWORD)wim;
831     }
832     TRACE("Ok (stream=%08lx)\n", (DWORD)wim->hAcmStream);
833     return MMSYSERR_NOERROR;
834 error:
835     HeapFree(GetProcessHeap(), 0, wim);
836     if (res==ACMERR_NOTPOSSIBLE) {
837         WARN("ret = WAVERR_BADFORMAT\n");
838         return WAVERR_BADFORMAT;
839     }
840     WARN("ret = 0x%08lx\n", res);
841     return res;
842 }
843
844 static  DWORD   widClose(WAVEMAPDATA* wim)
845 {
846     DWORD ret;
847
848     TRACE("(%p)\n", wim);
849
850     ret = waveInClose(wim->u.in.hInnerWave);
851     if (ret == MMSYSERR_NOERROR) {
852         if (wim->hAcmStream) {
853             ret = acmStreamClose(wim->hAcmStream, 0);
854         }
855         if (ret == MMSYSERR_NOERROR) {
856             HeapFree(GetProcessHeap(), 0, wim);
857         }
858     }
859     return ret;
860 }
861
862 static  DWORD   widAddBuffer(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
863 {
864     PACMSTREAMHEADER    ash;
865     LPWAVEHDR           lpWaveHdrSrc;
866
867     TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
868
869     if (!wim->hAcmStream) {
870         return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
871     }
872
873     lpWaveHdrDst->dwFlags |= WHDR_INQUEUE;
874     ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
875
876     lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
877     return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
878 }
879
880 static  DWORD   widPrepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
881 {
882     PACMSTREAMHEADER    ash;
883     DWORD               size;
884     DWORD               dwRet;
885     LPWAVEHDR           lpWaveHdrSrc;
886
887     TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
888
889     if (!wim->hAcmStream) {
890         return waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
891     }
892     if (acmStreamSize(wim->hAcmStream, lpWaveHdrDst->dwBufferLength, &size,
893                       ACM_STREAMSIZEF_DESTINATION) != MMSYSERR_NOERROR) {
894         WARN("acmStreamSize failed\n");
895         return MMSYSERR_ERROR;
896     }
897
898     ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
899     if (ash == NULL) {
900         WARN("no memory\n");
901         return MMSYSERR_NOMEM;
902     }
903
904     ash->cbStruct = sizeof(*ash);
905     ash->fdwStatus = 0L;
906     ash->dwUser = (DWORD)lpWaveHdrDst;
907     ash->pbSrc = (LPSTR)ash + sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR);
908     ash->cbSrcLength = size;
909     /* ash->cbSrcLengthUsed */
910     ash->dwSrcUser = 0L; /* FIXME ? */
911     ash->pbDst = lpWaveHdrDst->lpData;
912     ash->cbDstLength = lpWaveHdrDst->dwBufferLength;
913     /* ash->cbDstLengthUsed */
914     ash->dwDstUser = lpWaveHdrDst->dwUser; /* FIXME ? */
915     dwRet = acmStreamPrepareHeader(wim->hAcmStream, ash, 0L);
916     if (dwRet != MMSYSERR_NOERROR) {
917         WARN("acmStreamPrepareHeader failed\n");
918         goto errCleanUp;
919     }
920
921     lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
922     lpWaveHdrSrc->lpData = ash->pbSrc;
923     lpWaveHdrSrc->dwBufferLength = size; /* conversion is not done yet */
924     lpWaveHdrSrc->dwFlags = 0;
925     lpWaveHdrSrc->dwLoops = 0;
926     dwRet = waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
927     if (dwRet != MMSYSERR_NOERROR) {
928         WARN("waveInPrepareHeader failed\n");
929         goto errCleanUp;
930     }
931
932     lpWaveHdrDst->reserved = (DWORD)ash;
933     lpWaveHdrDst->dwFlags = WHDR_PREPARED;
934     TRACE("=> (0)\n");
935     return MMSYSERR_NOERROR;
936 errCleanUp:
937     TRACE("=> (%ld)\n", dwRet);
938     HeapFree(GetProcessHeap(), 0, ash);
939     return dwRet;
940 }
941
942 static  DWORD   widUnprepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
943 {
944     PACMSTREAMHEADER    ash;
945     LPWAVEHDR           lpWaveHdrSrc;
946     DWORD               dwRet1, dwRet2;
947
948     TRACE("(%p %p %08lx)\n", wim, lpWaveHdrDst, dwParam2);
949
950     if (!wim->hAcmStream) {
951         return waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
952     }
953     ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
954     dwRet1 = acmStreamUnprepareHeader(wim->hAcmStream, ash, 0L);
955
956     lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
957     dwRet2 = waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
958
959     HeapFree(GetProcessHeap(), 0, ash);
960
961     lpWaveHdrDst->dwFlags &= ~WHDR_PREPARED;
962     return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
963 }
964
965 static  DWORD   widGetPosition(WAVEMAPDATA* wim, LPMMTIME lpTime, DWORD dwParam2)
966 {
967     DWORD       val;
968     MMTIME      timepos;
969     TRACE("(%p %p %08lx)\n", wim, lpTime, dwParam2);
970
971     memcpy(&timepos, lpTime, sizeof(timepos));
972
973     /* For TIME_MS, we're going to recalculate using TIME_BYTES */
974     if (lpTime->wType == TIME_MS)
975         timepos.wType = TIME_BYTES;
976
977     /* This can change timepos.wType if the requested type is not supported */
978     val = waveInGetPosition(wim->u.in.hInnerWave, &timepos, dwParam2);
979
980     if (timepos.wType == TIME_BYTES)
981     {
982         DWORD dwInnerSamplesPerOuter = wim->nSamplesPerSecInner / wim->nSamplesPerSecOuter;
983         if (dwInnerSamplesPerOuter > 0)
984         {
985             DWORD dwInnerBytesPerSample = wim->avgSpeedInner / wim->nSamplesPerSecInner;
986             DWORD dwInnerBytesPerOuterSample = dwInnerBytesPerSample * dwInnerSamplesPerOuter;
987             DWORD remainder = 0;
988
989             /* If we are up sampling (going from lower sample rate to higher),
990             **   we need to make a special accomodation for times when we've
991             **   written a partial output sample.  This happens frequently
992             **   to us because we use msacm to do our up sampling, and it
993             **   will up sample on an unaligned basis.
994             ** For example, if you convert a 2 byte wide 8,000 'outer'
995             **   buffer to a 2 byte wide 48,000 inner device, you would
996             **   expect 2 bytes of input to produce 12 bytes of output.
997             **   Instead, msacm will produce 8 bytes of output.
998             **   But reporting our position as 1 byte of output is
999             **   nonsensical; the output buffer position needs to be
1000             **   aligned on outer sample size, and aggressively rounded up.
1001             */
1002             remainder = timepos.u.cb % dwInnerBytesPerOuterSample;
1003             if (remainder > 0)
1004             {
1005                 timepos.u.cb -= remainder;
1006                 timepos.u.cb += dwInnerBytesPerOuterSample;
1007             }
1008         }
1009
1010         lpTime->u.cb = MulDiv(timepos.u.cb, wim->avgSpeedOuter, wim->avgSpeedInner);
1011
1012         /* Once we have the TIME_BYTES right, we can easily convert to TIME_MS */
1013         if (lpTime->wType == TIME_MS)
1014             lpTime->u.ms = MulDiv(lpTime->u.cb, 1000, wim->avgSpeedOuter);
1015         else
1016             lpTime->wType = TIME_BYTES;
1017     }
1018     else if (lpTime->wType == TIME_SAMPLES && timepos.wType == TIME_SAMPLES)
1019         lpTime->u.sample = MulDiv(timepos.u.sample, wim->nSamplesPerSecOuter, wim->nSamplesPerSecInner);
1020     else
1021         /* other time types don't require conversion */
1022         lpTime->u = timepos.u;
1023
1024     return val;
1025 }
1026
1027 static  DWORD   widGetDevCaps(UINT wDevID, WAVEMAPDATA* wim, LPWAVEINCAPSW lpWaveCaps, DWORD dwParam2)
1028 {
1029     TRACE("(%04x, %p %p %08lx)\n", wDevID, wim, lpWaveCaps, dwParam2);
1030
1031     /* if opened low driver, forward message */
1032     if (WAVEMAP_IsData(wim))
1033         return waveInGetDevCapsW((UINT)wim->u.in.hInnerWave, lpWaveCaps, dwParam2);
1034     /* else if no drivers, nothing to map so return bad device */
1035     if (waveInGetNumDevs() == 0) {
1036         WARN("bad device id\n");
1037         return MMSYSERR_BADDEVICEID;
1038     }
1039     /* otherwise, return caps of mapper itself */
1040     if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
1041         WAVEINCAPSW wic;
1042         static const WCHAR init[] = {'W','i','n','e',' ','w','a','v','e',' ','i','n',' ','m','a','p','p','e','r',0};
1043         wic.wMid = 0x00FF;
1044         wic.wPid = 0x0001;
1045         wic.vDriverVersion = 0x0001;
1046         strcpyW(wic.szPname, init);
1047         wic.dwFormats =
1048             WAVE_FORMAT_96M08 | WAVE_FORMAT_96S08 | WAVE_FORMAT_96M16 | WAVE_FORMAT_96S16 |
1049             WAVE_FORMAT_48M08 | WAVE_FORMAT_48S08 | WAVE_FORMAT_48M16 | WAVE_FORMAT_48S16 |
1050             WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
1051             WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
1052             WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
1053         wic.wChannels = 2;
1054         memcpy(lpWaveCaps, &wic, min(dwParam2, sizeof(wic)));
1055
1056         return MMSYSERR_NOERROR;
1057     }
1058     ERR("This shouldn't happen\n");
1059     return MMSYSERR_ERROR;
1060 }
1061
1062 static  DWORD   widStop(WAVEMAPDATA* wim)
1063 {
1064     TRACE("(%p)\n", wim);
1065
1066     return waveInStop(wim->u.in.hInnerWave);
1067 }
1068
1069 static  DWORD   widStart(WAVEMAPDATA* wim)
1070 {
1071     TRACE("(%p)\n", wim);
1072
1073     return waveInStart(wim->u.in.hInnerWave);
1074 }
1075
1076 static  DWORD   widReset(WAVEMAPDATA* wim)
1077 {
1078     TRACE("(%p)\n", wim);
1079
1080     return waveInReset(wim->u.in.hInnerWave);
1081 }
1082
1083 static  DWORD   widMapperStatus(WAVEMAPDATA* wim, DWORD flags, LPVOID ptr)
1084 {
1085     UINT        id;
1086     DWORD       ret = MMSYSERR_NOTSUPPORTED;
1087
1088     TRACE("(%p %08lx %p)\n", wim, flags, ptr);
1089
1090     switch (flags) {
1091     case WAVEIN_MAPPER_STATUS_DEVICE:
1092         ret = waveInGetID(wim->u.in.hInnerWave, &id);
1093         *(LPDWORD)ptr = id;
1094         break;
1095     case WAVEIN_MAPPER_STATUS_MAPPED:
1096         FIXME("Unsupported yet flag=%ld\n", flags);
1097         *(LPDWORD)ptr = 0; /* FIXME ?? */
1098         break;
1099     case WAVEIN_MAPPER_STATUS_FORMAT:
1100         FIXME("Unsupported flag=%ld\n", flags);
1101         /* ptr points to a WAVEFORMATEX struct  - before or after streaming ? */
1102         *(LPDWORD)ptr = 0; /* FIXME ?? */
1103         break;
1104     default:
1105         FIXME("Unsupported flag=%ld\n", flags);
1106         *(LPDWORD)ptr = 0;
1107         break;
1108     }
1109     return ret;
1110 }
1111
1112 /**************************************************************************
1113  *                              widMessage (MSACM.@)
1114  */
1115 DWORD WINAPI WAVEMAP_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
1116                                 DWORD dwParam1, DWORD dwParam2)
1117 {
1118     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
1119           wDevID, wMsg, dwUser, dwParam1, dwParam2);
1120
1121     switch (wMsg) {
1122     case DRVM_INIT:
1123     case DRVM_EXIT:
1124     case DRVM_ENABLE:
1125     case DRVM_DISABLE:
1126         /* FIXME: Pretend this is supported */
1127         return 0;
1128
1129     case WIDM_OPEN:             return widOpen          ((LPDWORD)dwUser,     (LPWAVEOPENDESC)dwParam1, dwParam2);
1130     case WIDM_CLOSE:            return widClose         ((WAVEMAPDATA*)dwUser);
1131
1132     case WIDM_ADDBUFFER:        return widAddBuffer     ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
1133     case WIDM_PREPARE:          return widPrepare       ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
1134     case WIDM_UNPREPARE:        return widUnprepare     ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
1135     case WIDM_GETDEVCAPS:       return widGetDevCaps    (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEINCAPSW)dwParam1, dwParam2);
1136     case WIDM_GETNUMDEVS:       return 1;
1137     case WIDM_GETPOS:           return widGetPosition   ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1,      dwParam2);
1138     case WIDM_RESET:            return widReset         ((WAVEMAPDATA*)dwUser);
1139     case WIDM_START:            return widStart         ((WAVEMAPDATA*)dwUser);
1140     case WIDM_STOP:             return widStop          ((WAVEMAPDATA*)dwUser);
1141     case WIDM_MAPPER_STATUS:    return widMapperStatus  ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
1142     /* known but not supported */
1143     case DRV_QUERYDEVICEINTERFACESIZE:
1144     case DRV_QUERYDEVICEINTERFACE:
1145         return MMSYSERR_NOTSUPPORTED;
1146     default:
1147         FIXME("unknown message %u!\n", wMsg);
1148     }
1149     return MMSYSERR_NOTSUPPORTED;
1150 }
1151
1152 /*======================================================================*
1153  *                  Driver part                                         *
1154  *======================================================================*/
1155
1156 static  struct WINE_WAVEMAP* oss = NULL;
1157
1158 /**************************************************************************
1159  *                              WAVEMAP_drvOpen                 [internal]
1160  */
1161 static  DWORD   WAVEMAP_drvOpen(LPSTR str)
1162 {
1163     TRACE("(%p)\n", str);
1164
1165     if (oss)
1166         return 0;
1167
1168     /* I know, this is ugly, but who cares... */
1169     oss = (struct WINE_WAVEMAP*)1;
1170     return 1;
1171 }
1172
1173 /**************************************************************************
1174  *                              WAVEMAP_drvClose                [internal]
1175  */
1176 static  DWORD   WAVEMAP_drvClose(DWORD dwDevID)
1177 {
1178     TRACE("(%08lx)\n", dwDevID);
1179
1180     if (oss) {
1181         oss = NULL;
1182         return 1;
1183     }
1184     return 0;
1185 }
1186
1187 /**************************************************************************
1188  *                              DriverProc (MSACM.@)
1189  */
1190 LONG CALLBACK   WAVEMAP_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
1191                                    DWORD dwParam1, DWORD dwParam2)
1192 {
1193     TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
1194           dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1195
1196     switch(wMsg) {
1197     case DRV_LOAD:              return 1;
1198     case DRV_FREE:              return 1;
1199     case DRV_OPEN:              return WAVEMAP_drvOpen((LPSTR)dwParam1);
1200     case DRV_CLOSE:             return WAVEMAP_drvClose(dwDevID);
1201     case DRV_ENABLE:            return 1;
1202     case DRV_DISABLE:           return 1;
1203     case DRV_QUERYCONFIGURE:    return 1;
1204     case DRV_CONFIGURE:         MessageBoxA(0, "WAVEMAP MultiMedia Driver !", "Wave mapper Driver", MB_OK);     return 1;
1205     case DRV_INSTALL:           return DRVCNF_RESTART;
1206     case DRV_REMOVE:            return DRVCNF_RESTART;
1207     default:
1208         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
1209     }
1210 }