DEBUG_cmp_sym: micro optimization/cleanup.
[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/debug.h"
41
42 WINE_DEFAULT_DEBUG_CHANNEL(msacm);
43
44 typedef struct tagWAVEMAPDATA {
45     struct tagWAVEMAPDATA*      self;
46     union {
47         struct {
48             HWAVEOUT    hOuterWave;
49             HWAVEOUT    hInnerWave;
50         } out;
51         struct {
52             HWAVEIN     hOuterWave;
53             HWAVEIN     hInnerWave;
54         } in;
55     } u;
56     HACMSTREAM  hAcmStream;
57     /* needed data to filter callbacks. Only needed when hAcmStream is not 0 */
58     DWORD       dwCallback;
59     DWORD       dwClientInstance;
60     DWORD       dwFlags;
61     /* ratio to compute position from a PCM playback to any format */
62     DWORD       avgSpeedOuter;
63     DWORD       avgSpeedInner;
64 } WAVEMAPDATA;
65
66 static  BOOL    WAVEMAP_IsData(WAVEMAPDATA* wm)
67 {
68     return (!IsBadReadPtr(wm, sizeof(WAVEMAPDATA)) && wm->self == wm);
69 }
70
71 /*======================================================================*
72  *                  WAVE OUT part                                       *
73  *======================================================================*/
74
75 static void     CALLBACK wodCallback(HWAVEOUT hWave, UINT uMsg, DWORD dwInstance,
76                                      DWORD dwParam1, DWORD dwParam2)
77 {
78     WAVEMAPDATA*        wom = (WAVEMAPDATA*)dwInstance;
79
80     TRACE("(%p %u %ld %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
81
82     if (!WAVEMAP_IsData(wom)) {
83         ERR("Bad data\n");
84         return;
85     }
86
87     if (hWave != wom->u.out.hInnerWave && uMsg != WOM_OPEN)
88         ERR("Shouldn't happen (%p %p)\n", hWave, wom->u.out.hInnerWave);
89
90     switch (uMsg) {
91     case WOM_OPEN:
92     case WOM_CLOSE:
93         /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
94         break;
95     case WOM_DONE:
96         if (wom->hAcmStream) {
97             LPWAVEHDR           lpWaveHdrDst = (LPWAVEHDR)dwParam1;
98             PACMSTREAMHEADER    ash = (PACMSTREAMHEADER)((LPSTR)lpWaveHdrDst - sizeof(ACMSTREAMHEADER));
99             LPWAVEHDR           lpWaveHdrSrc = (LPWAVEHDR)ash->dwUser;
100
101             lpWaveHdrSrc->dwFlags &= ~WHDR_INQUEUE;
102             lpWaveHdrSrc->dwFlags |= WHDR_DONE;
103             dwParam1 = (DWORD)lpWaveHdrSrc;
104         }
105         break;
106     default:
107         ERR("Unknown msg %u\n", uMsg);
108     }
109
110     DriverCallback(wom->dwCallback, HIWORD(wom->dwFlags), (HDRVR)wom->u.out.hOuterWave,
111                    uMsg, wom->dwClientInstance, dwParam1, dwParam2);
112 }
113
114 /******************************************************************
115  *              wodOpenHelper
116  *
117  *
118  */
119 static  DWORD   wodOpenHelper(WAVEMAPDATA* wom, UINT idx,
120                               LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
121                               DWORD dwFlags)
122 {
123     DWORD       ret;
124
125     /* destination is always PCM, so the formulas below apply */
126     lpwfx->nBlockAlign = (lpwfx->nChannels * lpwfx->wBitsPerSample) / 8;
127     lpwfx->nAvgBytesPerSec = lpwfx->nSamplesPerSec * lpwfx->nBlockAlign;
128     if (dwFlags & WAVE_FORMAT_QUERY) {
129         ret = acmStreamOpen(NULL, 0, lpDesc->lpFormat, lpwfx, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY);
130     } else {
131         ret = acmStreamOpen(&wom->hAcmStream, 0, lpDesc->lpFormat, lpwfx, NULL, 0L, 0L, 0L);
132     }
133     if (ret == MMSYSERR_NOERROR) {
134         ret = waveOutOpen(&wom->u.out.hInnerWave, idx, lpwfx, (DWORD)wodCallback,
135                           (DWORD)wom, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION);
136         if (ret != MMSYSERR_NOERROR && !(dwFlags & WAVE_FORMAT_QUERY)) {
137             acmStreamClose(wom->hAcmStream, 0);
138             wom->hAcmStream = 0;
139         }
140     }
141     return ret;
142 }
143
144 static  DWORD   wodOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
145 {
146     UINT                ndlo, ndhi;
147     UINT                i;
148     WAVEMAPDATA*        wom = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
149
150     TRACE("(%p %p %08lx)\n", lpdwUser, lpDesc, dwFlags);
151
152     if (!wom)
153         return MMSYSERR_NOMEM;
154
155     ndhi = waveOutGetNumDevs();
156     if (dwFlags & WAVE_MAPPED) {
157         if (lpDesc->uMappedDeviceID >= ndhi) return MMSYSERR_INVALPARAM;
158         ndlo = lpDesc->uMappedDeviceID;
159         ndhi = ndlo + 1;
160         dwFlags &= ~WAVE_MAPPED;
161     } else {
162         ndlo = 0;
163     }
164     wom->self = wom;
165     wom->dwCallback = lpDesc->dwCallback;
166     wom->dwFlags = dwFlags;
167     wom->dwClientInstance = lpDesc->dwInstance;
168     wom->u.out.hOuterWave = (HWAVEOUT)lpDesc->hWave;
169     wom->avgSpeedOuter = wom->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec;
170
171     for (i = ndlo; i < ndhi; i++) {
172         /* if no ACM stuff is involved, no need to handle callbacks at this
173          * level, this will be done transparently
174          */
175         if (waveOutOpen(&wom->u.out.hInnerWave, i, lpDesc->lpFormat, (DWORD)wodCallback,
176                         (DWORD)wom, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) {
177             wom->hAcmStream = 0;
178             goto found;
179         }
180     }
181
182     if ((dwFlags & WAVE_FORMAT_DIRECT) == 0) {
183         WAVEFORMATEX    wfx;
184
185         wfx.wFormatTag = WAVE_FORMAT_PCM;
186         wfx.cbSize = 0; /* normally, this field is not used for PCM format, just in case */
187         /* try some ACM stuff */
188
189 #define TRY(sps,bps)    wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
190                         switch (wodOpenHelper(wom, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT)) { \
191                             case MMSYSERR_NOERROR: wom->avgSpeedInner = wfx.nAvgBytesPerSec; goto found; \
192                             case WAVERR_BADFORMAT: break; \
193                             default: goto error; \
194                         }
195
196         /* Our resampling algorithm is quite primitive so first try
197          * to just change the bit depth and number of channels
198          */
199         for (i = ndlo; i < ndhi; i++) {
200             wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
201             wfx.nChannels = lpDesc->lpFormat->nChannels;
202             TRY(wfx.nSamplesPerSec, 16);
203             TRY(wfx.nSamplesPerSec, 8);
204             wfx.nChannels ^= 3;
205             TRY(wfx.nSamplesPerSec, 16);
206             TRY(wfx.nSamplesPerSec, 8);
207         }
208
209         for (i = ndlo; i < ndhi; i++) {
210             /* first try with same stereo/mono option as source */
211             wfx.nChannels = lpDesc->lpFormat->nChannels;
212             TRY(96000, 16);
213             TRY(48000, 16);
214             TRY(44100, 16);
215             TRY(22050, 16);
216             TRY(11025, 16);
217
218             /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
219             wfx.nChannels ^= 3;
220             TRY(96000, 16);
221             TRY(48000, 16);
222             TRY(44100, 16);
223             TRY(22050, 16);
224             TRY(11025, 16);
225
226             /* first try with same stereo/mono option as source */
227             wfx.nChannels = lpDesc->lpFormat->nChannels;
228             TRY(96000, 8);
229             TRY(48000, 8);
230             TRY(44100, 8);
231             TRY(22050, 8);
232             TRY(11025, 8);
233
234             /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
235             wfx.nChannels ^= 3;
236             TRY(96000, 8);
237             TRY(48000, 8);
238             TRY(44100, 8);
239             TRY(22050, 8);
240             TRY(11025, 8);
241         }
242 #undef TRY
243     }
244
245     HeapFree(GetProcessHeap(), 0, wom);
246     return WAVERR_BADFORMAT;
247
248 found:
249     if (dwFlags & WAVE_FORMAT_QUERY) {
250         *lpdwUser = 0L;
251         HeapFree(GetProcessHeap(), 0, wom);
252     } else {
253         *lpdwUser = (DWORD)wom;
254     }
255     return MMSYSERR_NOERROR;
256 error:
257     HeapFree(GetProcessHeap(), 0, wom);
258     return MMSYSERR_ERROR;
259 }
260
261 static  DWORD   wodClose(WAVEMAPDATA* wom)
262 {
263     DWORD ret = waveOutClose(wom->u.out.hInnerWave);
264
265     if (ret == MMSYSERR_NOERROR) {
266         if (wom->hAcmStream) {
267             ret = acmStreamClose(wom->hAcmStream, 0);
268         }
269         if (ret == MMSYSERR_NOERROR) {
270             HeapFree(GetProcessHeap(), 0, wom);
271         }
272     }
273     return ret;
274 }
275
276 static  DWORD   wodWrite(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
277 {
278     PACMSTREAMHEADER    ash;
279     LPWAVEHDR           lpWaveHdrDst;
280
281     if (!wom->hAcmStream) {
282         return waveOutWrite(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
283     }
284
285     lpWaveHdrSrc->dwFlags |= WHDR_INQUEUE;
286     ash = (PACMSTREAMHEADER)lpWaveHdrSrc->reserved;
287     /* acmStreamConvert will actually check that the new size is less than initial size */
288     ash->cbSrcLength = lpWaveHdrSrc->dwBufferLength;
289     if (acmStreamConvert(wom->hAcmStream, ash, 0L) != MMSYSERR_NOERROR)
290         return MMSYSERR_ERROR;
291
292     lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
293     if (ash->cbSrcLength > ash->cbSrcLengthUsed)
294         FIXME("Not all src buffer has been written, expect bogus sound\n");
295     else if (ash->cbSrcLength < ash->cbSrcLengthUsed)
296         ERR("CoDec has read more data than it is allowed to\n");
297
298     if (ash->cbDstLengthUsed == 0)
299     {
300         /* something went wrong in decoding */
301         FIXME("Got 0 length\n");
302         return MMSYSERR_ERROR;
303     }
304     lpWaveHdrDst->dwBufferLength = ash->cbDstLengthUsed;
305     return waveOutWrite(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
306 }
307
308 static  DWORD   wodPrepare(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
309 {
310     PACMSTREAMHEADER    ash;
311     DWORD               size;
312     DWORD               dwRet;
313     LPWAVEHDR           lpWaveHdrDst;
314
315     if (!wom->hAcmStream) {
316         return waveOutPrepareHeader(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
317     }
318     if (acmStreamSize(wom->hAcmStream, lpWaveHdrSrc->dwBufferLength, &size, ACM_STREAMSIZEF_SOURCE) != MMSYSERR_NOERROR)
319         return MMSYSERR_ERROR;
320
321     ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
322     if (ash == NULL)
323         return MMSYSERR_NOMEM;
324
325     ash->cbStruct = sizeof(*ash);
326     ash->fdwStatus = 0L;
327     ash->dwUser = (DWORD)lpWaveHdrSrc;
328     ash->pbSrc = lpWaveHdrSrc->lpData;
329     ash->cbSrcLength = lpWaveHdrSrc->dwBufferLength;
330     /* ash->cbSrcLengthUsed */
331     ash->dwSrcUser = lpWaveHdrSrc->dwUser; /* FIXME ? */
332     ash->pbDst = (LPSTR)ash + sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR);
333     ash->cbDstLength = size;
334     /* ash->cbDstLengthUsed */
335     ash->dwDstUser = 0; /* FIXME ? */
336     dwRet = acmStreamPrepareHeader(wom->hAcmStream, ash, 0L);
337     if (dwRet != MMSYSERR_NOERROR)
338         goto errCleanUp;
339
340     lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
341     lpWaveHdrDst->lpData = ash->pbDst;
342     lpWaveHdrDst->dwBufferLength = size; /* conversion is not done yet */
343     lpWaveHdrDst->dwFlags = 0;
344     lpWaveHdrDst->dwLoops = 0;
345     dwRet = waveOutPrepareHeader(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
346     if (dwRet != MMSYSERR_NOERROR)
347         goto errCleanUp;
348
349     lpWaveHdrSrc->reserved = (DWORD)ash;
350     lpWaveHdrSrc->dwFlags = WHDR_PREPARED;
351     TRACE("=> (0)\n");
352     return MMSYSERR_NOERROR;
353 errCleanUp:
354     TRACE("=> (%ld)\n", dwRet);
355     HeapFree(GetProcessHeap(), 0, ash);
356     return dwRet;
357 }
358
359 static  DWORD   wodUnprepare(WAVEMAPDATA* wom, LPWAVEHDR lpWaveHdrSrc, DWORD dwParam2)
360 {
361     PACMSTREAMHEADER    ash;
362     LPWAVEHDR           lpWaveHdrDst;
363     DWORD               dwRet1, dwRet2;
364
365     if (!wom->hAcmStream) {
366         return waveOutUnprepareHeader(wom->u.out.hInnerWave, lpWaveHdrSrc, dwParam2);
367     }
368     ash = (PACMSTREAMHEADER)lpWaveHdrSrc->reserved;
369     dwRet1 = acmStreamUnprepareHeader(wom->hAcmStream, ash, 0L);
370
371     lpWaveHdrDst = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
372     dwRet2 = waveOutUnprepareHeader(wom->u.out.hInnerWave, lpWaveHdrDst, sizeof(*lpWaveHdrDst));
373
374     HeapFree(GetProcessHeap(), 0, ash);
375
376     lpWaveHdrSrc->dwFlags &= ~WHDR_PREPARED;
377     return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
378 }
379
380 static  DWORD   wodGetPosition(WAVEMAPDATA* wom, LPMMTIME lpTime, DWORD dwParam2)
381 {
382     DWORD       val = waveOutGetPosition(wom->u.out.hInnerWave, lpTime, dwParam2);
383     if (lpTime->wType == TIME_BYTES)
384         lpTime->u.cb = MulDiv(lpTime->u.cb, wom->avgSpeedOuter, wom->avgSpeedInner);
385     /* other time types don't require conversion */
386     return val;
387 }
388
389 static  DWORD   wodGetDevCaps(UINT wDevID, WAVEMAPDATA* wom, LPWAVEOUTCAPSA lpWaveCaps, DWORD dwParam2)
390 {
391     /* if opened low driver, forward message */
392     if (WAVEMAP_IsData(wom))
393         return waveOutGetDevCapsA((UINT)wom->u.out.hInnerWave, lpWaveCaps, dwParam2);
394     /* otherwise, return caps of mapper itself */
395     if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
396         lpWaveCaps->wMid = 0x00FF;
397         lpWaveCaps->wPid = 0x0001;
398         lpWaveCaps->vDriverVersion = 0x0100;
399         strcpy(lpWaveCaps->szPname, "Wine wave out mapper");
400         lpWaveCaps->dwFormats =
401             WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
402             WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
403             WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
404         lpWaveCaps->wChannels = 2;
405         lpWaveCaps->dwSupport = WAVECAPS_VOLUME | WAVECAPS_LRVOLUME;
406
407         return MMSYSERR_NOERROR;
408     }
409     ERR("This shouldn't happen\n");
410     return MMSYSERR_ERROR;
411 }
412
413 static  DWORD   wodGetVolume(UINT wDevID, WAVEMAPDATA* wom, LPDWORD lpVol)
414 {
415     if (WAVEMAP_IsData(wom))
416         return waveOutGetVolume(wom->u.out.hInnerWave, lpVol);
417     return MMSYSERR_NOERROR;
418 }
419
420 static  DWORD   wodSetVolume(UINT wDevID, WAVEMAPDATA* wom, DWORD vol)
421 {
422     if (WAVEMAP_IsData(wom))
423         return waveOutSetVolume(wom->u.out.hInnerWave, vol);
424     return MMSYSERR_NOERROR;
425 }
426
427 static  DWORD   wodPause(WAVEMAPDATA* wom)
428 {
429     return waveOutPause(wom->u.out.hInnerWave);
430 }
431
432 static  DWORD   wodRestart(WAVEMAPDATA* wom)
433 {
434     return waveOutRestart(wom->u.out.hInnerWave);
435 }
436
437 static  DWORD   wodReset(WAVEMAPDATA* wom)
438 {
439     return waveOutReset(wom->u.out.hInnerWave);
440 }
441
442 static  DWORD   wodBreakLoop(WAVEMAPDATA* wom)
443 {
444     return waveOutBreakLoop(wom->u.out.hInnerWave);
445 }
446
447 static  DWORD   wodMapperStatus(WAVEMAPDATA* wom, DWORD flags, LPVOID ptr)
448 {
449     UINT        id;
450     DWORD       ret = MMSYSERR_NOTSUPPORTED;
451
452     switch (flags) {
453     case WAVEOUT_MAPPER_STATUS_DEVICE:
454         ret = waveOutGetID(wom->u.out.hInnerWave, &id);
455         *(LPDWORD)ptr = id;
456         break;
457     case WAVEOUT_MAPPER_STATUS_MAPPED:
458         FIXME("Unsupported flag=%ld\n", flags);
459         *(LPDWORD)ptr = 0; /* FIXME ?? */
460         break;
461     case WAVEOUT_MAPPER_STATUS_FORMAT:
462         FIXME("Unsupported flag=%ld\n", flags);
463         /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
464         *(LPDWORD)ptr = 0;
465         break;
466     default:
467         FIXME("Unsupported flag=%ld\n", flags);
468         *(LPDWORD)ptr = 0;
469         break;
470     }
471     return ret;
472 }
473
474 /**************************************************************************
475  *                              wodMessage (MSACM.@)
476  */
477 DWORD WINAPI WAVEMAP_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
478                                 DWORD dwParam1, DWORD dwParam2)
479 {
480     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
481           wDevID, wMsg, dwUser, dwParam1, dwParam2);
482
483     switch (wMsg) {
484     case DRVM_INIT:
485     case DRVM_EXIT:
486     case DRVM_ENABLE:
487     case DRVM_DISABLE:
488         /* FIXME: Pretend this is supported */
489         return 0;
490     case WODM_OPEN:             return wodOpen          ((LPDWORD)dwUser,      (LPWAVEOPENDESC)dwParam1,dwParam2);
491     case WODM_CLOSE:            return wodClose         ((WAVEMAPDATA*)dwUser);
492     case WODM_WRITE:            return wodWrite         ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
493     case WODM_PAUSE:            return wodPause         ((WAVEMAPDATA*)dwUser);
494     case WODM_GETPOS:           return wodGetPosition   ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1,      dwParam2);
495     case WODM_BREAKLOOP:        return wodBreakLoop     ((WAVEMAPDATA*)dwUser);
496     case WODM_PREPARE:          return wodPrepare       ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
497     case WODM_UNPREPARE:        return wodUnprepare     ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
498     case WODM_GETDEVCAPS:       return wodGetDevCaps    (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEOUTCAPSA)dwParam1,dwParam2);
499     case WODM_GETNUMDEVS:       return 1;
500     case WODM_GETPITCH:         return MMSYSERR_NOTSUPPORTED;
501     case WODM_SETPITCH:         return MMSYSERR_NOTSUPPORTED;
502     case WODM_GETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
503     case WODM_SETPLAYBACKRATE:  return MMSYSERR_NOTSUPPORTED;
504     case WODM_GETVOLUME:        return wodGetVolume     (wDevID, (WAVEMAPDATA*)dwUser, (LPDWORD)dwParam1);
505     case WODM_SETVOLUME:        return wodSetVolume     (wDevID, (WAVEMAPDATA*)dwUser, dwParam1);
506     case WODM_RESTART:          return wodRestart       ((WAVEMAPDATA*)dwUser);
507     case WODM_RESET:            return wodReset         ((WAVEMAPDATA*)dwUser);
508     case WODM_MAPPER_STATUS:    return wodMapperStatus  ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
509     default:
510         FIXME("unknown message %d!\n", wMsg);
511     }
512     return MMSYSERR_NOTSUPPORTED;
513 }
514
515 /*======================================================================*
516  *                  WAVE IN part                                        *
517  *======================================================================*/
518
519 static void     CALLBACK widCallback(HWAVEIN hWave, UINT uMsg, DWORD dwInstance,
520                                      DWORD dwParam1, DWORD dwParam2)
521 {
522     WAVEMAPDATA*        wim = (WAVEMAPDATA*)dwInstance;
523
524     TRACE("(%p %u %ld %lx %lx);\n", hWave, uMsg, dwInstance, dwParam1, dwParam2);
525
526     if (!WAVEMAP_IsData(wim)) {
527         ERR("Bad data\n");
528         return;
529     }
530
531     if (hWave != wim->u.in.hInnerWave && uMsg != WIM_OPEN)
532         ERR("Shouldn't happen (%p %p)\n", hWave, wim->u.in.hInnerWave);
533
534     switch (uMsg) {
535     case WIM_OPEN:
536     case WIM_CLOSE:
537         /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
538         break;
539     case WIM_DATA:
540         if (wim->hAcmStream) {
541             LPWAVEHDR           lpWaveHdrSrc = (LPWAVEHDR)dwParam1;
542             PACMSTREAMHEADER    ash = (PACMSTREAMHEADER)((LPSTR)lpWaveHdrSrc - sizeof(ACMSTREAMHEADER));
543             LPWAVEHDR           lpWaveHdrDst = (LPWAVEHDR)ash->dwUser;
544
545             /* convert data just gotten from waveIn into requested format */
546             if (acmStreamConvert(wim->hAcmStream, ash, 0L) != MMSYSERR_NOERROR) {
547                 ERR("ACM conversion failed\n");
548                 return;
549             } else {
550                 TRACE("Converted %ld bytes into %ld\n", ash->cbSrcLengthUsed, ash->cbDstLengthUsed);
551             }
552             /* and setup the wavehdr to return accordingly */
553             lpWaveHdrDst->dwFlags &= ~WHDR_INQUEUE;
554             lpWaveHdrDst->dwFlags |= WHDR_DONE;
555             lpWaveHdrDst->dwBytesRecorded = ash->cbDstLengthUsed;
556             dwParam1 = (DWORD)lpWaveHdrDst;
557         }
558         break;
559     default:
560         ERR("Unknown msg %u\n", uMsg);
561     }
562
563     DriverCallback(wim->dwCallback, HIWORD(wim->dwFlags), (HDRVR)wim->u.in.hOuterWave,
564                    uMsg, wim->dwClientInstance, dwParam1, dwParam2);
565 }
566
567 static  DWORD   widOpenHelper(WAVEMAPDATA* wim, UINT idx,
568                               LPWAVEOPENDESC lpDesc, LPWAVEFORMATEX lpwfx,
569                               DWORD dwFlags)
570 {
571     DWORD       ret;
572
573     /* source is always PCM, so the formulas below apply */
574     lpwfx->nBlockAlign = (lpwfx->nChannels * lpwfx->wBitsPerSample) / 8;
575     lpwfx->nAvgBytesPerSec = lpwfx->nSamplesPerSec * lpwfx->nBlockAlign;
576     if (dwFlags & WAVE_FORMAT_QUERY) {
577         ret = acmStreamOpen(NULL, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, ACM_STREAMOPENF_QUERY);
578     } else {
579         ret = acmStreamOpen(&wim->hAcmStream, 0, lpwfx, lpDesc->lpFormat, NULL, 0L, 0L, 0L);
580     }
581     if (ret == MMSYSERR_NOERROR) {
582         ret = waveInOpen(&wim->u.in.hInnerWave, idx, lpwfx, (DWORD)widCallback,
583                          (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION);
584         if (ret != MMSYSERR_NOERROR && !(dwFlags & WAVE_FORMAT_QUERY)) {
585             acmStreamClose(wim->hAcmStream, 0);
586             wim->hAcmStream = 0;
587         }
588     }
589     return ret;
590 }
591
592 static  DWORD   widOpen(LPDWORD lpdwUser, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
593 {
594     UINT                ndlo, ndhi;
595     UINT                i;
596     WAVEMAPDATA*        wim = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA));
597
598     TRACE("(%p %p %08lx)\n", lpdwUser, lpDesc, dwFlags);
599
600     if (!wim)
601         return MMSYSERR_NOMEM;
602
603     wim->self = wim;
604     wim->dwCallback = lpDesc->dwCallback;
605     wim->dwFlags = dwFlags;
606     wim->dwClientInstance = lpDesc->dwInstance;
607     wim->u.in.hOuterWave = (HWAVEIN)lpDesc->hWave;
608
609     ndhi = waveInGetNumDevs();
610     if (dwFlags & WAVE_MAPPED) {
611         if (lpDesc->uMappedDeviceID >= ndhi) return MMSYSERR_INVALPARAM;
612         ndlo = lpDesc->uMappedDeviceID;
613         ndhi = ndlo + 1;
614         dwFlags &= ~WAVE_MAPPED;
615     } else {
616         ndlo = 0;
617     }
618
619     wim->avgSpeedOuter = wim->avgSpeedInner = lpDesc->lpFormat->nAvgBytesPerSec;
620
621     for (i = ndlo; i < ndhi; i++) {
622         if (waveInOpen(&wim->u.in.hInnerWave, i, lpDesc->lpFormat, (DWORD)widCallback,
623                        (DWORD)wim, (dwFlags & ~CALLBACK_TYPEMASK) | CALLBACK_FUNCTION | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) {
624             wim->hAcmStream = 0;
625             goto found;
626         }
627     }
628
629     if ((dwFlags & WAVE_FORMAT_DIRECT) == 0)
630     {
631         WAVEFORMATEX    wfx;
632
633         wfx.wFormatTag = WAVE_FORMAT_PCM;
634         wfx.cbSize = 0; /* normally, this field is not used for PCM format, just in case */
635         /* try some ACM stuff */
636         
637 #define TRY(sps,bps)    wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
638                         switch (widOpenHelper(wim, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT)) { \
639                         case MMSYSERR_NOERROR: wim->avgSpeedInner = wfx.nAvgBytesPerSec; goto found; \
640                         case WAVERR_BADFORMAT: break; \
641                         default: goto error; \
642                         }
643         
644         for (i = ndlo; i < ndhi; i++) {
645             wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
646             /* first try with same stereo/mono option as source */
647             wfx.nChannels = lpDesc->lpFormat->nChannels;
648                         TRY(wfx.nSamplesPerSec, 16);
649                         TRY(wfx.nSamplesPerSec, 8);
650                         wfx.nChannels ^= 3;
651                         TRY(wfx.nSamplesPerSec, 16);
652                         TRY(wfx.nSamplesPerSec, 8);
653         }
654
655         for (i = ndlo; i < ndhi; i++) {
656             wfx.nSamplesPerSec=lpDesc->lpFormat->nSamplesPerSec;
657             /* first try with same stereo/mono option as source */
658             wfx.nChannels = lpDesc->lpFormat->nChannels;
659             TRY(96000, 16);
660             TRY(48000, 16);
661             TRY(44100, 16);
662             TRY(22050, 16);
663             TRY(11025, 16);
664             
665             /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
666             wfx.nChannels ^= 3;
667             TRY(96000, 16);
668             TRY(48000, 16);
669             TRY(44100, 16);
670             TRY(22050, 16);
671             TRY(11025, 16);
672
673             /* first try with same stereo/mono option as source */
674             wfx.nChannels = lpDesc->lpFormat->nChannels;
675             TRY(96000, 8);
676             TRY(48000, 8);
677             TRY(44100, 8);
678             TRY(22050, 8);
679             TRY(11025, 8);
680             
681             /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
682             wfx.nChannels ^= 3;
683             TRY(96000, 8);
684             TRY(48000, 8);
685             TRY(44100, 8);
686             TRY(22050, 8);
687             TRY(11025, 8);
688         }
689 #undef TRY
690     }
691
692     HeapFree(GetProcessHeap(), 0, wim);
693     return WAVERR_BADFORMAT;
694 found:
695     if (dwFlags & WAVE_FORMAT_QUERY) {
696         *lpdwUser = 0L;
697         HeapFree(GetProcessHeap(), 0, wim);
698     } else {
699         *lpdwUser = (DWORD)wim;
700     }
701     TRACE("Ok (stream=%08lx)\n", (DWORD)wim->hAcmStream);
702     return MMSYSERR_NOERROR;
703 error:
704     HeapFree(GetProcessHeap(), 0, wim);
705     return MMSYSERR_ERROR;
706 }
707
708 static  DWORD   widClose(WAVEMAPDATA* wim)
709 {
710     DWORD ret = waveInClose(wim->u.in.hInnerWave);
711
712     if (ret == MMSYSERR_NOERROR) {
713         if (wim->hAcmStream) {
714             ret = acmStreamClose(wim->hAcmStream, 0);
715         }
716         if (ret == MMSYSERR_NOERROR) {
717             HeapFree(GetProcessHeap(), 0, wim);
718         }
719     }
720     return ret;
721 }
722
723 static  DWORD   widAddBuffer(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
724 {
725     PACMSTREAMHEADER    ash;
726     LPWAVEHDR           lpWaveHdrSrc;
727
728     if (!wim->hAcmStream) {
729         return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
730     }
731
732     lpWaveHdrDst->dwFlags |= WHDR_INQUEUE;
733     ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
734
735     lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
736     return waveInAddBuffer(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
737 }
738
739 static  DWORD   widPrepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
740 {
741     PACMSTREAMHEADER    ash;
742     DWORD               size;
743     DWORD               dwRet;
744     LPWAVEHDR           lpWaveHdrSrc;
745
746     if (!wim->hAcmStream) {
747         return waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
748     }
749     if (acmStreamSize(wim->hAcmStream, lpWaveHdrDst->dwBufferLength, &size,
750                       ACM_STREAMSIZEF_DESTINATION) != MMSYSERR_NOERROR)
751         return MMSYSERR_ERROR;
752
753     ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR) + size);
754     if (ash == NULL)
755         return MMSYSERR_NOMEM;
756
757     ash->cbStruct = sizeof(*ash);
758     ash->fdwStatus = 0L;
759     ash->dwUser = (DWORD)lpWaveHdrDst;
760     ash->pbSrc = (LPSTR)ash + sizeof(ACMSTREAMHEADER) + sizeof(WAVEHDR);
761     ash->cbSrcLength = size;
762     /* ash->cbSrcLengthUsed */
763     ash->dwSrcUser = 0L; /* FIXME ? */
764     ash->pbDst = lpWaveHdrDst->lpData;
765     ash->cbDstLength = lpWaveHdrDst->dwBufferLength;
766     /* ash->cbDstLengthUsed */
767     ash->dwDstUser = lpWaveHdrDst->dwUser; /* FIXME ? */
768     dwRet = acmStreamPrepareHeader(wim->hAcmStream, ash, 0L);
769     if (dwRet != MMSYSERR_NOERROR)
770         goto errCleanUp;
771
772     lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
773     lpWaveHdrSrc->lpData = ash->pbSrc;
774     lpWaveHdrSrc->dwBufferLength = size; /* conversion is not done yet */
775     lpWaveHdrSrc->dwFlags = 0;
776     lpWaveHdrSrc->dwLoops = 0;
777     dwRet = waveInPrepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
778     if (dwRet != MMSYSERR_NOERROR)
779         goto errCleanUp;
780
781     lpWaveHdrDst->reserved = (DWORD)ash;
782     lpWaveHdrDst->dwFlags = WHDR_PREPARED;
783     TRACE("=> (0)\n");
784     return MMSYSERR_NOERROR;
785 errCleanUp:
786     TRACE("=> (%ld)\n", dwRet);
787     HeapFree(GetProcessHeap(), 0, ash);
788     return dwRet;
789 }
790
791 static  DWORD   widUnprepare(WAVEMAPDATA* wim, LPWAVEHDR lpWaveHdrDst, DWORD dwParam2)
792 {
793     PACMSTREAMHEADER    ash;
794     LPWAVEHDR           lpWaveHdrSrc;
795     DWORD               dwRet1, dwRet2;
796
797     if (!wim->hAcmStream) {
798         return waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrDst, dwParam2);
799     }
800     ash = (PACMSTREAMHEADER)lpWaveHdrDst->reserved;
801     dwRet1 = acmStreamUnprepareHeader(wim->hAcmStream, ash, 0L);
802
803     lpWaveHdrSrc = (LPWAVEHDR)((LPSTR)ash + sizeof(ACMSTREAMHEADER));
804     dwRet2 = waveInUnprepareHeader(wim->u.in.hInnerWave, lpWaveHdrSrc, sizeof(*lpWaveHdrSrc));
805
806     HeapFree(GetProcessHeap(), 0, ash);
807
808     lpWaveHdrDst->dwFlags &= ~WHDR_PREPARED;
809     return (dwRet1 == MMSYSERR_NOERROR) ? dwRet2 : dwRet1;
810 }
811
812 static  DWORD   widGetPosition(WAVEMAPDATA* wim, LPMMTIME lpTime, DWORD dwParam2)
813 {
814     DWORD       val = waveInGetPosition(wim->u.in.hInnerWave, lpTime, dwParam2);
815     if (lpTime->wType == TIME_BYTES)
816         lpTime->u.cb = MulDiv(lpTime->u.cb, wim->avgSpeedOuter, wim->avgSpeedInner);
817     /* other time types don't require conversion */
818     return val;
819 }
820
821 static  DWORD   widGetDevCaps(UINT wDevID, WAVEMAPDATA* wim, LPWAVEINCAPSA lpWaveCaps, DWORD dwParam2)
822 {
823     /* if opened low driver, forward message */
824     if (WAVEMAP_IsData(wim))
825         return waveInGetDevCapsA((UINT)wim->u.in.hInnerWave, lpWaveCaps, dwParam2);
826     /* otherwise, return caps of mapper itself */
827     if (wDevID == (UINT)-1 || wDevID == (UINT16)-1) {
828         lpWaveCaps->wMid = 0x00FF;
829         lpWaveCaps->wPid = 0x0001;
830         lpWaveCaps->vDriverVersion = 0x0001;
831         strcpy(lpWaveCaps->szPname, "Wine wave in mapper");
832         lpWaveCaps->dwFormats =
833             WAVE_FORMAT_4M08 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S16 |
834             WAVE_FORMAT_2M08 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S16 |
835             WAVE_FORMAT_1M08 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S16;
836         lpWaveCaps->wChannels = 2;
837         return MMSYSERR_NOERROR;
838     }
839     ERR("This shouldn't happen\n");
840     return MMSYSERR_ERROR;
841 }
842
843 static  DWORD   widStop(WAVEMAPDATA* wim)
844 {
845     return waveInStop(wim->u.in.hInnerWave);
846 }
847
848 static  DWORD   widStart(WAVEMAPDATA* wim)
849 {
850     return waveInStart(wim->u.in.hInnerWave);
851 }
852
853 static  DWORD   widReset(WAVEMAPDATA* wim)
854 {
855     return waveInReset(wim->u.in.hInnerWave);
856 }
857
858 static  DWORD   widMapperStatus(WAVEMAPDATA* wim, DWORD flags, LPVOID ptr)
859 {
860     UINT        id;
861     DWORD       ret = MMSYSERR_NOTSUPPORTED;
862
863     switch (flags) {
864     case WAVEIN_MAPPER_STATUS_DEVICE:
865         ret = waveInGetID(wim->u.in.hInnerWave, &id);
866         *(LPDWORD)ptr = id;
867         break;
868     case WAVEIN_MAPPER_STATUS_MAPPED:
869         FIXME("Unsupported yet flag=%ld\n", flags);
870         *(LPDWORD)ptr = 0; /* FIXME ?? */
871         break;
872     case WAVEIN_MAPPER_STATUS_FORMAT:
873         FIXME("Unsupported flag=%ld\n", flags);
874         /* ptr points to a WAVEFORMATEX struct  - before or after streaming ? */
875         *(LPDWORD)ptr = 0; /* FIXME ?? */
876         break;
877     default:
878         FIXME("Unsupported flag=%ld\n", flags);
879         *(LPDWORD)ptr = 0;
880         break;
881     }
882     return ret;
883 }
884
885 /**************************************************************************
886  *                              widMessage (MSACM.@)
887  */
888 DWORD WINAPI WAVEMAP_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
889                                 DWORD dwParam1, DWORD dwParam2)
890 {
891     TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
892           wDevID, wMsg, dwUser, dwParam1, dwParam2);
893
894     switch (wMsg) {
895     case DRVM_INIT:
896     case DRVM_EXIT:
897     case DRVM_ENABLE:
898     case DRVM_DISABLE:
899         /* FIXME: Pretend this is supported */
900         return 0;
901
902     case WIDM_OPEN:             return widOpen          ((LPDWORD)dwUser,     (LPWAVEOPENDESC)dwParam1, dwParam2);
903     case WIDM_CLOSE:            return widClose         ((WAVEMAPDATA*)dwUser);
904
905     case WIDM_ADDBUFFER:        return widAddBuffer     ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
906     case WIDM_PREPARE:          return widPrepare       ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
907     case WIDM_UNPREPARE:        return widUnprepare     ((WAVEMAPDATA*)dwUser, (LPWAVEHDR)dwParam1,     dwParam2);
908     case WIDM_GETDEVCAPS:       return widGetDevCaps    (wDevID, (WAVEMAPDATA*)dwUser, (LPWAVEINCAPSA)dwParam1, dwParam2);
909     case WIDM_GETNUMDEVS:       return 1;
910     case WIDM_GETPOS:           return widGetPosition   ((WAVEMAPDATA*)dwUser, (LPMMTIME)dwParam1,      dwParam2);
911     case WIDM_RESET:            return widReset         ((WAVEMAPDATA*)dwUser);
912     case WIDM_START:            return widStart         ((WAVEMAPDATA*)dwUser);
913     case WIDM_STOP:             return widStop          ((WAVEMAPDATA*)dwUser);
914     case WIDM_MAPPER_STATUS:    return widMapperStatus  ((WAVEMAPDATA*)dwUser, dwParam1, (LPVOID)dwParam2);
915     default:
916         FIXME("unknown message %u!\n", wMsg);
917     }
918     return MMSYSERR_NOTSUPPORTED;
919 }
920
921 /*======================================================================*
922  *                  Driver part                                         *
923  *======================================================================*/
924
925 static  struct WINE_WAVEMAP* oss = NULL;
926
927 /**************************************************************************
928  *                              WAVEMAP_drvOpen                 [internal]
929  */
930 static  DWORD   WAVEMAP_drvOpen(LPSTR str)
931 {
932     if (oss)
933         return 0;
934
935     /* I know, this is ugly, but who cares... */
936     oss = (struct WINE_WAVEMAP*)1;
937     return 1;
938 }
939
940 /**************************************************************************
941  *                              WAVEMAP_drvClose                [internal]
942  */
943 static  DWORD   WAVEMAP_drvClose(DWORD dwDevID)
944 {
945     if (oss) {
946         oss = NULL;
947         return 1;
948     }
949     return 0;
950 }
951
952 /**************************************************************************
953  *                              DriverProc (MSACM.@)
954  */
955 LONG CALLBACK   WAVEMAP_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
956                                    DWORD dwParam1, DWORD dwParam2)
957 {
958     TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
959           dwDevID, hDriv, wMsg, dwParam1, dwParam2);
960
961     switch(wMsg) {
962     case DRV_LOAD:              return 1;
963     case DRV_FREE:              return 1;
964     case DRV_OPEN:              return WAVEMAP_drvOpen((LPSTR)dwParam1);
965     case DRV_CLOSE:             return WAVEMAP_drvClose(dwDevID);
966     case DRV_ENABLE:            return 1;
967     case DRV_DISABLE:           return 1;
968     case DRV_QUERYCONFIGURE:    return 1;
969     case DRV_CONFIGURE:         MessageBoxA(0, "WAVEMAP MultiMedia Driver !", "Wave mapper Driver", MB_OK);     return 1;
970     case DRV_INSTALL:           return DRVCNF_RESTART;
971     case DRV_REMOVE:            return DRVCNF_RESTART;
972     default:
973         return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
974     }
975 }