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