2 * Copyright 1993 Martin Ayotte
3 * 1998-2002 Eric Pouech
4 * 2011 Andrew Eikum for CodeWeavers
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
43 #include "mmdeviceapi.h"
44 #include "audioclient.h"
45 #include "audiopolicy.h"
47 #include "wine/debug.h"
49 /* TODO: Remove after dsound has been rewritten for mmdevapi */
52 #define DS_HW_ACCEL_FULL 0
54 WINE_DEFAULT_DEBUG_CHANNEL(winmm
);
56 /* HWAVE (and HMIXER) format:
58 * XXXX... 1FDD DDDD IIII IIII
59 * X = unused (must be 0)
60 * 1 = the bit is set to 1, to avoid all-zero HWAVEs
61 * F = flow direction (0 = IN, 1 = OUT)
62 * D = index into g_out_mmdevices
63 * I = index in the mmdevice's devices array
65 * Two reasons that we don't just use pointers:
66 * - HWAVEs must fit into 16 bits for compatibility with old applications.
67 * - We must be able to identify bad devices without crashing.
70 #define MAX_DEVICES 256
72 typedef struct _WINMM_CBInfo
{
79 struct _WINMM_MMDevice
;
80 typedef struct _WINMM_MMDevice WINMM_MMDevice
;
82 typedef struct _WINMM_Device
{
91 IAudioRenderClient
*render
;
93 IAudioStreamVolume
*volume
;
95 HACMSTREAM acm_handle
;
96 ACMSTREAMHEADER acm_hdr
;
99 WAVEHDR
*first
, *last
, *playing
, *loop_start
;
103 UINT32 bytes_per_frame
, samples_per_sec
, ofs_bytes
, played_frames
;
105 /* stored in frames of sample rate, *not* AC::GetFrequency */
106 UINT64 last_clock_pos
;
109 CRITICAL_SECTION lock
;
111 WINMM_MMDevice
*parent
;
114 struct _WINMM_MMDevice
{
115 WAVEOUTCAPSW out_caps
; /* must not be modified outside of WINMM_InitMMDevices*/
116 WAVEINCAPSW in_caps
; /* must not be modified outside of WINMM_InitMMDevices*/
121 CRITICAL_SECTION lock
;
123 WINMM_Device
*devices
[MAX_DEVICES
];
126 static WINMM_MMDevice
*g_out_mmdevices
;
127 static UINT g_outmmdevices_count
;
129 static WINMM_MMDevice
*g_in_mmdevices
;
130 static UINT g_inmmdevices_count
;
132 static IMMDeviceEnumerator
*g_devenum
;
134 static CRITICAL_SECTION g_devthread_lock
;
135 static HANDLE g_devices_thread
;
136 static HWND g_devices_hwnd
;
138 static UINT g_devhandle_count
;
139 static HANDLE
*g_device_handles
;
140 static WINMM_Device
**g_handle_devices
;
142 typedef struct _WINMM_OpenInfo
{
145 WAVEFORMATEX
*format
;
151 static LRESULT
WOD_Open(WINMM_OpenInfo
*info
);
152 static LRESULT
WOD_Close(HWAVEOUT hwave
);
154 BOOL
WINMM_InitWaveform(void)
156 InitializeCriticalSection(&g_devthread_lock
);
160 static inline HWAVE
WINMM_MakeHWAVE(UINT mmdevice
, BOOL is_out
, UINT device
)
162 return ULongToHandle((1 << 15) | ((!!is_out
) << 14) |
163 (mmdevice
<< 8) | device
);
166 static inline void WINMM_DecomposeHWAVE(HWAVE hwave
, UINT
*mmdevice_index
,
167 BOOL
*is_out
, UINT
*device_index
, UINT
*junk
)
169 ULONG32 l
= HandleToULong(hwave
);
170 *device_index
= l
& 0xFF;
171 *mmdevice_index
= (l
>> 8) & 0x3F;
172 *is_out
= (l
>> 14) & 0x1;
176 static void WINMM_InitDevice(WINMM_Device
*device
,
177 WINMM_MMDevice
*parent
, HWAVE hwave
)
179 InitializeCriticalSection(&device
->lock
);
180 device
->handle
= hwave
;
181 device
->parent
= parent
;
184 /* finds the first unused Device, marks it as "open", and returns
185 * a pointer to the device
187 * IMPORTANT: it is the caller's responsibility to release the device's lock
190 static WINMM_Device
*WINMM_FindUnusedDevice(BOOL is_out
, UINT mmdevice_index
)
192 WINMM_MMDevice
*mmdevice
;
196 mmdevice
= &g_out_mmdevices
[mmdevice_index
];
200 EnterCriticalSection(&mmdevice
->lock
);
201 for(i
= 0; i
< MAX_DEVICES
; ++i
){
202 WINMM_Device
*device
= mmdevice
->devices
[i
];
205 device
= mmdevice
->devices
[i
] = HeapAlloc(GetProcessHeap(),
206 HEAP_ZERO_MEMORY
, sizeof(WINMM_Device
));
208 LeaveCriticalSection(&mmdevice
->lock
);
212 WINMM_InitDevice(device
, mmdevice
,
213 WINMM_MakeHWAVE(mmdevice_index
, is_out
, i
));
214 EnterCriticalSection(&device
->lock
);
216 EnterCriticalSection(&device
->lock
);
219 LeaveCriticalSection(&mmdevice
->lock
);
221 TRACE("Found free device: mmdevice: %u, device id: %u\n",
226 LeaveCriticalSection(&device
->lock
);
229 LeaveCriticalSection(&mmdevice
->lock
);
231 TRACE("All devices in use: mmdevice: %u\n", mmdevice_index
);
236 static inline BOOL
WINMM_ValidateAndLock(WINMM_Device
*device
)
241 EnterCriticalSection(&device
->lock
);
244 LeaveCriticalSection(&device
->lock
);
251 static WINMM_Device
*WINMM_GetDeviceFromHWAVE(HWAVE hwave
)
253 WINMM_MMDevice
*mmdevice
;
254 WINMM_Device
*device
;
255 UINT mmdevice_index
, device_index
, junk
;
258 WINMM_DecomposeHWAVE(hwave
, &mmdevice_index
, &is_out
, &device_index
, &junk
);
263 if(mmdevice_index
>= (is_out
? g_outmmdevices_count
: g_inmmdevices_count
))
267 mmdevice
= &g_out_mmdevices
[mmdevice_index
];
269 mmdevice
= &g_in_mmdevices
[mmdevice_index
];
271 EnterCriticalSection(&mmdevice
->lock
);
273 device
= mmdevice
->devices
[device_index
];
275 LeaveCriticalSection(&mmdevice
->lock
);
280 /* Note: NotifyClient should never be called while holding the device lock
281 * since the client may call wave* functions from within the callback. */
282 static DWORD
WINMM_NotifyClient(WINMM_CBInfo
*info
, WORD msg
, DWORD_PTR param1
,
285 TRACE("(%p, %u, %lx, %lx)\n", info
->hwave
, msg
, param1
, param2
);
287 if(info
->flags
& DCB_NULL
)
288 return MMSYSERR_NOERROR
;
290 if(!DriverCallback(info
->callback
, info
->flags
, (HDRVR
)info
->hwave
,
291 msg
, info
->user
, param1
, param2
))
292 return MMSYSERR_ERROR
;
294 return MMSYSERR_NOERROR
;
297 static HRESULT
WINMM_GetFriendlyName(IMMDevice
*device
, WCHAR
*out
,
304 hr
= IMMDevice_OpenPropertyStore(device
, STGM_READ
, &ps
);
308 PropVariantInit(&var
);
310 hr
= IPropertyStore_GetValue(ps
,
311 (PROPERTYKEY
*)&DEVPKEY_Device_FriendlyName
, &var
);
313 IPropertyStore_Release(ps
);
317 lstrcpynW(out
, var
.u
.pwszVal
, outlen
);
319 PropVariantClear(&var
);
321 IPropertyStore_Release(ps
);
326 static HRESULT
WINMM_TestFormat(IAudioClient
*client
, DWORD rate
, DWORD depth
,
329 WAVEFORMATEX fmt
, *junk
;
332 fmt
.wFormatTag
= WAVE_FORMAT_PCM
;
333 fmt
.nChannels
= channels
;
334 fmt
.nSamplesPerSec
= rate
;
335 fmt
.wBitsPerSample
= depth
;
336 fmt
.nBlockAlign
= (channels
* depth
) / 8;
337 fmt
.nAvgBytesPerSec
= rate
* fmt
.nBlockAlign
;
340 hr
= IAudioClient_IsFormatSupported(client
, AUDCLNT_SHAREMODE_SHARED
,
348 static struct _TestFormat
{
353 } formats_to_test
[] = {
354 { WAVE_FORMAT_1M08
, 11025, 8, 1 },
355 { WAVE_FORMAT_1M16
, 11025, 16, 1 },
356 { WAVE_FORMAT_1S08
, 11025, 8, 2 },
357 { WAVE_FORMAT_1S16
, 11025, 16, 2 },
358 { WAVE_FORMAT_2M08
, 22050, 8, 1 },
359 { WAVE_FORMAT_2M16
, 22050, 16, 1 },
360 { WAVE_FORMAT_2S08
, 22050, 8, 2 },
361 { WAVE_FORMAT_2S16
, 22050, 16, 2 },
362 { WAVE_FORMAT_4M08
, 44100, 8, 1 },
363 { WAVE_FORMAT_4M16
, 44100, 16, 1 },
364 { WAVE_FORMAT_4S08
, 44100, 8, 2 },
365 { WAVE_FORMAT_4S16
, 44100, 16, 2 },
366 { WAVE_FORMAT_48M08
, 48000, 8, 1 },
367 { WAVE_FORMAT_48M16
, 48000, 16, 1 },
368 { WAVE_FORMAT_48S08
, 48000, 8, 2 },
369 { WAVE_FORMAT_48S16
, 48000, 16, 2 },
370 { WAVE_FORMAT_96M08
, 96000, 8, 1 },
371 { WAVE_FORMAT_96M16
, 96000, 16, 1 },
372 { WAVE_FORMAT_96S08
, 96000, 8, 2 },
373 { WAVE_FORMAT_96S16
, 96000, 16, 2 },
377 static DWORD
WINMM_GetSupportedFormats(IMMDevice
*device
)
381 struct _TestFormat
*fmt
;
382 IAudioClient
*client
;
384 hr
= IMMDevice_Activate(device
, &IID_IAudioClient
,
385 CLSCTX_INPROC_SERVER
, NULL
, (void**)&client
);
389 for(fmt
= formats_to_test
; fmt
->flag
; ++fmt
){
390 hr
= WINMM_TestFormat(client
, fmt
->rate
, fmt
->depth
, fmt
->channels
);
395 IAudioClient_Release(client
);
400 static HRESULT
WINMM_InitMMDevice(EDataFlow flow
, IMMDevice
*device
,
401 WINMM_MMDevice
*dev
, UINT index
)
406 dev
->out_caps
.wMid
= 0xFF;
407 dev
->out_caps
.wPid
= 0xFF;
408 dev
->out_caps
.vDriverVersion
= 0x00010001;
409 dev
->out_caps
.dwFormats
= WINMM_GetSupportedFormats(device
);
410 dev
->out_caps
.wReserved1
= 0;
411 dev
->out_caps
.dwSupport
= WAVECAPS_LRVOLUME
| WAVECAPS_VOLUME
|
412 WAVECAPS_SAMPLEACCURATE
;
413 dev
->out_caps
.wChannels
= 2;
414 dev
->out_caps
.szPname
[0] = '\0';
416 hr
= WINMM_GetFriendlyName(device
, dev
->out_caps
.szPname
,
417 sizeof(dev
->out_caps
.szPname
) /
418 sizeof(*dev
->out_caps
.szPname
));
422 dev
->in_caps
.wMid
= 0xFF;
423 dev
->in_caps
.wPid
= 0xFF;
424 dev
->in_caps
.vDriverVersion
= 0x00010001;
425 dev
->in_caps
.dwFormats
= WINMM_GetSupportedFormats(device
);
426 dev
->in_caps
.wReserved1
= 0;
427 dev
->in_caps
.wChannels
= 2;
428 dev
->in_caps
.szPname
[0] = '\0';
430 hr
= WINMM_GetFriendlyName(device
, dev
->in_caps
.szPname
,
431 sizeof(dev
->in_caps
.szPname
) /
432 sizeof(*dev
->in_caps
.szPname
));
437 hr
= IMMDevice_GetId(device
, &dev
->dev_id
);
441 CoCreateGuid(&dev
->session
);
443 InitializeCriticalSection(&dev
->lock
);
448 static HRESULT
WINMM_EnumDevices(WINMM_MMDevice
**devices
, UINT
*devcount
,
451 IMMDeviceCollection
*devcoll
;
454 hr
= IMMDeviceEnumerator_EnumAudioEndpoints(g_devenum
, flow
,
455 DEVICE_STATE_ACTIVE
, &devcoll
);
459 hr
= IMMDeviceCollection_GetCount(devcoll
, devcount
);
461 IMMDeviceCollection_Release(devcoll
);
467 IMMDevice
*def_dev
= NULL
;
469 *devices
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
,
470 sizeof(WINMM_MMDevice
) * (*devcount
));
472 IMMDeviceCollection_Release(devcoll
);
473 return E_OUTOFMEMORY
;
478 /* make sure that device 0 is the default device */
479 hr
= IMMDeviceEnumerator_GetDefaultAudioEndpoint(g_devenum
,
480 flow
, eConsole
, &def_dev
);
482 WINMM_InitMMDevice(flow
, def_dev
, &(*devices
)[0], 0);
486 for(n
= 0; n
< *devcount
; ++n
){
489 hr
= IMMDeviceCollection_Item(devcoll
, n
, &device
);
491 if(device
!= def_dev
){
492 WINMM_InitMMDevice(flow
, device
, &(*devices
)[count
], count
);
496 IMMDevice_Release(device
);
501 IMMDevice_Release(def_dev
);
506 IMMDeviceCollection_Release(devcoll
);
511 static HRESULT
WINMM_InitMMDevices(void)
515 hr
= CoCreateInstance(&CLSID_MMDeviceEnumerator
, NULL
,
516 CLSCTX_INPROC_SERVER
, &IID_IMMDeviceEnumerator
, (void**)&g_devenum
);
520 hr
= WINMM_EnumDevices(&g_out_mmdevices
, &g_outmmdevices_count
, eRender
);
522 g_outmmdevices_count
= 0;
523 g_inmmdevices_count
= 0;
527 hr
= WINMM_EnumDevices(&g_in_mmdevices
, &g_inmmdevices_count
, eCapture
);
529 g_inmmdevices_count
= 0;
536 static UINT
WAVE_Open(HANDLE
* lphndl
, UINT uDeviceID
, UINT uType
,
537 LPCWAVEFORMATEX lpFormat
, DWORD_PTR dwCallback
,
538 DWORD_PTR dwInstance
, DWORD dwFlags
)
545 TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08X);\n",
546 lphndl
, (int)uDeviceID
, (uType
==MMDRV_WAVEOUT
)?"Out":"In", lpFormat
, dwCallback
,
547 dwInstance
, dwFlags
);
549 if (dwFlags
& WAVE_FORMAT_QUERY
)
550 TRACE("WAVE_FORMAT_QUERY requested !\n");
552 dwRet
= WINMM_CheckCallback(dwCallback
, dwFlags
, FALSE
);
553 if (dwRet
!= MMSYSERR_NOERROR
)
556 if (lpFormat
== NULL
) {
557 WARN("bad format\n");
558 return WAVERR_BADFORMAT
;
561 if ((dwFlags
& WAVE_MAPPED
) && (uDeviceID
== (UINT
)-1)) {
562 WARN("invalid parameter\n");
563 return MMSYSERR_INVALPARAM
;
566 /* may have a PCMWAVEFORMAT rather than a WAVEFORMATEX so don't read cbSize */
567 TRACE("wFormatTag=%u, nChannels=%u, nSamplesPerSec=%u, nAvgBytesPerSec=%u, nBlockAlign=%u, wBitsPerSample=%u\n",
568 lpFormat
->wFormatTag
, lpFormat
->nChannels
, lpFormat
->nSamplesPerSec
,
569 lpFormat
->nAvgBytesPerSec
, lpFormat
->nBlockAlign
, lpFormat
->wBitsPerSample
);
571 if ((wmld
= MMDRV_Alloc(sizeof(WINE_WAVE
), uType
, &handle
,
572 &dwFlags
, &dwCallback
, &dwInstance
)) == NULL
) {
573 return MMSYSERR_NOMEM
;
577 wod
.lpFormat
= (LPWAVEFORMATEX
)lpFormat
; /* should the struct be copied iso pointer? */
578 wod
.dwCallback
= dwCallback
;
579 wod
.dwInstance
= dwInstance
;
582 TRACE("cb=%08lx\n", wod
.dwCallback
);
585 if (dwFlags
& WAVE_MAPPED
) {
586 wod
.uMappedDeviceID
= uDeviceID
;
587 uDeviceID
= WAVE_MAPPER
;
589 wod
.uMappedDeviceID
= -1;
591 wmld
->uDeviceID
= uDeviceID
;
593 dwRet
= MMDRV_Open(wmld
, (uType
== MMDRV_WAVEOUT
) ? WODM_OPEN
: WIDM_OPEN
,
594 (DWORD_PTR
)&wod
, dwFlags
);
596 TRACE("dwRet = %s\n", WINMM_ErrorToString(dwRet
));
597 if (dwRet
!= WAVERR_BADFORMAT
||
598 ((dwFlags
& (WAVE_MAPPED
|WAVE_FORMAT_DIRECT
)) != 0) || (uDeviceID
== WAVE_MAPPER
)) break;
599 /* if we ask for a format which isn't supported by the physical driver,
600 * let's try to map it through the wave mapper (except, if we already tried
601 * or user didn't allow us to use acm codecs or the device is already the mapper)
603 dwFlags
|= WAVE_MAPPED
;
604 /* we shall loop only one */
607 if ((dwFlags
& WAVE_FORMAT_QUERY
) || dwRet
!= MMSYSERR_NOERROR
) {
608 MMDRV_Free(handle
, wmld
);
612 if (lphndl
!= NULL
) *lphndl
= handle
;
613 TRACE("=> %s hWave=%p\n", WINMM_ErrorToString(dwRet
), handle
);
618 static inline BOOL
WINMM_IsMapper(UINT device
)
620 return (device
== WAVE_MAPPER
|| device
== (UINT16
)WAVE_MAPPER
);
623 static MMRESULT
WINMM_TryDeviceMapping(WINMM_OpenInfo
*info
, WORD channels
,
624 DWORD freq
, DWORD bits_per_samp
, BOOL is_out
)
626 WINMM_Device
*device
;
631 TRACE("format: %u, channels: %u, sample rate: %u, bit depth: %u\n",
632 WAVE_FORMAT_PCM
, channels
, freq
, bits_per_samp
);
634 target
.wFormatTag
= WAVE_FORMAT_PCM
;
635 target
.nChannels
= channels
;
636 target
.nSamplesPerSec
= freq
;
637 target
.wBitsPerSample
= bits_per_samp
;
638 target
.nBlockAlign
= (target
.nChannels
* target
.wBitsPerSample
) / 8;
639 target
.nAvgBytesPerSec
= target
.nSamplesPerSec
* target
.nBlockAlign
;
643 mr
= acmStreamOpen(NULL
, NULL
, info
->format
, &target
, NULL
, 0,
644 0, ACM_STREAMOPENF_QUERY
);
646 return MMSYSERR_ERROR
;
647 if(mr
!= MMSYSERR_NOERROR
)
650 /* ACM can convert from src->dst, so try to find a device
651 * that supports dst */
652 if(WINMM_IsMapper(info
->req_device
)){
653 for(i
= 0; i
< g_outmmdevices_count
; ++i
){
654 WINMM_OpenInfo l_info
= *info
;
655 l_info
.req_device
= i
;
656 l_info
.format
= &target
;
657 mr
= WOD_Open(&l_info
);
658 if(mr
== MMSYSERR_NOERROR
){
659 info
->handle
= l_info
.handle
;
664 WINMM_OpenInfo l_info
= *info
;
665 l_info
.flags
&= ~WAVE_MAPPED
;
666 l_info
.format
= &target
;
667 mr
= WOD_Open(&l_info
);
668 if(mr
== MMSYSERR_NOERROR
)
669 info
->handle
= l_info
.handle
;
671 if(mr
!= MMSYSERR_NOERROR
)
672 return WAVERR_BADFORMAT
;
674 device
= WINMM_GetDeviceFromHWAVE((HWAVE
)info
->handle
);
676 return MMSYSERR_INVALHANDLE
;
678 /* set up the ACM stream */
679 mr
= acmStreamOpen(&device
->acm_handle
, NULL
, info
->format
, &target
,
681 if(mr
!= MMSYSERR_NOERROR
){
682 WOD_Close((HWAVEOUT
)info
->handle
);
687 return MMSYSERR_NOERROR
;
690 static MMRESULT
WINMM_MapDevice(WINMM_OpenInfo
*info
, BOOL is_out
)
694 WAVEFORMATEXTENSIBLE
*fmtex
= (WAVEFORMATEXTENSIBLE
*)info
->format
;
696 TRACE("(%p, %d)\n", info
, is_out
);
698 /* try to find a direct match */
700 WINMM_OpenInfo l_info
= *info
;
701 if(WINMM_IsMapper(info
->req_device
)){
702 for(i
= 0; i
< g_outmmdevices_count
; ++i
){
703 l_info
.req_device
= i
;
704 mr
= WOD_Open(&l_info
);
705 if(mr
== MMSYSERR_NOERROR
){
706 info
->handle
= l_info
.handle
;
711 l_info
.flags
&= ~WAVE_MAPPED
;
712 mr
= WOD_Open(&l_info
);
713 if(mr
== MMSYSERR_NOERROR
){
714 info
->handle
= l_info
.handle
;
719 return MMSYSERR_ERROR
;
721 /* no direct match, so set up the ACM stream */
722 if(info
->format
->wFormatTag
!= WAVE_FORMAT_PCM
||
723 (info
->format
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
724 !IsEqualGUID(&fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_PCM
))){
725 /* convert to PCM format if it's not already */
726 mr
= WINMM_TryDeviceMapping(info
, info
->format
->nChannels
,
727 info
->format
->nSamplesPerSec
, 16, is_out
);
728 if(mr
== MMSYSERR_NOERROR
)
731 mr
= WINMM_TryDeviceMapping(info
, info
->format
->nChannels
,
732 info
->format
->nSamplesPerSec
, 8, is_out
);
733 if(mr
== MMSYSERR_NOERROR
)
738 /* first try just changing bit depth and channels */
739 channels
= info
->format
->nChannels
;
740 mr
= WINMM_TryDeviceMapping(info
, channels
,
741 info
->format
->nSamplesPerSec
, 16, is_out
);
742 if(mr
== MMSYSERR_NOERROR
)
744 mr
= WINMM_TryDeviceMapping(info
, channels
,
745 info
->format
->nSamplesPerSec
, 8, is_out
);
746 if(mr
== MMSYSERR_NOERROR
)
749 channels
= (channels
== 2) ? 1 : 2;
750 mr
= WINMM_TryDeviceMapping(info
, channels
,
751 info
->format
->nSamplesPerSec
, 16, is_out
);
752 if(mr
== MMSYSERR_NOERROR
)
754 mr
= WINMM_TryDeviceMapping(info
, channels
,
755 info
->format
->nSamplesPerSec
, 8, is_out
);
756 if(mr
== MMSYSERR_NOERROR
)
759 /* that didn't work, so now try different sample rates */
760 channels
= info
->format
->nChannels
;
761 mr
= WINMM_TryDeviceMapping(info
, channels
, 96000, 16, is_out
);
762 if(mr
== MMSYSERR_NOERROR
)
764 mr
= WINMM_TryDeviceMapping(info
, channels
, 48000, 16, is_out
);
765 if(mr
== MMSYSERR_NOERROR
)
767 mr
= WINMM_TryDeviceMapping(info
, channels
, 44100, 16, is_out
);
768 if(mr
== MMSYSERR_NOERROR
)
770 mr
= WINMM_TryDeviceMapping(info
, channels
, 22050, 16, is_out
);
771 if(mr
== MMSYSERR_NOERROR
)
773 mr
= WINMM_TryDeviceMapping(info
, channels
, 11025, 16, is_out
);
774 if(mr
== MMSYSERR_NOERROR
)
777 channels
= (channels
== 2) ? 1 : 2;
778 mr
= WINMM_TryDeviceMapping(info
, channels
, 96000, 16, is_out
);
779 if(mr
== MMSYSERR_NOERROR
)
781 mr
= WINMM_TryDeviceMapping(info
, channels
, 48000, 16, is_out
);
782 if(mr
== MMSYSERR_NOERROR
)
784 mr
= WINMM_TryDeviceMapping(info
, channels
, 44100, 16, is_out
);
785 if(mr
== MMSYSERR_NOERROR
)
787 mr
= WINMM_TryDeviceMapping(info
, channels
, 22050, 16, is_out
);
788 if(mr
== MMSYSERR_NOERROR
)
790 mr
= WINMM_TryDeviceMapping(info
, channels
, 11025, 16, is_out
);
791 if(mr
== MMSYSERR_NOERROR
)
794 channels
= info
->format
->nChannels
;
795 mr
= WINMM_TryDeviceMapping(info
, channels
, 96000, 8, is_out
);
796 if(mr
== MMSYSERR_NOERROR
)
798 mr
= WINMM_TryDeviceMapping(info
, channels
, 48000, 8, is_out
);
799 if(mr
== MMSYSERR_NOERROR
)
801 mr
= WINMM_TryDeviceMapping(info
, channels
, 44100, 8, is_out
);
802 if(mr
== MMSYSERR_NOERROR
)
804 mr
= WINMM_TryDeviceMapping(info
, channels
, 22050, 8, is_out
);
805 if(mr
== MMSYSERR_NOERROR
)
807 mr
= WINMM_TryDeviceMapping(info
, channels
, 11025, 8, is_out
);
808 if(mr
== MMSYSERR_NOERROR
)
811 channels
= (channels
== 2) ? 1 : 2;
812 mr
= WINMM_TryDeviceMapping(info
, channels
, 96000, 8, is_out
);
813 if(mr
== MMSYSERR_NOERROR
)
815 mr
= WINMM_TryDeviceMapping(info
, channels
, 48000, 8, is_out
);
816 if(mr
== MMSYSERR_NOERROR
)
818 mr
= WINMM_TryDeviceMapping(info
, channels
, 44100, 8, is_out
);
819 if(mr
== MMSYSERR_NOERROR
)
821 mr
= WINMM_TryDeviceMapping(info
, channels
, 22050, 8, is_out
);
822 if(mr
== MMSYSERR_NOERROR
)
824 mr
= WINMM_TryDeviceMapping(info
, channels
, 11025, 8, is_out
);
825 if(mr
== MMSYSERR_NOERROR
)
829 WARN("Unable to find compatible device!\n");
830 return WAVERR_BADFORMAT
;
833 static LRESULT
WINMM_OpenDevice(WINMM_Device
*device
, WINMM_MMDevice
*mmdevice
,
834 WINMM_OpenInfo
*info
)
836 WAVEFORMATEX
*closer_fmt
= NULL
, *passed_fmt
;
837 LRESULT ret
= MMSYSERR_ERROR
;
840 hr
= IMMDeviceEnumerator_GetDevice(g_devenum
, mmdevice
->dev_id
,
843 ERR("Device %s (%s) unavailable: %08x\n",
844 wine_dbgstr_w(mmdevice
->dev_id
),
845 wine_dbgstr_w(mmdevice
->out_caps
.szPname
), hr
);
849 hr
= IMMDevice_Activate(device
->device
, &IID_IAudioClient
,
850 CLSCTX_INPROC_SERVER
, NULL
, (void**)&device
->client
);
852 ERR("Activate failed: %08x\n", hr
);
856 if(info
->format
->wFormatTag
== WAVE_FORMAT_PCM
){
857 /* we aren't guaranteed that the struct in lpFormat is a full
858 * WAVEFORMATEX struct, which IAC::IsFormatSupported requires */
859 passed_fmt
= HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX
));
860 memcpy(passed_fmt
, info
->format
, sizeof(PCMWAVEFORMAT
));
861 passed_fmt
->cbSize
= 0;
863 passed_fmt
= info
->format
;
865 hr
= IAudioClient_IsFormatSupported(device
->client
,
866 AUDCLNT_SHAREMODE_SHARED
, passed_fmt
, &closer_fmt
);
868 CoTaskMemFree(closer_fmt
);
869 if(FAILED(hr
) && hr
!= AUDCLNT_E_UNSUPPORTED_FORMAT
){
870 if(info
->format
->wFormatTag
== WAVE_FORMAT_PCM
)
871 HeapFree(GetProcessHeap(), 0, passed_fmt
);
872 ERR("IsFormatSupported failed: %08x\n", hr
);
875 if(hr
== S_FALSE
|| hr
== AUDCLNT_E_UNSUPPORTED_FORMAT
){
876 if(info
->format
->wFormatTag
== WAVE_FORMAT_PCM
)
877 HeapFree(GetProcessHeap(), 0, passed_fmt
);
878 ret
= WAVERR_BADFORMAT
;
881 if(info
->flags
& WAVE_FORMAT_QUERY
){
882 if(info
->format
->wFormatTag
== WAVE_FORMAT_PCM
)
883 HeapFree(GetProcessHeap(), 0, passed_fmt
);
884 ret
= MMSYSERR_NOERROR
;
888 /* buffer size = 10 * 100000 (100 ns) = 0.1 seconds */
889 hr
= IAudioClient_Initialize(device
->client
, AUDCLNT_SHAREMODE_SHARED
,
890 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
| AUDCLNT_STREAMFLAGS_NOPERSIST
,
891 10 * 100000, 50000, passed_fmt
, &device
->parent
->session
);
892 if(info
->format
->wFormatTag
== WAVE_FORMAT_PCM
)
893 HeapFree(GetProcessHeap(), 0, passed_fmt
);
895 ERR("Initialize failed: %08x\n", hr
);
899 hr
= IAudioClient_GetService(device
->client
, &IID_IAudioClock
,
900 (void**)&device
->clock
);
902 ERR("GetService failed: %08x\n", hr
);
907 device
->event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
909 ERR("CreateEvent failed: %08x\n", hr
);
913 if(g_device_handles
){
914 g_device_handles
= HeapReAlloc(GetProcessHeap(), 0, g_device_handles
,
915 sizeof(HANDLE
) * (g_devhandle_count
+ 1));
916 g_handle_devices
= HeapReAlloc(GetProcessHeap(), 0, g_handle_devices
,
917 sizeof(WINMM_Device
*) * (g_devhandle_count
+ 1));
919 g_device_handles
= HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE
));
920 g_handle_devices
= HeapAlloc(GetProcessHeap(), 0,
921 sizeof(WINMM_Device
*));
923 g_device_handles
[g_devhandle_count
] = device
->event
;
924 g_handle_devices
[g_devhandle_count
] = device
;
928 hr
= IAudioClient_SetEventHandle(device
->client
, device
->event
);
930 ERR("SetEventHandle failed: %08x\n", hr
);
934 device
->bytes_per_frame
= info
->format
->nBlockAlign
;
935 device
->samples_per_sec
= info
->format
->nSamplesPerSec
;
937 device
->played_frames
= 0;
938 device
->last_clock_pos
= 0;
939 device
->ofs_bytes
= 0;
940 device
->loop_counter
= 0;
941 device
->stopped
= TRUE
;
942 device
->first
= device
->last
= device
->playing
= device
->loop_start
= NULL
;
944 device
->cb_info
.flags
= HIWORD(info
->flags
& CALLBACK_TYPEMASK
);
945 device
->cb_info
.callback
= info
->callback
;
946 device
->cb_info
.user
= info
->cb_user
;
947 device
->cb_info
.hwave
= (HWAVE
)device
->handle
;
949 info
->handle
= device
->handle
;
951 return MMSYSERR_NOERROR
;
955 IAudioClient_Release(device
->client
);
956 device
->client
= NULL
;
959 IMMDevice_Release(device
->device
);
960 device
->device
= NULL
;
966 static LRESULT
WOD_Open(WINMM_OpenInfo
*info
)
968 WINMM_MMDevice
*mmdevice
;
969 WINMM_Device
*device
= NULL
;
970 WINMM_CBInfo cb_info
;
971 LRESULT ret
= MMSYSERR_ERROR
;
974 TRACE("(%u, %p, %08x)\n", info
->req_device
, info
, info
->flags
);
976 if(WINMM_IsMapper(info
->req_device
) || (info
->flags
& WAVE_MAPPED
))
977 return WINMM_MapDevice(info
, TRUE
);
979 if(info
->req_device
>= g_outmmdevices_count
)
980 return MMSYSERR_BADDEVICEID
;
982 mmdevice
= &g_out_mmdevices
[info
->req_device
];
984 if(!mmdevice
->out_caps
.szPname
[0])
985 return MMSYSERR_NOTENABLED
;
987 device
= WINMM_FindUnusedDevice(TRUE
, info
->req_device
);
989 return MMSYSERR_ALLOCATED
;
991 ret
= WINMM_OpenDevice(device
, mmdevice
, info
);
992 if((info
->flags
& WAVE_FORMAT_QUERY
) || ret
!= MMSYSERR_NOERROR
)
994 ret
= MMSYSERR_ERROR
;
996 hr
= IAudioClient_GetService(device
->client
, &IID_IAudioRenderClient
,
997 (void**)&device
->render
);
999 ERR("GetService failed: %08x\n", hr
);
1003 hr
= IAudioClient_GetService(device
->client
, &IID_IAudioStreamVolume
,
1004 (void**)&device
->volume
);
1006 ERR("GetService failed: %08x\n", hr
);
1010 memcpy(&cb_info
, &device
->cb_info
, sizeof(cb_info
));
1012 LeaveCriticalSection(&device
->lock
);
1014 WINMM_NotifyClient(&cb_info
, WOM_OPEN
, 0, 0);
1016 return MMSYSERR_NOERROR
;
1020 IMMDevice_Release(device
->device
);
1021 device
->device
= NULL
;
1024 IAudioClient_Release(device
->client
);
1025 device
->client
= NULL
;
1028 IAudioRenderClient_Release(device
->render
);
1029 device
->render
= NULL
;
1032 IAudioStreamVolume_Release(device
->volume
);
1033 device
->volume
= NULL
;
1036 IAudioClock_Release(device
->clock
);
1037 device
->clock
= NULL
;
1039 device
->open
= FALSE
;
1040 LeaveCriticalSection(&device
->lock
);
1044 static HRESULT
WINMM_CloseDevice(WINMM_Device
*device
)
1046 device
->open
= FALSE
;
1048 if(!device
->stopped
){
1049 IAudioClient_Stop(device
->client
);
1050 device
->stopped
= TRUE
;
1053 IMMDevice_Release(device
->device
);
1054 device
->device
= NULL
;
1056 IAudioClient_Release(device
->client
);
1057 device
->client
= NULL
;
1059 IAudioClock_Release(device
->clock
);
1060 device
->clock
= NULL
;
1065 static LRESULT
WOD_Close(HWAVEOUT hwave
)
1067 WINMM_Device
*device
= WINMM_GetDeviceFromHWAVE((HWAVE
)hwave
);
1068 WINMM_CBInfo cb_info
;
1070 TRACE("(%p)\n", hwave
);
1072 if(!WINMM_ValidateAndLock(device
))
1073 return MMSYSERR_INVALHANDLE
;
1075 WINMM_CloseDevice(device
);
1077 IAudioRenderClient_Release(device
->render
);
1078 device
->render
= NULL
;
1080 IAudioStreamVolume_Release(device
->volume
);
1081 device
->volume
= NULL
;
1083 memcpy(&cb_info
, &device
->cb_info
, sizeof(cb_info
));
1085 LeaveCriticalSection(&device
->lock
);
1087 WINMM_NotifyClient(&cb_info
, WOM_CLOSE
, 0, 0);
1089 return MMSYSERR_NOERROR
;
1092 static LRESULT
WINMM_PrepareHeader(HWAVE hwave
, WAVEHDR
*header
)
1094 WINMM_Device
*device
= WINMM_GetDeviceFromHWAVE(hwave
);
1096 TRACE("(%p, %p)\n", hwave
, header
);
1098 if(!WINMM_ValidateAndLock(device
))
1099 return MMSYSERR_INVALHANDLE
;
1101 if(device
->render
&& device
->acm_handle
){
1102 ACMSTREAMHEADER
*ash
;
1106 mr
= acmStreamSize(device
->acm_handle
, header
->dwBufferLength
, &size
,
1107 ACM_STREAMSIZEF_SOURCE
);
1108 if(mr
!= MMSYSERR_NOERROR
){
1109 LeaveCriticalSection(&device
->lock
);
1113 ash
= HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER
) + size
);
1115 LeaveCriticalSection(&device
->lock
);
1116 return MMSYSERR_NOMEM
;
1119 ash
->cbStruct
= sizeof(*ash
);
1121 ash
->dwUser
= (DWORD_PTR
)header
;
1122 ash
->pbSrc
= (BYTE
*)header
->lpData
;
1123 ash
->cbSrcLength
= header
->dwBufferLength
;
1124 ash
->dwSrcUser
= header
->dwUser
;
1125 ash
->pbDst
= (BYTE
*)ash
+ sizeof(ACMSTREAMHEADER
);
1126 ash
->cbDstLength
= size
;
1129 mr
= acmStreamPrepareHeader(device
->acm_handle
, ash
, 0);
1130 if(mr
!= MMSYSERR_NOERROR
){
1131 LeaveCriticalSection(&device
->lock
);
1135 header
->reserved
= (DWORD_PTR
)ash
;
1138 LeaveCriticalSection(&device
->lock
);
1140 header
->dwFlags
|= WHDR_PREPARED
;
1141 header
->dwFlags
&= ~WHDR_DONE
;
1143 return MMSYSERR_NOERROR
;
1146 static LRESULT
WINMM_UnprepareHeader(HWAVE hwave
, WAVEHDR
*header
)
1148 WINMM_Device
*device
= WINMM_GetDeviceFromHWAVE(hwave
);
1150 TRACE("(%p, %p)\n", hwave
, header
);
1152 if(!WINMM_ValidateAndLock(device
))
1153 return MMSYSERR_INVALHANDLE
;
1155 if(device
->render
&& device
->acm_handle
){
1156 ACMSTREAMHEADER
*ash
= (ACMSTREAMHEADER
*)header
->reserved
;
1158 acmStreamUnprepareHeader(device
->acm_handle
, ash
, 0);
1160 HeapFree(GetProcessHeap(), 0, ash
);
1163 LeaveCriticalSection(&device
->lock
);
1165 header
->dwFlags
&= ~WHDR_PREPARED
;
1166 header
->dwFlags
|= WHDR_DONE
;
1168 return MMSYSERR_NOERROR
;
1171 static UINT32
WINMM_HeaderLenBytes(WINMM_Device
*device
, WAVEHDR
*header
)
1173 if(device
->acm_handle
){
1174 ACMSTREAMHEADER
*ash
= (ACMSTREAMHEADER
*)header
->reserved
;
1175 return ash
->cbDstLengthUsed
;
1178 return header
->dwBufferLength
;
1181 static UINT32
WINMM_HeaderLenFrames(WINMM_Device
*device
, WAVEHDR
*header
)
1183 return WINMM_HeaderLenBytes(device
, header
) / device
->bytes_per_frame
;
1186 static WAVEHDR
*WOD_MarkDoneHeaders(WINMM_Device
*device
)
1189 WAVEHDR
*queue
, *first
= device
->first
;
1190 UINT64 clock_freq
, clock_pos
, clock_frames
;
1191 UINT32 nloops
, queue_frames
;
1193 hr
= IAudioClock_GetFrequency(device
->clock
, &clock_freq
);
1195 ERR("GetFrequency failed: %08x\n", hr
);
1199 hr
= IAudioClock_GetPosition(device
->clock
, &clock_pos
, NULL
);
1201 ERR("GetPosition failed: %08x\n", hr
);
1205 clock_frames
= (clock_pos
/ (double)clock_freq
) * device
->samples_per_sec
;
1207 first
= queue
= device
->first
;
1208 nloops
= device
->loop_counter
;
1210 (queue_frames
= WINMM_HeaderLenFrames(device
, queue
)) <=
1211 clock_frames
- device
->last_clock_pos
){
1212 WAVEHDR
*next
= queue
->lpNext
;
1214 device
->first
->dwFlags
&= ~WHDR_INQUEUE
;
1215 device
->first
->dwFlags
|= WHDR_DONE
;
1216 device
->last_clock_pos
+= queue_frames
;
1217 queue
= device
->first
= next
;
1219 if(queue
->dwFlags
& WHDR_BEGINLOOP
)
1222 if(queue
->dwFlags
& WHDR_ENDLOOP
){
1223 queue
= device
->loop_start
;
1232 static void WOD_PushData(WINMM_Device
*device
)
1234 WINMM_CBInfo cb_info
;
1236 UINT32 pad
, bufsize
, avail_frames
, queue_frames
, written
, ofs
;
1237 UINT32 queue_bytes
, nloops
;
1239 WAVEHDR
*queue
, *first
= NULL
;
1241 TRACE("(%p)\n", device
->handle
);
1243 EnterCriticalSection(&device
->lock
);
1249 device
->stopped
= TRUE
;
1250 device
->last_clock_pos
= 0;
1251 IAudioClient_Stop(device
->client
);
1252 IAudioClient_Reset(device
->client
);
1256 hr
= IAudioClient_GetBufferSize(device
->client
, &bufsize
);
1258 ERR("GetBufferSize failed: %08x\n", hr
);
1262 hr
= IAudioClient_GetCurrentPadding(device
->client
, &pad
);
1264 ERR("GetCurrentPadding failed: %08x\n", hr
);
1268 first
= WOD_MarkDoneHeaders(device
);
1270 /* determine which is larger between the available buffer size and
1271 * the amount of data left in the queue */
1272 avail_frames
= bufsize
- pad
;
1274 queue
= device
->playing
;
1275 ofs
= device
->ofs_bytes
;
1278 while(queue
&& queue_frames
< avail_frames
){
1279 queue_bytes
= WINMM_HeaderLenBytes(device
, queue
);
1280 queue_frames
= (queue_bytes
- ofs
) / device
->bytes_per_frame
;
1284 if(queue
->dwFlags
& WHDR_ENDLOOP
&& nloops
< device
->loop_counter
){
1285 queue
= device
->loop_start
;
1288 queue
= queue
->lpNext
;
1291 if(avail_frames
!= 0 && queue_frames
== 0){
1292 hr
= IAudioRenderClient_GetBuffer(device
->render
, avail_frames
, &data
);
1294 ERR("GetBuffer failed: %08x\n", hr
);
1298 hr
= IAudioRenderClient_ReleaseBuffer(device
->render
, avail_frames
,
1299 AUDCLNT_BUFFERFLAGS_SILENT
);
1301 ERR("ReleaseBuffer failed: %08x\n", hr
);
1308 if(queue_frames
< avail_frames
)
1309 avail_frames
= queue_frames
;
1310 if(avail_frames
== 0)
1313 hr
= IAudioRenderClient_GetBuffer(device
->render
, avail_frames
, &data
);
1315 ERR("GetBuffer failed: %08x\n", hr
);
1320 while(device
->playing
&& written
< avail_frames
){
1321 UINT32 copy_frames
, copy_bytes
;
1324 queue
= device
->playing
;
1326 if(device
->acm_handle
){
1327 ACMSTREAMHEADER
*ash
= (ACMSTREAMHEADER
*)queue
->reserved
;
1328 queue_bytes
= ash
->cbDstLengthUsed
;
1329 queue_data
= ash
->pbDst
;
1331 queue_bytes
= queue
->dwBufferLength
;
1332 queue_data
= (BYTE
*)queue
->lpData
;
1335 queue_frames
= (queue_bytes
- device
->ofs_bytes
) /
1336 device
->bytes_per_frame
;
1338 copy_frames
= queue_frames
< (avail_frames
- written
) ?
1339 queue_frames
: avail_frames
- written
;
1340 copy_bytes
= copy_frames
* device
->bytes_per_frame
;
1342 memcpy(data
, queue_data
+ device
->ofs_bytes
, copy_bytes
);
1345 written
+= copy_frames
;
1346 device
->ofs_bytes
+= copy_bytes
;
1348 if(device
->ofs_bytes
>= queue_bytes
){
1349 device
->ofs_bytes
= 0;
1351 if(!(queue
->dwFlags
& (WHDR_BEGINLOOP
| WHDR_ENDLOOP
)))
1352 device
->playing
= queue
->lpNext
;
1354 if(queue
->dwFlags
& WHDR_BEGINLOOP
){
1355 device
->loop_start
= device
->playing
;
1356 device
->playing
= queue
->lpNext
;
1357 device
->loop_counter
= queue
->dwLoops
;
1359 if(queue
->dwFlags
& WHDR_ENDLOOP
){
1360 --device
->loop_counter
;
1361 if(device
->loop_counter
)
1362 device
->playing
= device
->loop_start
;
1364 device
->loop_start
= device
->playing
= queue
->lpNext
;
1370 hr
= IAudioRenderClient_ReleaseBuffer(device
->render
, avail_frames
, 0);
1372 ERR("ReleaseBuffer failed: %08x\n", hr
);
1376 device
->played_frames
+= avail_frames
;
1379 memcpy(&cb_info
, &device
->cb_info
, sizeof(cb_info
));
1381 LeaveCriticalSection(&device
->lock
);
1383 while(first
&& (first
->dwFlags
& WHDR_DONE
)){
1384 WAVEHDR
*next
= first
->lpNext
;
1385 WINMM_NotifyClient(&cb_info
, WOM_DONE
, (DWORD_PTR
)first
, 0);
1390 static HRESULT
WINMM_BeginPlaying(WINMM_Device
*device
)
1394 TRACE("(%p)\n", device
->handle
);
1396 EnterCriticalSection(&device
->lock
);
1399 /* prebuffer data before starting */
1400 WOD_PushData(device
);
1402 if(device
->stopped
){
1403 device
->stopped
= FALSE
;
1405 hr
= IAudioClient_Start(device
->client
);
1406 if(FAILED(hr
) && hr
!= AUDCLNT_E_NOT_STOPPED
){
1407 device
->stopped
= TRUE
;
1408 LeaveCriticalSection(&device
->lock
);
1409 ERR("Start failed: %08x\n", hr
);
1414 LeaveCriticalSection(&device
->lock
);
1419 static LRESULT
WINMM_Pause(HWAVE hwave
)
1421 WINMM_Device
*device
= WINMM_GetDeviceFromHWAVE((HWAVE
)hwave
);
1424 TRACE("(%p)\n", hwave
);
1426 if(!WINMM_ValidateAndLock(device
))
1427 return MMSYSERR_INVALHANDLE
;
1429 hr
= IAudioClient_Stop(device
->client
);
1431 LeaveCriticalSection(&device
->lock
);
1432 ERR("Stop failed: %08x\n", hr
);
1433 return MMSYSERR_ERROR
;
1436 device
->stopped
= FALSE
;
1438 LeaveCriticalSection(&device
->lock
);
1440 return MMSYSERR_NOERROR
;
1443 static LRESULT
WINMM_Reset(HWAVE hwave
)
1445 WINMM_CBInfo cb_info
;
1446 WINMM_Device
*device
= WINMM_GetDeviceFromHWAVE((HWAVE
)hwave
);
1450 TRACE("(%p)\n", hwave
);
1452 if(!WINMM_ValidateAndLock(device
))
1453 return MMSYSERR_INVALHANDLE
;
1455 mr
= WINMM_Pause(hwave
);
1456 if(mr
!= MMSYSERR_NOERROR
){
1457 LeaveCriticalSection(&device
->lock
);
1460 device
->stopped
= TRUE
;
1462 first
= WOD_MarkDoneHeaders(device
);
1463 device
->first
= device
->last
= device
->playing
= NULL
;
1464 device
->ofs_bytes
= 0;
1465 device
->played_frames
= 0;
1466 device
->loop_counter
= 0;
1467 device
->last_clock_pos
= 0;
1469 memcpy(&cb_info
, &device
->cb_info
, sizeof(cb_info
));
1471 LeaveCriticalSection(&device
->lock
);
1474 WAVEHDR
*next
= first
->lpNext
;
1475 first
->dwFlags
&= ~WHDR_INQUEUE
;
1476 first
->dwFlags
|= WHDR_DONE
;
1477 WINMM_NotifyClient(&cb_info
, WOM_DONE
, (DWORD_PTR
)first
, 0);
1481 return MMSYSERR_NOERROR
;
1484 static MMRESULT
WINMM_FramesToMMTime(MMTIME
*time
, UINT32 played_frames
,
1485 UINT32 sample_rate
, UINT32 bytes_per_frame
)
1487 switch(time
->wType
){
1489 time
->u
.sample
= played_frames
;
1490 return MMSYSERR_NOERROR
;
1492 time
->u
.ms
= (DWORD
)((played_frames
/ (double)sample_rate
) * 1000);
1493 return MMSYSERR_NOERROR
;
1495 time
->u
.smpte
.fps
= 30;
1496 if(played_frames
>= sample_rate
){
1497 time
->u
.smpte
.sec
= played_frames
/ (double)sample_rate
;
1498 time
->u
.smpte
.min
= time
->u
.smpte
.sec
/ 60;
1499 time
->u
.smpte
.hour
= time
->u
.smpte
.min
/ 60;
1500 time
->u
.smpte
.sec
%= 60;
1501 time
->u
.smpte
.min
%= 60;
1502 played_frames
%= sample_rate
;
1504 time
->u
.smpte
.sec
= 0;
1505 time
->u
.smpte
.min
= 0;
1506 time
->u
.smpte
.hour
= 0;
1508 time
->u
.smpte
.frame
= (played_frames
/ (double)sample_rate
) * 30;
1509 return MMSYSERR_NOERROR
;
1512 time
->wType
= TIME_BYTES
;
1513 time
->u
.cb
= played_frames
* bytes_per_frame
;
1514 return MMSYSERR_NOERROR
;
1517 return MMSYSERR_ERROR
;
1520 static LRESULT
WINMM_GetPosition(HWAVE hwave
, MMTIME
*time
)
1522 WINMM_Device
*device
= WINMM_GetDeviceFromHWAVE((HWAVE
)hwave
);
1523 UINT32 played_frames
, sample_rate
, bytes_per_frame
;
1525 TRACE("(%p, %p)\n", hwave
, time
);
1527 if(!WINMM_ValidateAndLock(device
))
1528 return MMSYSERR_INVALHANDLE
;
1530 played_frames
= device
->played_frames
;
1531 sample_rate
= device
->samples_per_sec
;
1532 bytes_per_frame
= device
->bytes_per_frame
;
1534 LeaveCriticalSection(&device
->lock
);
1536 return WINMM_FramesToMMTime(time
, played_frames
, sample_rate
,
1540 static LRESULT CALLBACK
WINMM_DevicesMsgProc(HWND hwnd
, UINT msg
, WPARAM wparam
,
1545 return WOD_Open((WINMM_OpenInfo
*)wparam
);
1547 return WOD_Close((HWAVEOUT
)wparam
);
1549 return DefWindowProcW(hwnd
, msg
, wparam
, lparam
);
1552 static DWORD WINAPI
WINMM_DevicesThreadProc(void *arg
)
1556 static const WCHAR messageW
[] = {'M','e','s','s','a','g','e',0};
1558 hr
= CoInitializeEx(NULL
, COINIT_APARTMENTTHREADED
);
1560 ERR("CoInitializeEx failed: %08x\n", hr
);
1564 hr
= WINMM_InitMMDevices();
1570 g_devices_hwnd
= CreateWindowW(messageW
, NULL
, 0, 0, 0, 0, 0,
1571 HWND_MESSAGE
, NULL
, NULL
, NULL
);
1572 if(!g_devices_hwnd
){
1573 ERR("CreateWindow failed: %d\n", GetLastError());
1578 SetWindowLongPtrW(g_devices_hwnd
, GWLP_WNDPROC
,
1579 (LONG_PTR
)WINMM_DevicesMsgProc
);
1581 /* inform caller that the thread is ready to process messages */
1583 evt
= NULL
; /* do not use after this point */
1587 wait
= MsgWaitForMultipleObjects(g_devhandle_count
, g_device_handles
,
1588 FALSE
, INFINITE
, QS_ALLINPUT
);
1589 if(wait
== g_devhandle_count
- WAIT_OBJECT_0
){
1591 if(PeekMessageW(&msg
, g_devices_hwnd
, 0, 0, PM_REMOVE
))
1592 ERR("Unexpected message: 0x%x\n", msg
.message
);
1593 }else if(wait
< g_devhandle_count
){
1594 WINMM_Device
*device
= g_handle_devices
[wait
- WAIT_OBJECT_0
];
1595 WOD_PushData(device
);
1597 ERR("Unexpected MsgWait result 0x%x, GLE: %d\n", wait
,
1601 DestroyWindow(g_devices_hwnd
);
1608 static BOOL
WINMM_StartDevicesThread(void)
1613 EnterCriticalSection(&g_devthread_lock
);
1615 if(g_devices_thread
){
1618 wait
= WaitForSingleObject(g_devices_thread
, 0);
1619 if(wait
== WAIT_TIMEOUT
){
1620 LeaveCriticalSection(&g_devthread_lock
);
1623 if(wait
!= WAIT_OBJECT_0
){
1624 LeaveCriticalSection(&g_devthread_lock
);
1628 g_devices_thread
= NULL
;
1629 g_devices_hwnd
= NULL
;
1632 TRACE("Starting up devices thread\n");
1634 events
[0] = CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
1636 g_devices_thread
= CreateThread(NULL
, 0, WINMM_DevicesThreadProc
,
1637 events
[0], 0, NULL
);
1638 if(!g_devices_thread
){
1639 LeaveCriticalSection(&g_devthread_lock
);
1640 CloseHandle(events
[0]);
1644 events
[1] = g_devices_thread
;
1645 wait
= WaitForMultipleObjects(2, events
, FALSE
, INFINITE
);
1646 CloseHandle(events
[0]);
1647 if(wait
!= WAIT_OBJECT_0
){
1648 if(wait
== 1 - WAIT_OBJECT_0
){
1649 CloseHandle(g_devices_thread
);
1650 g_devices_thread
= NULL
;
1651 g_devices_hwnd
= NULL
;
1653 LeaveCriticalSection(&g_devthread_lock
);
1657 LeaveCriticalSection(&g_devthread_lock
);
1662 /**************************************************************************
1663 * waveOutGetNumDevs [WINMM.@]
1665 UINT WINAPI
waveOutGetNumDevs(void)
1667 if(!WINMM_StartDevicesThread())
1670 TRACE("count: %u\n", g_outmmdevices_count
);
1672 return g_outmmdevices_count
;
1675 /**************************************************************************
1676 * waveOutGetDevCapsA [WINMM.@]
1678 UINT WINAPI
waveOutGetDevCapsA(UINT_PTR uDeviceID
, LPWAVEOUTCAPSA lpCaps
,
1684 TRACE("(%lu, %p, %u)\n", uDeviceID
, lpCaps
, uSize
);
1686 if(!WINMM_StartDevicesThread())
1687 return MMSYSERR_ERROR
;
1690 return MMSYSERR_INVALPARAM
;
1692 ret
= waveOutGetDevCapsW(uDeviceID
, &wocW
, sizeof(wocW
));
1694 if (ret
== MMSYSERR_NOERROR
) {
1696 wocA
.wMid
= wocW
.wMid
;
1697 wocA
.wPid
= wocW
.wPid
;
1698 wocA
.vDriverVersion
= wocW
.vDriverVersion
;
1699 WideCharToMultiByte( CP_ACP
, 0, wocW
.szPname
, -1, wocA
.szPname
,
1700 sizeof(wocA
.szPname
), NULL
, NULL
);
1701 wocA
.dwFormats
= wocW
.dwFormats
;
1702 wocA
.wChannels
= wocW
.wChannels
;
1703 wocA
.dwSupport
= wocW
.dwSupport
;
1704 memcpy(lpCaps
, &wocA
, min(uSize
, sizeof(wocA
)));
1709 /**************************************************************************
1710 * waveOutGetDevCapsW [WINMM.@]
1712 UINT WINAPI
waveOutGetDevCapsW(UINT_PTR uDeviceID
, LPWAVEOUTCAPSW lpCaps
,
1715 WAVEOUTCAPSW mapper_caps
, *caps
;
1717 TRACE("(%lu, %p, %u)\n", uDeviceID
, lpCaps
, uSize
);
1719 if(!WINMM_StartDevicesThread())
1720 return MMSYSERR_ERROR
;
1722 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
1724 if(WINMM_IsMapper(uDeviceID
)){
1725 /* FIXME: Should be localized */
1726 static const WCHAR mapper_pnameW
[] = {'W','i','n','e',' ','S','o','u',
1727 'n','d',' ','M','a','p','p','e','r',0};
1729 mapper_caps
.wMid
= 0xFF;
1730 mapper_caps
.wPid
= 0xFF;
1731 mapper_caps
.vDriverVersion
= 0x00010001;
1732 mapper_caps
.dwFormats
= 0xFFFFFFFF;
1733 mapper_caps
.wReserved1
= 0;
1734 mapper_caps
.dwSupport
= WAVECAPS_LRVOLUME
| WAVECAPS_VOLUME
|
1735 WAVECAPS_SAMPLEACCURATE
;
1736 mapper_caps
.wChannels
= 2;
1737 lstrcpyW(mapper_caps
.szPname
, mapper_pnameW
);
1739 caps
= &mapper_caps
;
1741 if(uDeviceID
>= g_outmmdevices_count
)
1742 return MMSYSERR_BADDEVICEID
;
1744 caps
= &g_out_mmdevices
[uDeviceID
].out_caps
;
1747 memcpy(lpCaps
, caps
, min(uSize
, sizeof(*lpCaps
)));
1749 return MMSYSERR_NOERROR
;
1752 /**************************************************************************
1753 * waveOutGetErrorTextA [WINMM.@]
1754 * waveInGetErrorTextA [WINMM.@]
1756 UINT WINAPI
waveOutGetErrorTextA(UINT uError
, LPSTR lpText
, UINT uSize
)
1760 if (lpText
== NULL
) ret
= MMSYSERR_INVALPARAM
;
1761 else if (uSize
== 0) ret
= MMSYSERR_NOERROR
;
1764 LPWSTR xstr
= HeapAlloc(GetProcessHeap(), 0, uSize
* sizeof(WCHAR
));
1765 if (!xstr
) ret
= MMSYSERR_NOMEM
;
1768 ret
= waveOutGetErrorTextW(uError
, xstr
, uSize
);
1769 if (ret
== MMSYSERR_NOERROR
)
1770 WideCharToMultiByte(CP_ACP
, 0, xstr
, -1, lpText
, uSize
, NULL
, NULL
);
1771 HeapFree(GetProcessHeap(), 0, xstr
);
1777 /**************************************************************************
1778 * waveOutGetErrorTextW [WINMM.@]
1779 * waveInGetErrorTextW [WINMM.@]
1781 UINT WINAPI
waveOutGetErrorTextW(UINT uError
, LPWSTR lpText
, UINT uSize
)
1783 UINT ret
= MMSYSERR_BADERRNUM
;
1785 if (lpText
== NULL
) ret
= MMSYSERR_INVALPARAM
;
1786 else if (uSize
== 0) ret
= MMSYSERR_NOERROR
;
1788 /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
1789 * a warning for the test was always true */
1790 (/*uError >= MMSYSERR_BASE && */ uError
<= MMSYSERR_LASTERROR
) ||
1791 (uError
>= WAVERR_BASE
&& uError
<= WAVERR_LASTERROR
)) {
1792 if (LoadStringW(hWinMM32Instance
,
1793 uError
, lpText
, uSize
) > 0) {
1794 ret
= MMSYSERR_NOERROR
;
1800 /**************************************************************************
1801 * waveOutOpen [WINMM.@]
1802 * All the args/structs have the same layout as the win16 equivalents
1804 MMRESULT WINAPI
waveOutOpen(LPHWAVEOUT lphWaveOut
, UINT uDeviceID
,
1805 LPCWAVEFORMATEX lpFormat
, DWORD_PTR dwCallback
,
1806 DWORD_PTR dwInstance
, DWORD dwFlags
)
1810 WINMM_OpenInfo info
;
1812 TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut
, uDeviceID
, lpFormat
,
1813 dwCallback
, dwInstance
, dwFlags
);
1815 if(!WINMM_StartDevicesThread())
1816 return MMSYSERR_ERROR
;
1818 if(!lphWaveOut
&& !(dwFlags
& WAVE_FORMAT_QUERY
))
1819 return MMSYSERR_INVALPARAM
;
1821 hr
= WINMM_StartDevicesThread();
1823 ERR("Couldn't start the device thread: %08x\n", hr
);
1824 return MMSYSERR_ERROR
;
1827 info
.format
= (WAVEFORMATEX
*)lpFormat
;
1828 info
.callback
= dwCallback
;
1829 info
.cb_user
= dwInstance
;
1830 info
.req_device
= uDeviceID
;
1831 info
.flags
= dwFlags
;
1833 res
= SendMessageW(g_devices_hwnd
, WODM_OPEN
, (DWORD_PTR
)&info
, 0);
1834 if(res
!= MMSYSERR_NOERROR
)
1838 *lphWaveOut
= (HWAVEOUT
)info
.handle
;
1843 /**************************************************************************
1844 * waveOutClose [WINMM.@]
1846 UINT WINAPI
waveOutClose(HWAVEOUT hWaveOut
)
1848 TRACE("(%p)\n", hWaveOut
);
1850 if(!WINMM_StartDevicesThread())
1851 return MMSYSERR_ERROR
;
1853 return SendMessageW(g_devices_hwnd
, WODM_CLOSE
, (WPARAM
)hWaveOut
, 0);
1856 /**************************************************************************
1857 * waveOutPrepareHeader [WINMM.@]
1859 UINT WINAPI
waveOutPrepareHeader(HWAVEOUT hWaveOut
,
1860 WAVEHDR
* lpWaveOutHdr
, UINT uSize
)
1862 TRACE("(%p, %p, %u)\n", hWaveOut
, lpWaveOutHdr
, uSize
);
1864 if(!WINMM_StartDevicesThread())
1865 return MMSYSERR_ERROR
;
1867 if(!lpWaveOutHdr
|| uSize
< sizeof(WAVEHDR
))
1868 return MMSYSERR_INVALPARAM
;
1870 if(lpWaveOutHdr
->dwFlags
& WHDR_INQUEUE
)
1871 return WAVERR_STILLPLAYING
;
1873 return WINMM_PrepareHeader((HWAVE
)hWaveOut
, lpWaveOutHdr
);
1876 /**************************************************************************
1877 * waveOutUnprepareHeader [WINMM.@]
1879 UINT WINAPI
waveOutUnprepareHeader(HWAVEOUT hWaveOut
,
1880 LPWAVEHDR lpWaveOutHdr
, UINT uSize
)
1882 TRACE("(%p, %p, %u)\n", hWaveOut
, lpWaveOutHdr
, uSize
);
1884 if(!WINMM_StartDevicesThread())
1885 return MMSYSERR_ERROR
;
1887 if(!lpWaveOutHdr
|| uSize
< sizeof(WAVEHDR
))
1888 return MMSYSERR_INVALPARAM
;
1890 if(!(lpWaveOutHdr
->dwFlags
& WHDR_PREPARED
))
1891 return MMSYSERR_NOERROR
;
1893 if(lpWaveOutHdr
->dwFlags
& WHDR_INQUEUE
)
1894 return WAVERR_STILLPLAYING
;
1896 return WINMM_UnprepareHeader((HWAVE
)hWaveOut
, lpWaveOutHdr
);
1899 /**************************************************************************
1900 * waveOutWrite [WINMM.@]
1902 UINT WINAPI
waveOutWrite(HWAVEOUT hWaveOut
, WAVEHDR
*header
, UINT uSize
)
1904 WINMM_Device
*device
;
1907 TRACE("(%p, %p, %u)\n", hWaveOut
, header
, uSize
);
1909 if(!WINMM_StartDevicesThread())
1910 return MMSYSERR_ERROR
;
1912 device
= WINMM_GetDeviceFromHWAVE((HWAVE
)hWaveOut
);
1914 if(!WINMM_ValidateAndLock(device
))
1915 return MMSYSERR_INVALHANDLE
;
1917 if(!header
->lpData
|| !(header
->dwFlags
& WHDR_PREPARED
)){
1918 LeaveCriticalSection(&device
->lock
);
1919 return WAVERR_UNPREPARED
;
1922 if(header
->dwFlags
& WHDR_INQUEUE
){
1923 LeaveCriticalSection(&device
->lock
);
1924 return WAVERR_STILLPLAYING
;
1927 if(device
->acm_handle
){
1928 ACMSTREAMHEADER
*ash
= (ACMSTREAMHEADER
*)header
->reserved
;
1931 ash
->cbSrcLength
= header
->dwBufferLength
;
1932 mr
= acmStreamConvert(device
->acm_handle
, ash
, 0);
1933 if(mr
!= MMSYSERR_NOERROR
){
1934 LeaveCriticalSection(&device
->lock
);
1940 device
->last
->lpNext
= header
;
1941 device
->last
= header
;
1942 if(!device
->playing
)
1943 device
->playing
= header
;
1945 device
->playing
= device
->first
= device
->last
= header
;
1947 header
->lpNext
= NULL
;
1948 header
->dwFlags
&= ~WHDR_DONE
;
1949 header
->dwFlags
|= WHDR_INQUEUE
;
1951 hr
= WINMM_BeginPlaying(device
);
1953 LeaveCriticalSection(&device
->lock
);
1954 return MMSYSERR_ERROR
;
1957 LeaveCriticalSection(&device
->lock
);
1959 return MMSYSERR_NOERROR
;
1962 /**************************************************************************
1963 * waveOutBreakLoop [WINMM.@]
1965 UINT WINAPI
waveOutBreakLoop(HWAVEOUT hWaveOut
)
1967 WINMM_Device
*device
;
1969 TRACE("(%p)\n", hWaveOut
);
1971 if(!WINMM_StartDevicesThread())
1972 return MMSYSERR_ERROR
;
1974 device
= WINMM_GetDeviceFromHWAVE((HWAVE
)hWaveOut
);
1976 if(!WINMM_ValidateAndLock(device
))
1977 return MMSYSERR_INVALHANDLE
;
1979 device
->loop_counter
= 0;
1981 LeaveCriticalSection(&device
->lock
);
1983 return MMSYSERR_NOERROR
;
1986 /**************************************************************************
1987 * waveOutPause [WINMM.@]
1989 UINT WINAPI
waveOutPause(HWAVEOUT hWaveOut
)
1991 TRACE("(%p)\n", hWaveOut
);
1993 if(!WINMM_StartDevicesThread())
1994 return MMSYSERR_ERROR
;
1996 return WINMM_Pause((HWAVE
)hWaveOut
);
1999 /**************************************************************************
2000 * waveOutReset [WINMM.@]
2002 UINT WINAPI
waveOutReset(HWAVEOUT hWaveOut
)
2004 TRACE("(%p)\n", hWaveOut
);
2006 if(!WINMM_StartDevicesThread())
2007 return MMSYSERR_ERROR
;
2009 return WINMM_Reset((HWAVE
)hWaveOut
);
2012 /**************************************************************************
2013 * waveOutRestart [WINMM.@]
2015 UINT WINAPI
waveOutRestart(HWAVEOUT hWaveOut
)
2017 WINMM_Device
*device
;
2020 TRACE("(%p)\n", hWaveOut
);
2022 if(!WINMM_StartDevicesThread())
2023 return MMSYSERR_ERROR
;
2025 device
= WINMM_GetDeviceFromHWAVE((HWAVE
)hWaveOut
);
2027 if(!WINMM_ValidateAndLock(device
))
2028 return MMSYSERR_INVALHANDLE
;
2030 device
->stopped
= TRUE
;
2032 hr
= WINMM_BeginPlaying(device
);
2034 LeaveCriticalSection(&device
->lock
);
2035 return MMSYSERR_ERROR
;
2038 LeaveCriticalSection(&device
->lock
);
2040 return MMSYSERR_NOERROR
;
2043 /**************************************************************************
2044 * waveOutGetPosition [WINMM.@]
2046 UINT WINAPI
waveOutGetPosition(HWAVEOUT hWaveOut
, LPMMTIME lpTime
,
2049 TRACE("(%p, %p, %u)\n", hWaveOut
, lpTime
, uSize
);
2051 if(!WINMM_StartDevicesThread())
2052 return MMSYSERR_ERROR
;
2054 if(!uSize
|| !lpTime
|| uSize
!= sizeof(MMTIME
))
2055 return MMSYSERR_INVALPARAM
;
2057 return WINMM_GetPosition((HWAVE
)hWaveOut
, lpTime
);
2060 /**************************************************************************
2061 * waveOutGetPitch [WINMM.@]
2063 UINT WINAPI
waveOutGetPitch(HWAVEOUT hWaveOut
, LPDWORD lpdw
)
2065 TRACE("(%p, %p)\n", hWaveOut
, lpdw
);
2066 return MMSYSERR_NOTSUPPORTED
;
2069 /**************************************************************************
2070 * waveOutSetPitch [WINMM.@]
2072 UINT WINAPI
waveOutSetPitch(HWAVEOUT hWaveOut
, DWORD dw
)
2074 TRACE("(%p, %08x)\n", hWaveOut
, dw
);
2076 return MMSYSERR_NOTSUPPORTED
;
2079 /**************************************************************************
2080 * waveOutGetPlaybackRate [WINMM.@]
2082 UINT WINAPI
waveOutGetPlaybackRate(HWAVEOUT hWaveOut
, LPDWORD lpdw
)
2084 TRACE("(%p, %p)\n", hWaveOut
, lpdw
);
2086 return MMSYSERR_NOTSUPPORTED
;
2089 /**************************************************************************
2090 * waveOutSetPlaybackRate [WINMM.@]
2092 UINT WINAPI
waveOutSetPlaybackRate(HWAVEOUT hWaveOut
, DWORD dw
)
2094 TRACE("(%p, %08x)\n", hWaveOut
, dw
);
2096 return MMSYSERR_NOTSUPPORTED
;
2099 /**************************************************************************
2100 * waveOutGetVolume [WINMM.@]
2102 UINT WINAPI
waveOutGetVolume(HWAVEOUT hWaveOut
, DWORD
*out
)
2104 WINMM_Device
*device
;
2109 TRACE("(%p, %p)\n", hWaveOut
, out
);
2111 if(!WINMM_StartDevicesThread())
2112 return MMSYSERR_ERROR
;
2115 return MMSYSERR_INVALPARAM
;
2117 device
= WINMM_GetDeviceFromHWAVE((HWAVE
)hWaveOut
);
2119 if(!WINMM_ValidateAndLock(device
))
2120 return MMSYSERR_INVALHANDLE
;
2122 hr
= IAudioStreamVolume_GetChannelCount(device
->volume
, &channels
);
2124 LeaveCriticalSection(&device
->lock
);
2125 ERR("GetChannelCount failed: %08x\n", hr
);
2126 return MMSYSERR_ERROR
;
2129 vols
= HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels
);
2131 LeaveCriticalSection(&device
->lock
);
2132 return MMSYSERR_NOMEM
;
2135 hr
= IAudioStreamVolume_GetAllVolumes(device
->volume
, channels
, vols
);
2137 LeaveCriticalSection(&device
->lock
);
2138 HeapFree(GetProcessHeap(), 0, vols
);
2139 ERR("GetAllVolumes failed: %08x\n", hr
);
2140 return MMSYSERR_ERROR
;
2143 LeaveCriticalSection(&device
->lock
);
2145 *out
= ((UINT16
)(vols
[0] * (DWORD
)0xFFFF));
2147 *out
|= ((UINT16
)(vols
[1] * (DWORD
)0xFFFF)) << 16;
2149 HeapFree(GetProcessHeap(), 0, vols
);
2151 return MMSYSERR_NOERROR
;
2154 /**************************************************************************
2155 * waveOutSetVolume [WINMM.@]
2157 UINT WINAPI
waveOutSetVolume(HWAVEOUT hWaveOut
, DWORD in
)
2159 WINMM_Device
*device
;
2164 TRACE("(%p, %08x)\n", hWaveOut
, in
);
2166 if(!WINMM_StartDevicesThread())
2167 return MMSYSERR_ERROR
;
2169 device
= WINMM_GetDeviceFromHWAVE((HWAVE
)hWaveOut
);
2171 if(!WINMM_ValidateAndLock(device
))
2172 return MMSYSERR_INVALHANDLE
;
2174 hr
= IAudioStreamVolume_GetChannelCount(device
->volume
, &channels
);
2176 LeaveCriticalSection(&device
->lock
);
2177 ERR("GetChannelCount failed: %08x\n", hr
);
2178 return MMSYSERR_ERROR
;
2181 vols
= HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels
);
2183 LeaveCriticalSection(&device
->lock
);
2184 return MMSYSERR_NOMEM
;
2187 hr
= IAudioStreamVolume_GetAllVolumes(device
->volume
, channels
, vols
);
2189 LeaveCriticalSection(&device
->lock
);
2190 HeapFree(GetProcessHeap(), 0, vols
);
2191 ERR("GetAllVolumes failed: %08x\n", hr
);
2192 return MMSYSERR_ERROR
;
2195 vols
[0] = (float)((DWORD
)(in
& 0xFFFF) / (float)0xFFFF);
2197 vols
[1] = (float)((DWORD
)(in
>> 16) / (float)0xFFFF);
2199 hr
= IAudioStreamVolume_SetAllVolumes(device
->volume
, channels
, vols
);
2201 LeaveCriticalSection(&device
->lock
);
2202 HeapFree(GetProcessHeap(), 0, vols
);
2203 ERR("SetAllVolumes failed: %08x\n", hr
);
2204 return MMSYSERR_ERROR
;
2207 LeaveCriticalSection(&device
->lock
);
2209 HeapFree(GetProcessHeap(), 0, vols
);
2211 return MMSYSERR_NOERROR
;
2214 /**************************************************************************
2215 * waveOutGetID [WINMM.@]
2217 UINT WINAPI
waveOutGetID(HWAVEOUT hWaveOut
, UINT
* lpuDeviceID
)
2219 WINMM_Device
*device
;
2223 TRACE("(%p, %p)\n", hWaveOut
, lpuDeviceID
);
2225 if(!WINMM_StartDevicesThread())
2226 return MMSYSERR_ERROR
;
2229 return MMSYSERR_INVALPARAM
;
2231 device
= WINMM_GetDeviceFromHWAVE((HWAVE
)hWaveOut
);
2232 if(!WINMM_ValidateAndLock(device
))
2233 return MMSYSERR_INVALHANDLE
;
2235 LeaveCriticalSection(&device
->lock
);
2237 WINMM_DecomposeHWAVE((HWAVE
)hWaveOut
, lpuDeviceID
, &is_out
, &dev
, &junk
);
2239 return MMSYSERR_NOERROR
;
2242 static UINT
WINMM_QueryInstanceIDSize(UINT device
, DWORD_PTR
*len
, BOOL is_out
)
2245 WINMM_MMDevice
*devices
;
2247 TRACE("(%u, %p, %d)\n", device
, len
, is_out
);
2250 count
= g_outmmdevices_count
;
2251 devices
= g_out_mmdevices
;
2253 count
= g_inmmdevices_count
;
2254 devices
= g_in_mmdevices
;
2258 return MMSYSERR_INVALHANDLE
;
2260 *len
= (lstrlenW(devices
[device
].dev_id
) + 1) * sizeof(WCHAR
);
2262 return MMSYSERR_NOERROR
;
2265 static UINT
WINMM_QueryInstanceID(UINT device
, WCHAR
*str
, DWORD_PTR len
,
2269 WINMM_MMDevice
*devices
;
2271 TRACE("(%u, %p, %d)\n", device
, str
, is_out
);
2274 count
= g_outmmdevices_count
;
2275 devices
= g_out_mmdevices
;
2277 count
= g_inmmdevices_count
;
2278 devices
= g_in_mmdevices
;
2282 return MMSYSERR_INVALHANDLE
;
2284 id_len
= (lstrlenW(devices
[device
].dev_id
) + 1) * sizeof(WCHAR
);
2286 return MMSYSERR_ERROR
;
2288 memcpy(str
, devices
[device
].dev_id
, id_len
);
2290 return MMSYSERR_NOERROR
;
2293 UINT
WINMM_DRVMessage(UINT dev
, UINT message
, DWORD_PTR param1
,
2298 TRACE("(%u, %u, %ld, %ld)\n", dev
, message
, param1
, param2
);
2300 if((wmld
= MMDRV_Get(ULongToHandle(dev
), MMDRV_WAVEOUT
, FALSE
)) == NULL
){
2301 if((wmld
= MMDRV_Get(ULongToHandle(dev
), MMDRV_WAVEOUT
, TRUE
)) != NULL
)
2302 return MMDRV_PhysicalFeatures(wmld
, message
, param1
, param2
);
2303 return MMSYSERR_INVALHANDLE
;
2306 if(message
< DRVM_IOCTL
||
2307 (message
>= DRVM_IOCTL_LAST
&& message
< DRVM_MAPPER
))
2308 return MMSYSERR_INVALPARAM
;
2310 return MMDRV_Message(wmld
, message
, param1
, param2
);
2313 static UINT
WINMM_FillDSDriverDesc(UINT dev
, DSDRIVERDESC
*desc
, BOOL is_out
)
2318 if(dev
>= g_outmmdevices_count
)
2319 return MMSYSERR_INVALHANDLE
;
2320 name
= g_out_mmdevices
[dev
].out_caps
.szPname
;
2322 if(dev
>= g_inmmdevices_count
)
2323 return MMSYSERR_INVALHANDLE
;
2324 name
= g_in_mmdevices
[dev
].in_caps
.szPname
;
2327 memset(desc
, 0, sizeof(*desc
));
2329 strcpy(desc
->szDesc
, "WinMM: ");
2330 WideCharToMultiByte(CP_ACP
, 0, name
, -1,
2331 desc
->szDesc
+ strlen(desc
->szDesc
),
2332 sizeof(desc
->szDesc
) - strlen(desc
->szDesc
), NULL
, NULL
);
2334 strcpy(desc
->szDrvname
, "winmm.dll");
2336 return MMSYSERR_NOERROR
;
2339 /**************************************************************************
2340 * waveOutMessage [WINMM.@]
2342 UINT WINAPI
waveOutMessage(HWAVEOUT hWaveOut
, UINT uMessage
,
2343 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
2345 TRACE("(%p, %u, %lx, %lx)\n", hWaveOut
, uMessage
, dwParam1
, dwParam2
);
2348 case DRV_QUERYFUNCTIONINSTANCEIDSIZE
:
2349 return WINMM_QueryInstanceIDSize(HandleToULong(hWaveOut
),
2350 (DWORD_PTR
*)dwParam1
, TRUE
);
2351 case DRV_QUERYFUNCTIONINSTANCEID
:
2352 return WINMM_QueryInstanceID(HandleToULong(hWaveOut
),
2353 (WCHAR
*)dwParam1
, (DWORD_PTR
)dwParam2
, TRUE
);
2354 /* TODO: Remove after dsound has been rewritten for mmdevapi */
2355 case DRV_QUERYDSOUNDDESC
:
2356 case DRV_QUERYDSOUNDIFACE
:
2357 if(dwParam2
== DS_HW_ACCEL_FULL
)
2358 return WINMM_DRVMessage(HandleToULong(hWaveOut
), uMessage
,
2360 return WINMM_FillDSDriverDesc(HandleToULong(hWaveOut
),
2361 (DSDRIVERDESC
*)dwParam1
, TRUE
);
2364 return MMSYSERR_NOTSUPPORTED
;
2367 /**************************************************************************
2368 * waveInGetNumDevs [WINMM.@]
2370 UINT WINAPI
waveInGetNumDevs(void)
2372 return MMDRV_GetNum(MMDRV_WAVEIN
);
2375 /**************************************************************************
2376 * waveInGetDevCapsW [WINMM.@]
2378 UINT WINAPI
waveInGetDevCapsW(UINT_PTR uDeviceID
, LPWAVEINCAPSW lpCaps
, UINT uSize
)
2382 TRACE("(%lu %p %u)!\n", uDeviceID
, lpCaps
, uSize
);
2384 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
2386 if ((wmld
= MMDRV_Get((HANDLE
)uDeviceID
, MMDRV_WAVEIN
, TRUE
)) == NULL
)
2387 return MMSYSERR_BADDEVICEID
;
2389 return MMDRV_Message(wmld
, WIDM_GETDEVCAPS
, (DWORD_PTR
)lpCaps
, uSize
);
2392 /**************************************************************************
2393 * waveInGetDevCapsA [WINMM.@]
2395 UINT WINAPI
waveInGetDevCapsA(UINT_PTR uDeviceID
, LPWAVEINCAPSA lpCaps
, UINT uSize
)
2400 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
2402 ret
= waveInGetDevCapsW(uDeviceID
, &wicW
, sizeof(wicW
));
2404 if (ret
== MMSYSERR_NOERROR
) {
2406 wicA
.wMid
= wicW
.wMid
;
2407 wicA
.wPid
= wicW
.wPid
;
2408 wicA
.vDriverVersion
= wicW
.vDriverVersion
;
2409 WideCharToMultiByte( CP_ACP
, 0, wicW
.szPname
, -1, wicA
.szPname
,
2410 sizeof(wicA
.szPname
), NULL
, NULL
);
2411 wicA
.dwFormats
= wicW
.dwFormats
;
2412 wicA
.wChannels
= wicW
.wChannels
;
2413 memcpy(lpCaps
, &wicA
, min(uSize
, sizeof(wicA
)));
2418 /**************************************************************************
2419 * waveInOpen [WINMM.@]
2421 MMRESULT WINAPI
waveInOpen(HWAVEIN
* lphWaveIn
, UINT uDeviceID
,
2422 LPCWAVEFORMATEX lpFormat
, DWORD_PTR dwCallback
,
2423 DWORD_PTR dwInstance
, DWORD dwFlags
)
2425 return WAVE_Open((HANDLE
*)lphWaveIn
, uDeviceID
, MMDRV_WAVEIN
, lpFormat
,
2426 dwCallback
, dwInstance
, dwFlags
);
2429 /**************************************************************************
2430 * waveInClose [WINMM.@]
2432 UINT WINAPI
waveInClose(HWAVEIN hWaveIn
)
2437 TRACE("(%p)\n", hWaveIn
);
2439 if ((wmld
= MMDRV_Get(hWaveIn
, MMDRV_WAVEIN
, FALSE
)) == NULL
)
2440 return MMSYSERR_INVALHANDLE
;
2442 dwRet
= MMDRV_Message(wmld
, WIDM_CLOSE
, 0L, 0L);
2443 if (dwRet
!= WAVERR_STILLPLAYING
)
2444 MMDRV_Free(hWaveIn
, wmld
);
2448 /**************************************************************************
2449 * waveInPrepareHeader [WINMM.@]
2451 UINT WINAPI
waveInPrepareHeader(HWAVEIN hWaveIn
, WAVEHDR
* lpWaveInHdr
,
2457 TRACE("(%p, %p, %u);\n", hWaveIn
, lpWaveInHdr
, uSize
);
2459 if (lpWaveInHdr
== NULL
|| uSize
< sizeof (WAVEHDR
))
2460 return MMSYSERR_INVALPARAM
;
2462 if ((wmld
= MMDRV_Get(hWaveIn
, MMDRV_WAVEIN
, FALSE
)) == NULL
)
2463 return MMSYSERR_INVALHANDLE
;
2465 if ((result
= MMDRV_Message(wmld
, WIDM_PREPARE
, (DWORD_PTR
)lpWaveInHdr
,
2466 uSize
)) != MMSYSERR_NOTSUPPORTED
)
2469 if (lpWaveInHdr
->dwFlags
& WHDR_INQUEUE
)
2470 return WAVERR_STILLPLAYING
;
2472 lpWaveInHdr
->dwFlags
|= WHDR_PREPARED
;
2473 lpWaveInHdr
->dwFlags
&= ~WHDR_DONE
;
2474 lpWaveInHdr
->dwBytesRecorded
= 0;
2476 return MMSYSERR_NOERROR
;
2479 /**************************************************************************
2480 * waveInUnprepareHeader [WINMM.@]
2482 UINT WINAPI
waveInUnprepareHeader(HWAVEIN hWaveIn
, WAVEHDR
* lpWaveInHdr
,
2488 TRACE("(%p, %p, %u);\n", hWaveIn
, lpWaveInHdr
, uSize
);
2490 if (lpWaveInHdr
== NULL
|| uSize
< sizeof (WAVEHDR
))
2491 return MMSYSERR_INVALPARAM
;
2493 if (!(lpWaveInHdr
->dwFlags
& WHDR_PREPARED
))
2494 return MMSYSERR_NOERROR
;
2496 if ((wmld
= MMDRV_Get(hWaveIn
, MMDRV_WAVEIN
, FALSE
)) == NULL
)
2497 return MMSYSERR_INVALHANDLE
;
2499 if ((result
= MMDRV_Message(wmld
, WIDM_UNPREPARE
, (DWORD_PTR
)lpWaveInHdr
,
2500 uSize
)) != MMSYSERR_NOTSUPPORTED
)
2503 if (lpWaveInHdr
->dwFlags
& WHDR_INQUEUE
)
2504 return WAVERR_STILLPLAYING
;
2506 lpWaveInHdr
->dwFlags
&= ~WHDR_PREPARED
;
2507 lpWaveInHdr
->dwFlags
|= WHDR_DONE
;
2509 return MMSYSERR_NOERROR
;
2512 /**************************************************************************
2513 * waveInAddBuffer [WINMM.@]
2515 UINT WINAPI
waveInAddBuffer(HWAVEIN hWaveIn
,
2516 WAVEHDR
* lpWaveInHdr
, UINT uSize
)
2520 TRACE("(%p, %p, %u);\n", hWaveIn
, lpWaveInHdr
, uSize
);
2522 if (lpWaveInHdr
== NULL
) return MMSYSERR_INVALPARAM
;
2523 if ((wmld
= MMDRV_Get(hWaveIn
, MMDRV_WAVEIN
, FALSE
)) == NULL
)
2524 return MMSYSERR_INVALHANDLE
;
2526 return MMDRV_Message(wmld
, WIDM_ADDBUFFER
, (DWORD_PTR
)lpWaveInHdr
, uSize
);
2529 /**************************************************************************
2530 * waveInReset [WINMM.@]
2532 UINT WINAPI
waveInReset(HWAVEIN hWaveIn
)
2536 TRACE("(%p);\n", hWaveIn
);
2538 if ((wmld
= MMDRV_Get(hWaveIn
, MMDRV_WAVEIN
, FALSE
)) == NULL
)
2539 return MMSYSERR_INVALHANDLE
;
2541 return MMDRV_Message(wmld
, WIDM_RESET
, 0L, 0L);
2544 /**************************************************************************
2545 * waveInStart [WINMM.@]
2547 UINT WINAPI
waveInStart(HWAVEIN hWaveIn
)
2551 TRACE("(%p);\n", hWaveIn
);
2553 if ((wmld
= MMDRV_Get(hWaveIn
, MMDRV_WAVEIN
, FALSE
)) == NULL
)
2554 return MMSYSERR_INVALHANDLE
;
2556 return MMDRV_Message(wmld
, WIDM_START
, 0L, 0L);
2559 /**************************************************************************
2560 * waveInStop [WINMM.@]
2562 UINT WINAPI
waveInStop(HWAVEIN hWaveIn
)
2566 TRACE("(%p);\n", hWaveIn
);
2568 if ((wmld
= MMDRV_Get(hWaveIn
, MMDRV_WAVEIN
, FALSE
)) == NULL
)
2569 return MMSYSERR_INVALHANDLE
;
2571 return MMDRV_Message(wmld
,WIDM_STOP
, 0L, 0L);
2574 /**************************************************************************
2575 * waveInGetPosition [WINMM.@]
2577 UINT WINAPI
waveInGetPosition(HWAVEIN hWaveIn
, LPMMTIME lpTime
,
2582 TRACE("(%p, %p, %u);\n", hWaveIn
, lpTime
, uSize
);
2584 if ((wmld
= MMDRV_Get(hWaveIn
, MMDRV_WAVEIN
, FALSE
)) == NULL
)
2585 return MMSYSERR_INVALHANDLE
;
2587 return MMDRV_Message(wmld
, WIDM_GETPOS
, (DWORD_PTR
)lpTime
, uSize
);
2590 /**************************************************************************
2591 * waveInGetID [WINMM.@]
2593 UINT WINAPI
waveInGetID(HWAVEIN hWaveIn
, UINT
* lpuDeviceID
)
2597 TRACE("(%p, %p);\n", hWaveIn
, lpuDeviceID
);
2599 if (lpuDeviceID
== NULL
) return MMSYSERR_INVALHANDLE
;
2601 if ((wmld
= MMDRV_Get(hWaveIn
, MMDRV_WAVEIN
, FALSE
)) == NULL
)
2602 return MMSYSERR_INVALHANDLE
;
2604 *lpuDeviceID
= wmld
->uDeviceID
;
2605 return MMSYSERR_NOERROR
;
2608 /**************************************************************************
2609 * waveInMessage [WINMM.@]
2611 UINT WINAPI
waveInMessage(HWAVEIN hWaveIn
, UINT uMessage
,
2612 DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
2616 TRACE("(%p, %u, %ld, %ld)\n", hWaveIn
, uMessage
, dwParam1
, dwParam2
);
2618 if ((wmld
= MMDRV_Get(hWaveIn
, MMDRV_WAVEIN
, FALSE
)) == NULL
) {
2619 if ((wmld
= MMDRV_Get(hWaveIn
, MMDRV_WAVEIN
, TRUE
)) != NULL
) {
2620 return MMDRV_PhysicalFeatures(wmld
, uMessage
, dwParam1
, dwParam2
);
2622 return MMSYSERR_INVALHANDLE
;
2626 if (uMessage
< DRVM_IOCTL
|| (uMessage
>= DRVM_IOCTL_LAST
&& uMessage
< DRVM_MAPPER
))
2627 return MMSYSERR_INVALPARAM
;
2630 return MMDRV_Message(wmld
, uMessage
, dwParam1
, dwParam2
);
2633 /**************************************************************************
2634 * find out the real mixer ID depending on hmix (depends on dwFlags)
2636 static UINT
MIXER_GetDev(HMIXEROBJ hmix
, DWORD dwFlags
, LPWINE_MIXER
* lplpwm
)
2638 LPWINE_MIXER lpwm
= NULL
;
2639 UINT uRet
= MMSYSERR_NOERROR
;
2641 switch (dwFlags
& 0xF0000000ul
) {
2642 case MIXER_OBJECTF_MIXER
:
2643 lpwm
= (LPWINE_MIXER
)MMDRV_Get(hmix
, MMDRV_MIXER
, TRUE
);
2645 case MIXER_OBJECTF_HMIXER
:
2646 lpwm
= (LPWINE_MIXER
)MMDRV_Get(hmix
, MMDRV_MIXER
, FALSE
);
2648 case MIXER_OBJECTF_WAVEOUT
:
2649 lpwm
= (LPWINE_MIXER
)MMDRV_GetRelated(hmix
, MMDRV_WAVEOUT
, TRUE
, MMDRV_MIXER
);
2651 case MIXER_OBJECTF_HWAVEOUT
:
2652 lpwm
= (LPWINE_MIXER
)MMDRV_GetRelated(hmix
, MMDRV_WAVEOUT
, FALSE
, MMDRV_MIXER
);
2654 case MIXER_OBJECTF_WAVEIN
:
2655 lpwm
= (LPWINE_MIXER
)MMDRV_GetRelated(hmix
, MMDRV_WAVEIN
, TRUE
, MMDRV_MIXER
);
2657 case MIXER_OBJECTF_HWAVEIN
:
2658 lpwm
= (LPWINE_MIXER
)MMDRV_GetRelated(hmix
, MMDRV_WAVEIN
, FALSE
, MMDRV_MIXER
);
2660 case MIXER_OBJECTF_MIDIOUT
:
2661 lpwm
= (LPWINE_MIXER
)MMDRV_GetRelated(hmix
, MMDRV_MIDIOUT
, TRUE
, MMDRV_MIXER
);
2663 case MIXER_OBJECTF_HMIDIOUT
:
2664 lpwm
= (LPWINE_MIXER
)MMDRV_GetRelated(hmix
, MMDRV_MIDIOUT
, FALSE
, MMDRV_MIXER
);
2666 case MIXER_OBJECTF_MIDIIN
:
2667 lpwm
= (LPWINE_MIXER
)MMDRV_GetRelated(hmix
, MMDRV_MIDIIN
, TRUE
, MMDRV_MIXER
);
2669 case MIXER_OBJECTF_HMIDIIN
:
2670 lpwm
= (LPWINE_MIXER
)MMDRV_GetRelated(hmix
, MMDRV_MIDIIN
, FALSE
, MMDRV_MIXER
);
2672 case MIXER_OBJECTF_AUX
:
2673 lpwm
= (LPWINE_MIXER
)MMDRV_GetRelated(hmix
, MMDRV_AUX
, TRUE
, MMDRV_MIXER
);
2676 WARN("Unsupported flag (%08lx)\n", dwFlags
& 0xF0000000ul
);
2678 uRet
= MMSYSERR_INVALFLAG
;
2682 if (lpwm
== 0 && uRet
== MMSYSERR_NOERROR
)
2683 uRet
= MMSYSERR_INVALPARAM
;
2687 /**************************************************************************
2688 * mixerGetNumDevs [WINMM.@]
2690 UINT WINAPI
mixerGetNumDevs(void)
2692 return MMDRV_GetNum(MMDRV_MIXER
);
2695 /**************************************************************************
2696 * mixerGetDevCapsA [WINMM.@]
2698 UINT WINAPI
mixerGetDevCapsA(UINT_PTR uDeviceID
, LPMIXERCAPSA lpCaps
, UINT uSize
)
2703 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
2705 ret
= mixerGetDevCapsW(uDeviceID
, &micW
, sizeof(micW
));
2707 if (ret
== MMSYSERR_NOERROR
) {
2709 micA
.wMid
= micW
.wMid
;
2710 micA
.wPid
= micW
.wPid
;
2711 micA
.vDriverVersion
= micW
.vDriverVersion
;
2712 WideCharToMultiByte( CP_ACP
, 0, micW
.szPname
, -1, micA
.szPname
,
2713 sizeof(micA
.szPname
), NULL
, NULL
);
2714 micA
.fdwSupport
= micW
.fdwSupport
;
2715 micA
.cDestinations
= micW
.cDestinations
;
2716 memcpy(lpCaps
, &micA
, min(uSize
, sizeof(micA
)));
2721 /**************************************************************************
2722 * mixerGetDevCapsW [WINMM.@]
2724 UINT WINAPI
mixerGetDevCapsW(UINT_PTR uDeviceID
, LPMIXERCAPSW lpCaps
, UINT uSize
)
2728 if (lpCaps
== NULL
) return MMSYSERR_INVALPARAM
;
2730 if ((wmld
= MMDRV_Get((HANDLE
)uDeviceID
, MMDRV_MIXER
, TRUE
)) == NULL
)
2731 return MMSYSERR_BADDEVICEID
;
2733 return MMDRV_Message(wmld
, MXDM_GETDEVCAPS
, (DWORD_PTR
)lpCaps
, uSize
);
2736 static void CALLBACK
MIXER_WCallback(HMIXEROBJ hmx
, UINT uMsg
, DWORD_PTR dwInstance
, DWORD_PTR dwParam
, DWORD_PTR param2
)
2738 HWND hWnd
= (HWND
)dwInstance
;
2743 PostMessageW(hWnd
, uMsg
, (WPARAM
)hmx
, (LPARAM
)dwParam
);
2746 /**************************************************************************
2747 * mixerOpen [WINMM.@]
2749 UINT WINAPI
mixerOpen(LPHMIXER lphMix
, UINT uDeviceID
, DWORD_PTR dwCallback
,
2750 DWORD_PTR dwInstance
, DWORD fdwOpen
)
2757 TRACE("(%p, %d, %08lx, %08lx, %08x)\n",
2758 lphMix
, uDeviceID
, dwCallback
, dwInstance
, fdwOpen
);
2760 dwRet
= WINMM_CheckCallback(dwCallback
, fdwOpen
, TRUE
);
2761 if (dwRet
!= MMSYSERR_NOERROR
)
2764 mod
.dwCallback
= (DWORD_PTR
)MIXER_WCallback
;
2765 if ((fdwOpen
& CALLBACK_TYPEMASK
) == CALLBACK_WINDOW
)
2766 mod
.dwInstance
= dwCallback
;
2770 /* We're remapping to CALLBACK_FUNCTION because that's what old winmm is
2771 * documented to do when opening the mixer driver.
2772 * FIXME: Native supports CALLBACK_EVENT + CALLBACK_THREAD flags since w2k.
2773 * FIXME: The non ALSA drivers ignore callback requests - bug.
2776 wmld
= MMDRV_Alloc(sizeof(WINE_MIXER
), MMDRV_MIXER
, &hMix
, &fdwOpen
,
2777 &dwCallback
, &dwInstance
);
2778 wmld
->uDeviceID
= uDeviceID
;
2781 dwRet
= MMDRV_Open(wmld
, MXDM_OPEN
, (DWORD_PTR
)&mod
, CALLBACK_FUNCTION
);
2783 if (dwRet
!= MMSYSERR_NOERROR
) {
2784 MMDRV_Free(hMix
, wmld
);
2787 if (lphMix
) *lphMix
= hMix
;
2788 TRACE("=> %d hMixer=%p\n", dwRet
, hMix
);
2793 /**************************************************************************
2794 * mixerClose [WINMM.@]
2796 UINT WINAPI
mixerClose(HMIXER hMix
)
2801 TRACE("(%p)\n", hMix
);
2803 if ((wmld
= MMDRV_Get(hMix
, MMDRV_MIXER
, FALSE
)) == NULL
) return MMSYSERR_INVALHANDLE
;
2805 dwRet
= MMDRV_Close(wmld
, MXDM_CLOSE
);
2806 MMDRV_Free(hMix
, wmld
);
2811 /**************************************************************************
2812 * mixerGetID [WINMM.@]
2814 UINT WINAPI
mixerGetID(HMIXEROBJ hmix
, LPUINT lpid
, DWORD fdwID
)
2817 UINT uRet
= MMSYSERR_NOERROR
;
2819 TRACE("(%p %p %08x)\n", hmix
, lpid
, fdwID
);
2821 if ((uRet
= MIXER_GetDev(hmix
, fdwID
, &lpwm
)) != MMSYSERR_NOERROR
)
2825 *lpid
= lpwm
->mld
.uDeviceID
;
2830 /**************************************************************************
2831 * mixerGetControlDetailsW [WINMM.@]
2833 UINT WINAPI
mixerGetControlDetailsW(HMIXEROBJ hmix
, LPMIXERCONTROLDETAILS lpmcdW
,
2837 UINT uRet
= MMSYSERR_NOERROR
;
2839 TRACE("(%p, %p, %08x)\n", hmix
, lpmcdW
, fdwDetails
);
2841 if ((uRet
= MIXER_GetDev(hmix
, fdwDetails
, &lpwm
)) != MMSYSERR_NOERROR
)
2844 if (lpmcdW
== NULL
|| lpmcdW
->cbStruct
!= sizeof(*lpmcdW
))
2845 return MMSYSERR_INVALPARAM
;
2847 return MMDRV_Message(&lpwm
->mld
, MXDM_GETCONTROLDETAILS
, (DWORD_PTR
)lpmcdW
,
2851 /**************************************************************************
2852 * mixerGetControlDetailsA [WINMM.@]
2854 UINT WINAPI
mixerGetControlDetailsA(HMIXEROBJ hmix
, LPMIXERCONTROLDETAILS lpmcdA
,
2857 DWORD ret
= MMSYSERR_NOTENABLED
;
2859 TRACE("(%p, %p, %08x)\n", hmix
, lpmcdA
, fdwDetails
);
2861 if (lpmcdA
== NULL
|| lpmcdA
->cbStruct
!= sizeof(*lpmcdA
))
2862 return MMSYSERR_INVALPARAM
;
2864 switch (fdwDetails
& MIXER_GETCONTROLDETAILSF_QUERYMASK
) {
2865 case MIXER_GETCONTROLDETAILSF_VALUE
:
2866 /* can safely use A structure as it is, no string inside */
2867 ret
= mixerGetControlDetailsW(hmix
, lpmcdA
, fdwDetails
);
2869 case MIXER_GETCONTROLDETAILSF_LISTTEXT
:
2871 MIXERCONTROLDETAILS_LISTTEXTA
*pDetailsA
= lpmcdA
->paDetails
;
2872 MIXERCONTROLDETAILS_LISTTEXTW
*pDetailsW
;
2873 int size
= max(1, lpmcdA
->cChannels
) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW
);
2876 if (lpmcdA
->u
.cMultipleItems
!= 0) {
2877 size
*= lpmcdA
->u
.cMultipleItems
;
2879 pDetailsW
= HeapAlloc(GetProcessHeap(), 0, size
);
2880 lpmcdA
->paDetails
= pDetailsW
;
2881 lpmcdA
->cbDetails
= sizeof(MIXERCONTROLDETAILS_LISTTEXTW
);
2882 /* set up lpmcd->paDetails */
2883 ret
= mixerGetControlDetailsW(hmix
, lpmcdA
, fdwDetails
);
2884 /* copy from lpmcd->paDetails back to paDetailsW; */
2885 if (ret
== MMSYSERR_NOERROR
) {
2886 for (i
= 0; i
< lpmcdA
->u
.cMultipleItems
* lpmcdA
->cChannels
; i
++) {
2887 pDetailsA
->dwParam1
= pDetailsW
->dwParam1
;
2888 pDetailsA
->dwParam2
= pDetailsW
->dwParam2
;
2889 WideCharToMultiByte( CP_ACP
, 0, pDetailsW
->szName
, -1,
2891 sizeof(pDetailsA
->szName
), NULL
, NULL
);
2895 pDetailsA
-= lpmcdA
->u
.cMultipleItems
* lpmcdA
->cChannels
;
2896 pDetailsW
-= lpmcdA
->u
.cMultipleItems
* lpmcdA
->cChannels
;
2898 HeapFree(GetProcessHeap(), 0, pDetailsW
);
2899 lpmcdA
->paDetails
= pDetailsA
;
2900 lpmcdA
->cbDetails
= sizeof(MIXERCONTROLDETAILS_LISTTEXTA
);
2904 ERR("Unsupported fdwDetails=0x%08x\n", fdwDetails
);
2910 /**************************************************************************
2911 * mixerGetLineControlsA [WINMM.@]
2913 UINT WINAPI
mixerGetLineControlsA(HMIXEROBJ hmix
, LPMIXERLINECONTROLSA lpmlcA
,
2916 MIXERLINECONTROLSW mlcW
;
2920 TRACE("(%p, %p, %08x)\n", hmix
, lpmlcA
, fdwControls
);
2922 if (lpmlcA
== NULL
|| lpmlcA
->cbStruct
!= sizeof(*lpmlcA
) ||
2923 lpmlcA
->cbmxctrl
!= sizeof(MIXERCONTROLA
))
2924 return MMSYSERR_INVALPARAM
;
2926 mlcW
.cbStruct
= sizeof(mlcW
);
2927 mlcW
.dwLineID
= lpmlcA
->dwLineID
;
2928 mlcW
.u
.dwControlID
= lpmlcA
->u
.dwControlID
;
2929 mlcW
.u
.dwControlType
= lpmlcA
->u
.dwControlType
;
2931 /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
2932 the control count is assumed to be 1 - This is relied upon by a game,
2933 "Dynomite Deluze" */
2934 if (MIXER_GETLINECONTROLSF_ONEBYTYPE
== (fdwControls
& MIXER_GETLINECONTROLSF_QUERYMASK
)) {
2937 mlcW
.cControls
= lpmlcA
->cControls
;
2939 mlcW
.cbmxctrl
= sizeof(MIXERCONTROLW
);
2940 mlcW
.pamxctrl
= HeapAlloc(GetProcessHeap(), 0,
2941 mlcW
.cControls
* mlcW
.cbmxctrl
);
2943 ret
= mixerGetLineControlsW(hmix
, &mlcW
, fdwControls
);
2945 if (ret
== MMSYSERR_NOERROR
) {
2946 lpmlcA
->dwLineID
= mlcW
.dwLineID
;
2947 lpmlcA
->u
.dwControlID
= mlcW
.u
.dwControlID
;
2948 lpmlcA
->u
.dwControlType
= mlcW
.u
.dwControlType
;
2950 for (i
= 0; i
< mlcW
.cControls
; i
++) {
2951 lpmlcA
->pamxctrl
[i
].cbStruct
= sizeof(MIXERCONTROLA
);
2952 lpmlcA
->pamxctrl
[i
].dwControlID
= mlcW
.pamxctrl
[i
].dwControlID
;
2953 lpmlcA
->pamxctrl
[i
].dwControlType
= mlcW
.pamxctrl
[i
].dwControlType
;
2954 lpmlcA
->pamxctrl
[i
].fdwControl
= mlcW
.pamxctrl
[i
].fdwControl
;
2955 lpmlcA
->pamxctrl
[i
].cMultipleItems
= mlcW
.pamxctrl
[i
].cMultipleItems
;
2956 WideCharToMultiByte( CP_ACP
, 0, mlcW
.pamxctrl
[i
].szShortName
, -1,
2957 lpmlcA
->pamxctrl
[i
].szShortName
,
2958 sizeof(lpmlcA
->pamxctrl
[i
].szShortName
), NULL
, NULL
);
2959 WideCharToMultiByte( CP_ACP
, 0, mlcW
.pamxctrl
[i
].szName
, -1,
2960 lpmlcA
->pamxctrl
[i
].szName
,
2961 sizeof(lpmlcA
->pamxctrl
[i
].szName
), NULL
, NULL
);
2962 /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
2963 * sizeof(mlcW.pamxctrl[i].Bounds) */
2964 memcpy(&lpmlcA
->pamxctrl
[i
].Bounds
, &mlcW
.pamxctrl
[i
].Bounds
,
2965 sizeof(mlcW
.pamxctrl
[i
].Bounds
));
2966 /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
2967 * sizeof(mlcW.pamxctrl[i].Metrics) */
2968 memcpy(&lpmlcA
->pamxctrl
[i
].Metrics
, &mlcW
.pamxctrl
[i
].Metrics
,
2969 sizeof(mlcW
.pamxctrl
[i
].Metrics
));
2973 HeapFree(GetProcessHeap(), 0, mlcW
.pamxctrl
);
2978 /**************************************************************************
2979 * mixerGetLineControlsW [WINMM.@]
2981 UINT WINAPI
mixerGetLineControlsW(HMIXEROBJ hmix
, LPMIXERLINECONTROLSW lpmlcW
,
2985 UINT uRet
= MMSYSERR_NOERROR
;
2987 TRACE("(%p, %p, %08x)\n", hmix
, lpmlcW
, fdwControls
);
2989 if ((uRet
= MIXER_GetDev(hmix
, fdwControls
, &lpwm
)) != MMSYSERR_NOERROR
)
2992 if (lpmlcW
== NULL
|| lpmlcW
->cbStruct
!= sizeof(*lpmlcW
))
2993 return MMSYSERR_INVALPARAM
;
2995 return MMDRV_Message(&lpwm
->mld
, MXDM_GETLINECONTROLS
, (DWORD_PTR
)lpmlcW
,
2999 /**************************************************************************
3000 * mixerGetLineInfoW [WINMM.@]
3002 UINT WINAPI
mixerGetLineInfoW(HMIXEROBJ hmix
, LPMIXERLINEW lpmliW
, DWORD fdwInfo
)
3005 UINT uRet
= MMSYSERR_NOERROR
;
3007 TRACE("(%p, %p, %08x)\n", hmix
, lpmliW
, fdwInfo
);
3009 if ((uRet
= MIXER_GetDev(hmix
, fdwInfo
, &lpwm
)) != MMSYSERR_NOERROR
)
3012 return MMDRV_Message(&lpwm
->mld
, MXDM_GETLINEINFO
, (DWORD_PTR
)lpmliW
,
3016 /**************************************************************************
3017 * mixerGetLineInfoA [WINMM.@]
3019 UINT WINAPI
mixerGetLineInfoA(HMIXEROBJ hmix
, LPMIXERLINEA lpmliA
,
3025 TRACE("(%p, %p, %08x)\n", hmix
, lpmliA
, fdwInfo
);
3027 if (lpmliA
== NULL
|| lpmliA
->cbStruct
!= sizeof(*lpmliA
))
3028 return MMSYSERR_INVALPARAM
;
3030 mliW
.cbStruct
= sizeof(mliW
);
3031 switch (fdwInfo
& MIXER_GETLINEINFOF_QUERYMASK
) {
3032 case MIXER_GETLINEINFOF_COMPONENTTYPE
:
3033 mliW
.dwComponentType
= lpmliA
->dwComponentType
;
3035 case MIXER_GETLINEINFOF_DESTINATION
:
3036 mliW
.dwDestination
= lpmliA
->dwDestination
;
3038 case MIXER_GETLINEINFOF_LINEID
:
3039 mliW
.dwLineID
= lpmliA
->dwLineID
;
3041 case MIXER_GETLINEINFOF_SOURCE
:
3042 mliW
.dwDestination
= lpmliA
->dwDestination
;
3043 mliW
.dwSource
= lpmliA
->dwSource
;
3045 case MIXER_GETLINEINFOF_TARGETTYPE
:
3046 mliW
.Target
.dwType
= lpmliA
->Target
.dwType
;
3047 mliW
.Target
.wMid
= lpmliA
->Target
.wMid
;
3048 mliW
.Target
.wPid
= lpmliA
->Target
.wPid
;
3049 mliW
.Target
.vDriverVersion
= lpmliA
->Target
.vDriverVersion
;
3050 MultiByteToWideChar( CP_ACP
, 0, lpmliA
->Target
.szPname
, -1, mliW
.Target
.szPname
, sizeof(mliW
.Target
.szPname
)/sizeof(WCHAR
));
3053 WARN("Unsupported fdwControls=0x%08x\n", fdwInfo
);
3054 return MMSYSERR_INVALFLAG
;
3057 ret
= mixerGetLineInfoW(hmix
, &mliW
, fdwInfo
);
3059 if(ret
== MMSYSERR_NOERROR
)
3061 lpmliA
->dwDestination
= mliW
.dwDestination
;
3062 lpmliA
->dwSource
= mliW
.dwSource
;
3063 lpmliA
->dwLineID
= mliW
.dwLineID
;
3064 lpmliA
->fdwLine
= mliW
.fdwLine
;
3065 lpmliA
->dwUser
= mliW
.dwUser
;
3066 lpmliA
->dwComponentType
= mliW
.dwComponentType
;
3067 lpmliA
->cChannels
= mliW
.cChannels
;
3068 lpmliA
->cConnections
= mliW
.cConnections
;
3069 lpmliA
->cControls
= mliW
.cControls
;
3070 WideCharToMultiByte( CP_ACP
, 0, mliW
.szShortName
, -1, lpmliA
->szShortName
,
3071 sizeof(lpmliA
->szShortName
), NULL
, NULL
);
3072 WideCharToMultiByte( CP_ACP
, 0, mliW
.szName
, -1, lpmliA
->szName
,
3073 sizeof(lpmliA
->szName
), NULL
, NULL
);
3074 lpmliA
->Target
.dwType
= mliW
.Target
.dwType
;
3075 lpmliA
->Target
.dwDeviceID
= mliW
.Target
.dwDeviceID
;
3076 lpmliA
->Target
.wMid
= mliW
.Target
.wMid
;
3077 lpmliA
->Target
.wPid
= mliW
.Target
.wPid
;
3078 lpmliA
->Target
.vDriverVersion
= mliW
.Target
.vDriverVersion
;
3079 WideCharToMultiByte( CP_ACP
, 0, mliW
.Target
.szPname
, -1, lpmliA
->Target
.szPname
,
3080 sizeof(lpmliA
->Target
.szPname
), NULL
, NULL
);
3085 /**************************************************************************
3086 * mixerSetControlDetails [WINMM.@]
3088 UINT WINAPI
mixerSetControlDetails(HMIXEROBJ hmix
, LPMIXERCONTROLDETAILS lpmcd
,
3092 UINT uRet
= MMSYSERR_NOERROR
;
3094 TRACE("(%p, %p, %08x)\n", hmix
, lpmcd
, fdwDetails
);
3096 if ((uRet
= MIXER_GetDev(hmix
, fdwDetails
, &lpwm
)) != MMSYSERR_NOERROR
)
3099 return MMDRV_Message(&lpwm
->mld
, MXDM_SETCONTROLDETAILS
, (DWORD_PTR
)lpmcd
,
3103 /**************************************************************************
3104 * mixerMessage [WINMM.@]
3106 DWORD WINAPI
mixerMessage(HMIXER hmix
, UINT uMsg
, DWORD_PTR dwParam1
, DWORD_PTR dwParam2
)
3110 TRACE("(%p, %d, %08lx, %08lx): semi-stub?\n",
3111 hmix
, uMsg
, dwParam1
, dwParam2
);
3113 if ((wmld
= MMDRV_Get(hmix
, MMDRV_MIXER
, FALSE
)) == NULL
)
3114 return MMSYSERR_INVALHANDLE
;
3116 return MMDRV_Message(wmld
, uMsg
, dwParam1
, dwParam2
);