1 /* -*- tab-width: 8; c-basic-offset: 4 -*- */
3 * Wine Wave mapper driver
5 * Copyright 1999,2001 Eric Pouech
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.
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.
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
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...
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(msacm
);
42 typedef struct tagWAVEMAPDATA
{
43 struct tagWAVEMAPDATA
* self
;
54 HACMSTREAM hAcmStream
;
55 /* needed data to filter callbacks. Only needed when hAcmStream is not 0 */
57 DWORD dwClientInstance
;
59 /* ratio to compute position from a PCM playback to any format */
64 static BOOL
WAVEMAP_IsData(WAVEMAPDATA
* wm
)
66 return (!IsBadReadPtr(wm
, sizeof(WAVEMAPDATA
)) && wm
->self
== wm
);
69 /*======================================================================*
71 *======================================================================*/
73 static void CALLBACK
wodCallback(HWAVEOUT hWave
, UINT uMsg
, DWORD dwInstance
,
74 DWORD dwParam1
, DWORD dwParam2
)
76 WAVEMAPDATA
* wom
= (WAVEMAPDATA
*)dwInstance
;
78 TRACE("(%p %u %ld %lx %lx);\n", hWave
, uMsg
, dwInstance
, dwParam1
, dwParam2
);
80 if (!WAVEMAP_IsData(wom
)) {
85 if (hWave
!= wom
->u
.out
.hInnerWave
&& uMsg
!= WOM_OPEN
)
86 ERR("Shouldn't happen (%p %p)\n", hWave
, wom
->u
.out
.hInnerWave
);
91 /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
94 if (wom
->hAcmStream
) {
95 LPWAVEHDR lpWaveHdrDst
= (LPWAVEHDR
)dwParam1
;
96 PACMSTREAMHEADER ash
= (PACMSTREAMHEADER
)((LPSTR
)lpWaveHdrDst
- sizeof(ACMSTREAMHEADER
));
97 LPWAVEHDR lpWaveHdrSrc
= (LPWAVEHDR
)ash
->dwUser
;
99 lpWaveHdrSrc
->dwFlags
&= ~WHDR_INQUEUE
;
100 lpWaveHdrSrc
->dwFlags
|= WHDR_DONE
;
101 dwParam1
= (DWORD
)lpWaveHdrSrc
;
105 ERR("Unknown msg %u\n", uMsg
);
108 DriverCallback(wom
->dwCallback
, HIWORD(wom
->dwFlags
), (HDRVR
)wom
->u
.out
.hOuterWave
,
109 uMsg
, wom
->dwClientInstance
, dwParam1
, dwParam2
);
112 /******************************************************************
117 static DWORD
wodOpenHelper(WAVEMAPDATA
* wom
, UINT idx
,
118 LPWAVEOPENDESC lpDesc
, LPWAVEFORMATEX lpwfx
,
123 /* destination is always PCM, so the formulas below apply */
124 lpwfx
->nBlockAlign
= (lpwfx
->nChannels
* lpwfx
->wBitsPerSample
) / 8;
125 lpwfx
->nAvgBytesPerSec
= lpwfx
->nSamplesPerSec
* lpwfx
->nBlockAlign
;
126 if (dwFlags
& WAVE_FORMAT_QUERY
) {
127 ret
= acmStreamOpen(NULL
, 0, lpDesc
->lpFormat
, lpwfx
, NULL
, 0L, 0L, ACM_STREAMOPENF_QUERY
);
129 ret
= acmStreamOpen(&wom
->hAcmStream
, 0, lpDesc
->lpFormat
, lpwfx
, NULL
, 0L, 0L, 0L);
131 if (ret
== MMSYSERR_NOERROR
) {
132 ret
= waveOutOpen(&wom
->u
.out
.hInnerWave
, idx
, lpwfx
, (DWORD
)wodCallback
,
133 (DWORD
)wom
, (dwFlags
& ~CALLBACK_TYPEMASK
) | CALLBACK_FUNCTION
);
134 if (ret
!= MMSYSERR_NOERROR
&& !(dwFlags
& WAVE_FORMAT_QUERY
)) {
135 acmStreamClose(wom
->hAcmStream
, 0);
142 static DWORD
wodOpen(LPDWORD lpdwUser
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
146 WAVEMAPDATA
* wom
= HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA
));
148 TRACE("(%p %p %08lx\n", lpdwUser
, lpDesc
, dwFlags
);
151 return MMSYSERR_NOMEM
;
153 ndhi
= waveOutGetNumDevs();
154 if (dwFlags
& WAVE_MAPPED
) {
155 if (lpDesc
->uMappedDeviceID
>= ndhi
) return MMSYSERR_INVALPARAM
;
156 ndlo
= lpDesc
->uMappedDeviceID
;
158 dwFlags
&= ~WAVE_MAPPED
;
163 wom
->dwCallback
= lpDesc
->dwCallback
;
164 wom
->dwFlags
= dwFlags
;
165 wom
->dwClientInstance
= lpDesc
->dwInstance
;
166 wom
->u
.out
.hOuterWave
= (HWAVEOUT
)lpDesc
->hWave
;
167 wom
->avgSpeedOuter
= wom
->avgSpeedInner
= lpDesc
->lpFormat
->nAvgBytesPerSec
;
169 for (i
= ndlo
; i
< ndhi
; i
++) {
170 /* if no ACM stuff is involved, no need to handle callbacks at this
171 * level, this will be done transparently
173 if (waveOutOpen(&wom
->u
.out
.hInnerWave
, i
, lpDesc
->lpFormat
, (DWORD
)wodCallback
,
174 (DWORD
)wom
, (dwFlags
& ~CALLBACK_TYPEMASK
) | CALLBACK_FUNCTION
| WAVE_FORMAT_DIRECT
) == MMSYSERR_NOERROR
) {
180 if ((dwFlags
& WAVE_FORMAT_DIRECT
) == 0) {
183 wfx
.wFormatTag
= WAVE_FORMAT_PCM
;
184 wfx
.cbSize
= 0; /* normally, this field is not used for PCM format, just in case */
185 /* try some ACM stuff */
187 #define TRY(sps,bps) wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
188 if (wodOpenHelper(wom, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) \
189 {wom->avgSpeedInner = wfx.nAvgBytesPerSec; goto found;}
191 for (i
= ndlo
; i
< ndhi
; i
++) {
192 /* first try with same stereo/mono option as source */
193 wfx
.nChannels
= lpDesc
->lpFormat
->nChannels
;
198 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
204 /* first try with same stereo/mono option as source */
205 wfx
.nChannels
= lpDesc
->lpFormat
->nChannels
;
210 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
219 HeapFree(GetProcessHeap(), 0, wom
);
220 return MMSYSERR_ALLOCATED
;
222 if (dwFlags
& WAVE_FORMAT_QUERY
) {
224 HeapFree(GetProcessHeap(), 0, wom
);
226 *lpdwUser
= (DWORD
)wom
;
228 return MMSYSERR_NOERROR
;
231 static DWORD
wodClose(WAVEMAPDATA
* wom
)
233 DWORD ret
= waveOutClose(wom
->u
.out
.hInnerWave
);
235 if (ret
== MMSYSERR_NOERROR
) {
236 if (wom
->hAcmStream
) {
237 ret
= acmStreamClose(wom
->hAcmStream
, 0);
239 if (ret
== MMSYSERR_NOERROR
) {
240 HeapFree(GetProcessHeap(), 0, wom
);
246 static DWORD
wodWrite(WAVEMAPDATA
* wom
, LPWAVEHDR lpWaveHdrSrc
, DWORD dwParam2
)
248 PACMSTREAMHEADER ash
;
249 LPWAVEHDR lpWaveHdrDst
;
251 if (!wom
->hAcmStream
) {
252 return waveOutWrite(wom
->u
.out
.hInnerWave
, lpWaveHdrSrc
, dwParam2
);
255 lpWaveHdrSrc
->dwFlags
|= WHDR_INQUEUE
;
256 ash
= (PACMSTREAMHEADER
)lpWaveHdrSrc
->reserved
;
257 /* acmStreamConvert will actually check that the new size is less than initial size */
258 ash
->cbSrcLength
= lpWaveHdrSrc
->dwBufferLength
;
259 if (acmStreamConvert(wom
->hAcmStream
, ash
, 0L) != MMSYSERR_NOERROR
)
260 return MMSYSERR_ERROR
;
262 lpWaveHdrDst
= (LPWAVEHDR
)((LPSTR
)ash
+ sizeof(ACMSTREAMHEADER
));
263 if (ash
->cbSrcLength
> ash
->cbSrcLengthUsed
)
264 FIXME("Not all src buffer has been written, expect bogus sound\n");
265 else if (ash
->cbSrcLength
< ash
->cbSrcLengthUsed
)
266 ERR("CoDec has read more data than it is allowed to\n");
268 if (ash
->cbDstLengthUsed
== 0)
270 /* something went wrong in decoding */
271 FIXME("Got 0 length\n");
272 return MMSYSERR_ERROR
;
274 lpWaveHdrDst
->dwBufferLength
= ash
->cbDstLengthUsed
;
275 return waveOutWrite(wom
->u
.out
.hInnerWave
, lpWaveHdrDst
, sizeof(*lpWaveHdrDst
));
278 static DWORD
wodPrepare(WAVEMAPDATA
* wom
, LPWAVEHDR lpWaveHdrSrc
, DWORD dwParam2
)
280 PACMSTREAMHEADER ash
;
283 LPWAVEHDR lpWaveHdrDst
;
285 if (!wom
->hAcmStream
) {
286 return waveOutPrepareHeader(wom
->u
.out
.hInnerWave
, lpWaveHdrSrc
, dwParam2
);
288 if (acmStreamSize(wom
->hAcmStream
, lpWaveHdrSrc
->dwBufferLength
, &size
, ACM_STREAMSIZEF_SOURCE
) != MMSYSERR_NOERROR
)
289 return MMSYSERR_ERROR
;
291 ash
= HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER
) + sizeof(WAVEHDR
) + size
);
293 return MMSYSERR_NOMEM
;
295 ash
->cbStruct
= sizeof(*ash
);
297 ash
->dwUser
= (DWORD
)lpWaveHdrSrc
;
298 ash
->pbSrc
= lpWaveHdrSrc
->lpData
;
299 ash
->cbSrcLength
= lpWaveHdrSrc
->dwBufferLength
;
300 /* ash->cbSrcLengthUsed */
301 ash
->dwSrcUser
= lpWaveHdrSrc
->dwUser
; /* FIXME ? */
302 ash
->pbDst
= (LPSTR
)ash
+ sizeof(ACMSTREAMHEADER
) + sizeof(WAVEHDR
);
303 ash
->cbDstLength
= size
;
304 /* ash->cbDstLengthUsed */
305 ash
->dwDstUser
= 0; /* FIXME ? */
306 dwRet
= acmStreamPrepareHeader(wom
->hAcmStream
, ash
, 0L);
307 if (dwRet
!= MMSYSERR_NOERROR
)
310 lpWaveHdrDst
= (LPWAVEHDR
)((LPSTR
)ash
+ sizeof(ACMSTREAMHEADER
));
311 lpWaveHdrDst
->lpData
= ash
->pbDst
;
312 lpWaveHdrDst
->dwBufferLength
= size
; /* conversion is not done yet */
313 lpWaveHdrDst
->dwFlags
= 0;
314 lpWaveHdrDst
->dwLoops
= 0;
315 dwRet
= waveOutPrepareHeader(wom
->u
.out
.hInnerWave
, lpWaveHdrDst
, sizeof(*lpWaveHdrDst
));
316 if (dwRet
!= MMSYSERR_NOERROR
)
319 lpWaveHdrSrc
->reserved
= (DWORD
)ash
;
320 lpWaveHdrSrc
->dwFlags
= WHDR_PREPARED
;
322 return MMSYSERR_NOERROR
;
324 TRACE("=> (%ld)\n", dwRet
);
325 HeapFree(GetProcessHeap(), 0, ash
);
329 static DWORD
wodUnprepare(WAVEMAPDATA
* wom
, LPWAVEHDR lpWaveHdrSrc
, DWORD dwParam2
)
331 PACMSTREAMHEADER ash
;
332 LPWAVEHDR lpWaveHdrDst
;
333 DWORD dwRet1
, dwRet2
;
335 if (!wom
->hAcmStream
) {
336 return waveOutUnprepareHeader(wom
->u
.out
.hInnerWave
, lpWaveHdrSrc
, dwParam2
);
338 ash
= (PACMSTREAMHEADER
)lpWaveHdrSrc
->reserved
;
339 dwRet1
= acmStreamUnprepareHeader(wom
->hAcmStream
, ash
, 0L);
341 lpWaveHdrDst
= (LPWAVEHDR
)((LPSTR
)ash
+ sizeof(ACMSTREAMHEADER
));
342 dwRet2
= waveOutUnprepareHeader(wom
->u
.out
.hInnerWave
, lpWaveHdrDst
, sizeof(*lpWaveHdrDst
));
344 HeapFree(GetProcessHeap(), 0, ash
);
346 lpWaveHdrSrc
->dwFlags
&= ~WHDR_PREPARED
;
347 return (dwRet1
== MMSYSERR_NOERROR
) ? dwRet2
: dwRet1
;
350 static DWORD
wodGetPosition(WAVEMAPDATA
* wom
, LPMMTIME lpTime
, DWORD dwParam2
)
352 DWORD val
= waveOutGetPosition(wom
->u
.out
.hInnerWave
, lpTime
, dwParam2
);
353 if (lpTime
->wType
== TIME_BYTES
)
354 val
= MulDiv(val
, wom
->avgSpeedOuter
, wom
->avgSpeedInner
);
355 /* other time types don't require conversion */
359 static DWORD
wodGetDevCaps(UINT wDevID
, WAVEMAPDATA
* wom
, LPWAVEOUTCAPSA lpWaveCaps
, DWORD dwParam2
)
361 /* if opened low driver, forward message */
362 if (WAVEMAP_IsData(wom
))
363 return waveOutGetDevCapsA((UINT
)wom
->u
.out
.hInnerWave
, lpWaveCaps
, dwParam2
);
364 /* otherwise, return caps of mapper itself */
365 if (wDevID
== (UINT
)-1 || wDevID
== (UINT16
)-1) {
366 lpWaveCaps
->wMid
= 0x00FF;
367 lpWaveCaps
->wPid
= 0x0001;
368 lpWaveCaps
->vDriverVersion
= 0x0100;
369 strcpy(lpWaveCaps
->szPname
, "Wine wave out mapper");
370 lpWaveCaps
->dwFormats
=
371 WAVE_FORMAT_4M08
| WAVE_FORMAT_4S08
| WAVE_FORMAT_4M16
| WAVE_FORMAT_4S16
|
372 WAVE_FORMAT_2M08
| WAVE_FORMAT_2S08
| WAVE_FORMAT_2M16
| WAVE_FORMAT_2S16
|
373 WAVE_FORMAT_1M08
| WAVE_FORMAT_1S08
| WAVE_FORMAT_1M16
| WAVE_FORMAT_1S16
;
374 lpWaveCaps
->wChannels
= 2;
375 lpWaveCaps
->dwSupport
= WAVECAPS_VOLUME
| WAVECAPS_LRVOLUME
;
377 return MMSYSERR_NOERROR
;
379 ERR("This shouldn't happen\n");
380 return MMSYSERR_ERROR
;
383 static DWORD
wodGetVolume(UINT wDevID
, WAVEMAPDATA
* wom
, LPDWORD lpVol
)
385 if (WAVEMAP_IsData(wom
))
386 return waveOutGetVolume((UINT
)wom
->u
.out
.hInnerWave
, lpVol
);
387 return MMSYSERR_NOERROR
;
390 static DWORD
wodSetVolume(UINT wDevID
, WAVEMAPDATA
* wom
, DWORD vol
)
392 if (WAVEMAP_IsData(wom
))
393 return waveOutSetVolume((UINT
)wom
->u
.out
.hInnerWave
, vol
);
394 return MMSYSERR_NOERROR
;
397 static DWORD
wodPause(WAVEMAPDATA
* wom
)
399 return waveOutPause(wom
->u
.out
.hInnerWave
);
402 static DWORD
wodRestart(WAVEMAPDATA
* wom
)
404 return waveOutRestart(wom
->u
.out
.hInnerWave
);
407 static DWORD
wodReset(WAVEMAPDATA
* wom
)
409 return waveOutReset(wom
->u
.out
.hInnerWave
);
412 static DWORD
wodBreakLoop(WAVEMAPDATA
* wom
)
414 return waveOutBreakLoop(wom
->u
.out
.hInnerWave
);
417 static DWORD
wodMapperStatus(WAVEMAPDATA
* wom
, DWORD flags
, LPVOID ptr
)
420 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
423 case WAVEOUT_MAPPER_STATUS_DEVICE
:
424 ret
= waveOutGetID(wom
->u
.out
.hInnerWave
, &id
);
427 case WAVEOUT_MAPPER_STATUS_MAPPED
:
428 FIXME("Unsupported flag=%ld\n", flags
);
429 *(LPDWORD
)ptr
= 0; /* FIXME ?? */
431 case WAVEOUT_MAPPER_STATUS_FORMAT
:
432 FIXME("Unsupported flag=%ld\n", flags
);
433 /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
437 FIXME("Unsupported flag=%ld\n", flags
);
444 /**************************************************************************
445 * wodMessage (MSACM.@)
447 DWORD WINAPI
WAVEMAP_wodMessage(UINT wDevID
, UINT wMsg
, DWORD dwUser
,
448 DWORD dwParam1
, DWORD dwParam2
)
450 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
451 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
458 /* FIXME: Pretend this is supported */
460 case WODM_OPEN
: return wodOpen ((LPDWORD
)dwUser
, (LPWAVEOPENDESC
)dwParam1
,dwParam2
);
461 case WODM_CLOSE
: return wodClose ((WAVEMAPDATA
*)dwUser
);
462 case WODM_WRITE
: return wodWrite ((WAVEMAPDATA
*)dwUser
, (LPWAVEHDR
)dwParam1
, dwParam2
);
463 case WODM_PAUSE
: return wodPause ((WAVEMAPDATA
*)dwUser
);
464 case WODM_GETPOS
: return wodGetPosition ((WAVEMAPDATA
*)dwUser
, (LPMMTIME
)dwParam1
, dwParam2
);
465 case WODM_BREAKLOOP
: return wodBreakLoop ((WAVEMAPDATA
*)dwUser
);
466 case WODM_PREPARE
: return wodPrepare ((WAVEMAPDATA
*)dwUser
, (LPWAVEHDR
)dwParam1
, dwParam2
);
467 case WODM_UNPREPARE
: return wodUnprepare ((WAVEMAPDATA
*)dwUser
, (LPWAVEHDR
)dwParam1
, dwParam2
);
468 case WODM_GETDEVCAPS
: return wodGetDevCaps (wDevID
, (WAVEMAPDATA
*)dwUser
, (LPWAVEOUTCAPSA
)dwParam1
,dwParam2
);
469 case WODM_GETNUMDEVS
: return 1;
470 case WODM_GETPITCH
: return MMSYSERR_NOTSUPPORTED
;
471 case WODM_SETPITCH
: return MMSYSERR_NOTSUPPORTED
;
472 case WODM_GETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
473 case WODM_SETPLAYBACKRATE
: return MMSYSERR_NOTSUPPORTED
;
474 case WODM_GETVOLUME
: return wodGetVolume (wDevID
, (WAVEMAPDATA
*)dwUser
, (LPDWORD
)dwParam1
);
475 case WODM_SETVOLUME
: return wodSetVolume (wDevID
, (WAVEMAPDATA
*)dwUser
, dwParam1
);
476 case WODM_RESTART
: return wodRestart ((WAVEMAPDATA
*)dwUser
);
477 case WODM_RESET
: return wodReset ((WAVEMAPDATA
*)dwUser
);
478 case WODM_MAPPER_STATUS
: return wodMapperStatus ((WAVEMAPDATA
*)dwUser
, dwParam1
, (LPVOID
)dwParam2
);
480 FIXME("unknown message %d!\n", wMsg
);
482 return MMSYSERR_NOTSUPPORTED
;
485 /*======================================================================*
487 *======================================================================*/
489 static void CALLBACK
widCallback(HWAVEIN hWave
, UINT uMsg
, DWORD dwInstance
,
490 DWORD dwParam1
, DWORD dwParam2
)
492 WAVEMAPDATA
* wim
= (WAVEMAPDATA
*)dwInstance
;
494 TRACE("(%p %u %ld %lx %lx);\n", hWave
, uMsg
, dwInstance
, dwParam1
, dwParam2
);
496 if (!WAVEMAP_IsData(wim
)) {
501 if (hWave
!= wim
->u
.in
.hInnerWave
&& uMsg
!= WIM_OPEN
)
502 ERR("Shouldn't happen (%p %p)\n", hWave
, wim
->u
.in
.hInnerWave
);
507 /* dwParam1 & dwParam2 are supposed to be 0, nothing to do */
510 if (wim
->hAcmStream
) {
511 LPWAVEHDR lpWaveHdrSrc
= (LPWAVEHDR
)dwParam1
;
512 PACMSTREAMHEADER ash
= (PACMSTREAMHEADER
)((LPSTR
)lpWaveHdrSrc
- sizeof(ACMSTREAMHEADER
));
513 LPWAVEHDR lpWaveHdrDst
= (LPWAVEHDR
)ash
->dwUser
;
515 /* convert data just gotten from waveIn into requested format */
516 if (acmStreamConvert(wim
->hAcmStream
, ash
, 0L) != MMSYSERR_NOERROR
) {
517 ERR("ACM conversion failed\n");
520 TRACE("Converted %ld bytes into %ld\n", ash
->cbSrcLengthUsed
, ash
->cbDstLengthUsed
);
522 /* and setup the wavehdr to return accordingly */
523 lpWaveHdrDst
->dwFlags
&= ~WHDR_INQUEUE
;
524 lpWaveHdrDst
->dwFlags
|= WHDR_DONE
;
525 lpWaveHdrDst
->dwBytesRecorded
= ash
->cbDstLengthUsed
;
526 dwParam1
= (DWORD
)lpWaveHdrDst
;
530 ERR("Unknown msg %u\n", uMsg
);
533 DriverCallback(wim
->dwCallback
, HIWORD(wim
->dwFlags
), (HDRVR
)wim
->u
.in
.hOuterWave
,
534 uMsg
, wim
->dwClientInstance
, dwParam1
, dwParam2
);
537 static DWORD
widOpenHelper(WAVEMAPDATA
* wim
, UINT idx
,
538 LPWAVEOPENDESC lpDesc
, LPWAVEFORMATEX lpwfx
,
543 /* source is always PCM, so the formulas below apply */
544 lpwfx
->nBlockAlign
= (lpwfx
->nChannels
* lpwfx
->wBitsPerSample
) / 8;
545 lpwfx
->nAvgBytesPerSec
= lpwfx
->nSamplesPerSec
* lpwfx
->nBlockAlign
;
546 if (dwFlags
& WAVE_FORMAT_QUERY
) {
547 ret
= acmStreamOpen(NULL
, 0, lpwfx
, lpDesc
->lpFormat
, NULL
, 0L, 0L, ACM_STREAMOPENF_QUERY
);
549 ret
= acmStreamOpen(&wim
->hAcmStream
, 0, lpwfx
, lpDesc
->lpFormat
, NULL
, 0L, 0L, 0L);
551 if (ret
== MMSYSERR_NOERROR
) {
552 ret
= waveInOpen(&wim
->u
.in
.hInnerWave
, idx
, lpwfx
, (DWORD
)widCallback
,
553 (DWORD
)wim
, (dwFlags
& ~CALLBACK_TYPEMASK
) | CALLBACK_FUNCTION
);
554 if (ret
!= MMSYSERR_NOERROR
&& !(dwFlags
& WAVE_FORMAT_QUERY
)) {
555 acmStreamClose(wim
->hAcmStream
, 0);
562 static DWORD
widOpen(LPDWORD lpdwUser
, LPWAVEOPENDESC lpDesc
, DWORD dwFlags
)
566 WAVEMAPDATA
* wim
= HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEMAPDATA
));
568 TRACE("(%p %p %08lx)\n", lpdwUser
, lpDesc
, dwFlags
);
571 return MMSYSERR_NOMEM
;
574 wim
->dwCallback
= lpDesc
->dwCallback
;
575 wim
->dwFlags
= dwFlags
;
576 wim
->dwClientInstance
= lpDesc
->dwInstance
;
577 wim
->u
.in
.hOuterWave
= (HWAVEIN
)lpDesc
->hWave
;
579 ndhi
= waveOutGetNumDevs();
580 if (dwFlags
& WAVE_MAPPED
) {
581 if (lpDesc
->uMappedDeviceID
>= ndhi
) return MMSYSERR_INVALPARAM
;
582 ndlo
= lpDesc
->uMappedDeviceID
;
584 dwFlags
&= ~WAVE_MAPPED
;
589 wim
->avgSpeedOuter
= wim
->avgSpeedInner
= lpDesc
->lpFormat
->nAvgBytesPerSec
;
591 for (i
= ndlo
; i
< ndhi
; i
++) {
592 if (waveInOpen(&wim
->u
.in
.hInnerWave
, i
, lpDesc
->lpFormat
, (DWORD
)widCallback
,
593 (DWORD
)wim
, (dwFlags
& ~CALLBACK_TYPEMASK
) | CALLBACK_FUNCTION
| WAVE_FORMAT_DIRECT
) == MMSYSERR_NOERROR
) {
599 if ((dwFlags
& WAVE_FORMAT_DIRECT
) == 0)
603 wfx
.wFormatTag
= WAVE_FORMAT_PCM
;
604 wfx
.cbSize
= 0; /* normally, this field is not used for PCM format, just in case */
605 /* try some ACM stuff */
607 #define TRY(sps,bps) wfx.nSamplesPerSec = (sps); wfx.wBitsPerSample = (bps); \
608 if (widOpenHelper(wim, i, lpDesc, &wfx, dwFlags | WAVE_FORMAT_DIRECT) == MMSYSERR_NOERROR) \
609 {wim->avgSpeedInner = wfx.nAvgBytesPerSec; goto found;}
611 for (i
= ndlo
; i
< ndhi
; i
++) {
612 /* first try with same stereo/mono option as source */
613 wfx
.nChannels
= lpDesc
->lpFormat
->nChannels
;
618 /* 2^3 => 1, 1^3 => 2, so if stereo, try mono (and the other way around) */
627 HeapFree(GetProcessHeap(), 0, wim
);
628 return MMSYSERR_ALLOCATED
;
630 if (dwFlags
& WAVE_FORMAT_QUERY
) {
632 HeapFree(GetProcessHeap(), 0, wim
);
634 *lpdwUser
= (DWORD
)wim
;
636 TRACE("Ok (stream=%08lx)\n", (DWORD
)wim
->hAcmStream
);
637 return MMSYSERR_NOERROR
;
640 static DWORD
widClose(WAVEMAPDATA
* wim
)
642 DWORD ret
= waveInClose(wim
->u
.in
.hInnerWave
);
644 if (ret
== MMSYSERR_NOERROR
) {
645 if (wim
->hAcmStream
) {
646 ret
= acmStreamClose(wim
->hAcmStream
, 0);
648 if (ret
== MMSYSERR_NOERROR
) {
649 HeapFree(GetProcessHeap(), 0, wim
);
655 static DWORD
widAddBuffer(WAVEMAPDATA
* wim
, LPWAVEHDR lpWaveHdrDst
, DWORD dwParam2
)
657 PACMSTREAMHEADER ash
;
658 LPWAVEHDR lpWaveHdrSrc
;
660 if (!wim
->hAcmStream
) {
661 return waveInAddBuffer(wim
->u
.in
.hInnerWave
, lpWaveHdrDst
, dwParam2
);
664 lpWaveHdrDst
->dwFlags
|= WHDR_INQUEUE
;
665 ash
= (PACMSTREAMHEADER
)lpWaveHdrDst
->reserved
;
667 lpWaveHdrSrc
= (LPWAVEHDR
)((LPSTR
)ash
+ sizeof(ACMSTREAMHEADER
));
668 return waveInAddBuffer(wim
->u
.in
.hInnerWave
, lpWaveHdrSrc
, sizeof(*lpWaveHdrSrc
));
671 static DWORD
widPrepare(WAVEMAPDATA
* wim
, LPWAVEHDR lpWaveHdrDst
, DWORD dwParam2
)
673 PACMSTREAMHEADER ash
;
676 LPWAVEHDR lpWaveHdrSrc
;
678 if (!wim
->hAcmStream
) {
679 return waveInPrepareHeader(wim
->u
.in
.hInnerWave
, lpWaveHdrDst
, dwParam2
);
681 if (acmStreamSize(wim
->hAcmStream
, lpWaveHdrDst
->dwBufferLength
, &size
,
682 ACM_STREAMSIZEF_DESTINATION
) != MMSYSERR_NOERROR
)
683 return MMSYSERR_ERROR
;
685 ash
= HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER
) + sizeof(WAVEHDR
) + size
);
687 return MMSYSERR_NOMEM
;
689 ash
->cbStruct
= sizeof(*ash
);
691 ash
->dwUser
= (DWORD
)lpWaveHdrDst
;
692 ash
->pbSrc
= (LPSTR
)ash
+ sizeof(ACMSTREAMHEADER
) + sizeof(WAVEHDR
);
693 ash
->cbSrcLength
= size
;
694 /* ash->cbSrcLengthUsed */
695 ash
->dwSrcUser
= 0L; /* FIXME ? */
696 ash
->pbDst
= lpWaveHdrDst
->lpData
;
697 ash
->cbDstLength
= lpWaveHdrDst
->dwBufferLength
;
698 /* ash->cbDstLengthUsed */
699 ash
->dwDstUser
= lpWaveHdrDst
->dwUser
; /* FIXME ? */
700 dwRet
= acmStreamPrepareHeader(wim
->hAcmStream
, ash
, 0L);
701 if (dwRet
!= MMSYSERR_NOERROR
)
704 lpWaveHdrSrc
= (LPWAVEHDR
)((LPSTR
)ash
+ sizeof(ACMSTREAMHEADER
));
705 lpWaveHdrSrc
->lpData
= ash
->pbSrc
;
706 lpWaveHdrSrc
->dwBufferLength
= size
; /* conversion is not done yet */
707 lpWaveHdrSrc
->dwFlags
= 0;
708 lpWaveHdrSrc
->dwLoops
= 0;
709 dwRet
= waveInPrepareHeader(wim
->u
.in
.hInnerWave
, lpWaveHdrSrc
, sizeof(*lpWaveHdrSrc
));
710 if (dwRet
!= MMSYSERR_NOERROR
)
713 lpWaveHdrDst
->reserved
= (DWORD
)ash
;
714 lpWaveHdrDst
->dwFlags
= WHDR_PREPARED
;
716 return MMSYSERR_NOERROR
;
718 TRACE("=> (%ld)\n", dwRet
);
719 HeapFree(GetProcessHeap(), 0, ash
);
723 static DWORD
widUnprepare(WAVEMAPDATA
* wim
, LPWAVEHDR lpWaveHdrDst
, DWORD dwParam2
)
725 PACMSTREAMHEADER ash
;
726 LPWAVEHDR lpWaveHdrSrc
;
727 DWORD dwRet1
, dwRet2
;
729 if (!wim
->hAcmStream
) {
730 return waveInUnprepareHeader(wim
->u
.in
.hInnerWave
, lpWaveHdrDst
, dwParam2
);
732 ash
= (PACMSTREAMHEADER
)lpWaveHdrDst
->reserved
;
733 dwRet1
= acmStreamUnprepareHeader(wim
->hAcmStream
, ash
, 0L);
735 lpWaveHdrSrc
= (LPWAVEHDR
)((LPSTR
)ash
+ sizeof(ACMSTREAMHEADER
));
736 dwRet2
= waveInUnprepareHeader(wim
->u
.in
.hInnerWave
, lpWaveHdrSrc
, sizeof(*lpWaveHdrSrc
));
738 HeapFree(GetProcessHeap(), 0, ash
);
740 lpWaveHdrDst
->dwFlags
&= ~WHDR_PREPARED
;
741 return (dwRet1
== MMSYSERR_NOERROR
) ? dwRet2
: dwRet1
;
744 static DWORD
widGetPosition(WAVEMAPDATA
* wim
, LPMMTIME lpTime
, DWORD dwParam2
)
746 DWORD val
= waveInGetPosition(wim
->u
.in
.hInnerWave
, lpTime
, dwParam2
);
747 if (lpTime
->wType
== TIME_BYTES
)
748 val
= MulDiv(val
, wim
->avgSpeedOuter
, wim
->avgSpeedInner
);
749 /* other time types don't require conversion */
753 static DWORD
widGetDevCaps(UINT wDevID
, WAVEMAPDATA
* wim
, LPWAVEINCAPSA lpWaveCaps
, DWORD dwParam2
)
755 /* if opened low driver, forward message */
756 if (WAVEMAP_IsData(wim
))
757 return waveInGetDevCapsA((UINT
)wim
->u
.in
.hInnerWave
, lpWaveCaps
, dwParam2
);
758 /* otherwise, return caps of mapper itself */
759 if (wDevID
== (UINT
)-1 || wDevID
== (UINT16
)-1) {
760 lpWaveCaps
->wMid
= 0x00FF;
761 lpWaveCaps
->wPid
= 0x0001;
762 lpWaveCaps
->vDriverVersion
= 0x0001;
763 strcpy(lpWaveCaps
->szPname
, "Wine wave in mapper");
764 lpWaveCaps
->dwFormats
=
765 WAVE_FORMAT_4M08
| WAVE_FORMAT_4S08
| WAVE_FORMAT_4M16
| WAVE_FORMAT_4S16
|
766 WAVE_FORMAT_2M08
| WAVE_FORMAT_2S08
| WAVE_FORMAT_2M16
| WAVE_FORMAT_2S16
|
767 WAVE_FORMAT_1M08
| WAVE_FORMAT_1S08
| WAVE_FORMAT_1M16
| WAVE_FORMAT_1S16
;
768 lpWaveCaps
->wChannels
= 2;
769 return MMSYSERR_NOERROR
;
771 ERR("This shouldn't happen\n");
772 return MMSYSERR_ERROR
;
775 static DWORD
widStop(WAVEMAPDATA
* wim
)
777 return waveInStop(wim
->u
.in
.hInnerWave
);
780 static DWORD
widStart(WAVEMAPDATA
* wim
)
782 return waveInStart(wim
->u
.in
.hInnerWave
);
785 static DWORD
widReset(WAVEMAPDATA
* wim
)
787 return waveInReset(wim
->u
.in
.hInnerWave
);
790 static DWORD
widMapperStatus(WAVEMAPDATA
* wim
, DWORD flags
, LPVOID ptr
)
793 DWORD ret
= MMSYSERR_NOTSUPPORTED
;
796 case WAVEIN_MAPPER_STATUS_DEVICE
:
797 ret
= waveInGetID(wim
->u
.in
.hInnerWave
, &id
);
800 case WAVEIN_MAPPER_STATUS_MAPPED
:
801 FIXME("Unsupported yet flag=%ld\n", flags
);
802 *(LPDWORD
)ptr
= 0; /* FIXME ?? */
804 case WAVEIN_MAPPER_STATUS_FORMAT
:
805 FIXME("Unsupported flag=%ld\n", flags
);
806 /* ptr points to a WAVEFORMATEX struct - before or after streaming ? */
807 *(LPDWORD
)ptr
= 0; /* FIXME ?? */
810 FIXME("Unsupported flag=%ld\n", flags
);
817 /**************************************************************************
818 * widMessage (MSACM.@)
820 DWORD WINAPI
WAVEMAP_widMessage(WORD wDevID
, WORD wMsg
, DWORD dwUser
,
821 DWORD dwParam1
, DWORD dwParam2
)
823 TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
824 wDevID
, wMsg
, dwUser
, dwParam1
, dwParam2
);
831 /* FIXME: Pretend this is supported */
834 case WIDM_OPEN
: return widOpen ((LPDWORD
)dwUser
, (LPWAVEOPENDESC
)dwParam1
, dwParam2
);
835 case WIDM_CLOSE
: return widClose ((WAVEMAPDATA
*)dwUser
);
837 case WIDM_ADDBUFFER
: return widAddBuffer ((WAVEMAPDATA
*)dwUser
, (LPWAVEHDR
)dwParam1
, dwParam2
);
838 case WIDM_PREPARE
: return widPrepare ((WAVEMAPDATA
*)dwUser
, (LPWAVEHDR
)dwParam1
, dwParam2
);
839 case WIDM_UNPREPARE
: return widUnprepare ((WAVEMAPDATA
*)dwUser
, (LPWAVEHDR
)dwParam1
, dwParam2
);
840 case WIDM_GETDEVCAPS
: return widGetDevCaps (wDevID
, (WAVEMAPDATA
*)dwUser
, (LPWAVEINCAPSA
)dwParam1
, dwParam2
);
841 case WIDM_GETNUMDEVS
: return 1;
842 case WIDM_GETPOS
: return widGetPosition ((WAVEMAPDATA
*)dwUser
, (LPMMTIME
)dwParam1
, dwParam2
);
843 case WIDM_RESET
: return widReset ((WAVEMAPDATA
*)dwUser
);
844 case WIDM_START
: return widStart ((WAVEMAPDATA
*)dwUser
);
845 case WIDM_STOP
: return widStop ((WAVEMAPDATA
*)dwUser
);
846 case WIDM_MAPPER_STATUS
: return widMapperStatus ((WAVEMAPDATA
*)dwUser
, dwParam1
, (LPVOID
)dwParam2
);
848 FIXME("unknown message %u!\n", wMsg
);
850 return MMSYSERR_NOTSUPPORTED
;
853 /*======================================================================*
855 *======================================================================*/
857 static struct WINE_WAVEMAP
* oss
= NULL
;
859 /**************************************************************************
860 * WAVEMAP_drvOpen [internal]
862 static DWORD
WAVEMAP_drvOpen(LPSTR str
)
867 /* I know, this is ugly, but who cares... */
868 oss
= (struct WINE_WAVEMAP
*)1;
872 /**************************************************************************
873 * WAVEMAP_drvClose [internal]
875 static DWORD
WAVEMAP_drvClose(DWORD dwDevID
)
884 /**************************************************************************
885 * DriverProc (MSACM.@)
887 LONG CALLBACK
WAVEMAP_DriverProc(DWORD dwDevID
, HDRVR hDriv
, DWORD wMsg
,
888 DWORD dwParam1
, DWORD dwParam2
)
890 TRACE("(%08lX, %p, %08lX, %08lX, %08lX)\n",
891 dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);
894 case DRV_LOAD
: return 1;
895 case DRV_FREE
: return 1;
896 case DRV_OPEN
: return WAVEMAP_drvOpen((LPSTR
)dwParam1
);
897 case DRV_CLOSE
: return WAVEMAP_drvClose(dwDevID
);
898 case DRV_ENABLE
: return 1;
899 case DRV_DISABLE
: return 1;
900 case DRV_QUERYCONFIGURE
: return 1;
901 case DRV_CONFIGURE
: MessageBoxA(0, "WAVEMAP MultiMedia Driver !", "Wave mapper Driver", MB_OK
); return 1;
902 case DRV_INSTALL
: return DRVCNF_RESTART
;
903 case DRV_REMOVE
: return DRVCNF_RESTART
;
905 return DefDriverProc(dwDevID
, hDriv
, wMsg
, dwParam1
, dwParam2
);