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