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