msvcrt: Add a new file for the x86_64 exception handling.
[wine/multimedia.git] / dlls / winmm / waveform.c
blobd7d2be65b52be0e9858614513b89df28de4f86ca
1 /*
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
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
27 #define COBJMACROS
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "mmsystem.h"
32 #include "mmreg.h"
33 #include "msacm.h"
34 #include "winuser.h"
35 #include "winnls.h"
36 #include "winternl.h"
38 #include "winemm.h"
40 #include "ole2.h"
41 #include "initguid.h"
42 #include "devpkey.h"
43 #include "mmdeviceapi.h"
44 #include "audioclient.h"
45 #include "audiopolicy.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
51 /* FIXME: Should be localized */
52 static const WCHAR volumeW[] = {'V','o','l','u','m','e',0};
53 static const WCHAR mastervolumeW[] = {'M','a','s','t','e','r',' ','V','o','l',
54 'u','m','e',0};
55 static const WCHAR muteW[] = {'M','u','t','e',0};
57 /* HWAVE (and HMIXER) format:
59 * XXXX... 1FDD DDDD IIII IIII
60 * X = unused (must be 0)
61 * 1 = the bit is set to 1, to avoid all-zero HWAVEs
62 * F = flow direction (0 = IN, 1 = OUT)
63 * D = index into g_out_mmdevices
64 * I = index in the mmdevice's devices array
66 * Two reasons that we don't just use pointers:
67 * - HWAVEs must fit into 16 bits for compatibility with old applications.
68 * - We must be able to identify bad devices without crashing.
71 #define MAX_DEVICES 256
73 typedef struct _WINMM_CBInfo {
74 DWORD_PTR callback;
75 DWORD_PTR user;
76 DWORD flags;
77 HWAVE hwave;
78 } WINMM_CBInfo;
80 struct _WINMM_MMDevice;
81 typedef struct _WINMM_MMDevice WINMM_MMDevice;
83 typedef struct _WINMM_Device {
84 WINMM_CBInfo cb_info;
86 HWAVE handle;
88 BOOL open;
90 IMMDevice *device;
91 IAudioClient *client;
92 IAudioRenderClient *render;
93 IAudioCaptureClient *capture;
94 IAudioClock *clock;
95 IAudioStreamVolume *volume;
97 HACMSTREAM acm_handle;
98 ACMSTREAMHEADER acm_hdr;
99 UINT32 acm_offs;
101 WAVEHDR *first, *last, *playing, *loop_start;
103 BOOL stopped;
104 DWORD loop_counter;
105 UINT32 bytes_per_frame, samples_per_sec, ofs_bytes, played_frames;
107 /* stored in frames of sample rate, *not* AC::GetFrequency */
108 UINT64 last_clock_pos;
110 HANDLE event;
111 CRITICAL_SECTION lock;
113 WINMM_MMDevice *parent;
114 } WINMM_Device;
116 struct _WINMM_MMDevice {
117 WAVEOUTCAPSW out_caps; /* must not be modified outside of WINMM_InitMMDevices*/
118 WAVEINCAPSW in_caps; /* must not be modified outside of WINMM_InitMMDevices*/
119 WCHAR *dev_id;
121 ISimpleAudioVolume *volume;
123 GUID session;
125 /* HMIXER format is the same as the HWAVE format, but the I bits are
126 * replaced by the value of this counter, to keep each HMIXER unique */
127 UINT mixer_count;
129 CRITICAL_SECTION lock;
131 WINMM_Device *devices[MAX_DEVICES];
134 static WINMM_MMDevice *g_out_mmdevices;
135 static UINT g_outmmdevices_count;
137 static WINMM_MMDevice *g_in_mmdevices;
138 static UINT g_inmmdevices_count;
140 static IMMDeviceEnumerator *g_devenum;
142 static CRITICAL_SECTION g_devthread_lock;
143 static CRITICAL_SECTION_DEBUG g_devthread_lock_debug =
145 0, 0, &g_devthread_lock,
146 { &g_devthread_lock_debug.ProcessLocksList, &g_devthread_lock_debug.ProcessLocksList },
147 0, 0, { (DWORD_PTR)(__FILE__ ": g_devthread_lock") }
149 static CRITICAL_SECTION g_devthread_lock = { &g_devthread_lock_debug, -1, 0, 0, 0, 0 };
150 static HANDLE g_devices_thread;
151 static HWND g_devices_hwnd;
153 static UINT g_devhandle_count;
154 static HANDLE *g_device_handles;
155 static WINMM_Device **g_handle_devices;
157 typedef struct _WINMM_OpenInfo {
158 HWAVE handle;
159 UINT req_device;
160 WAVEFORMATEX *format;
161 DWORD_PTR callback;
162 DWORD_PTR cb_user;
163 DWORD flags;
164 } WINMM_OpenInfo;
166 typedef struct _WINMM_ControlDetails {
167 HMIXEROBJ hmix;
168 MIXERCONTROLDETAILS *details;
169 DWORD flags;
170 } WINMM_ControlDetails;
172 static LRESULT WOD_Open(WINMM_OpenInfo *info);
173 static LRESULT WOD_Close(HWAVEOUT hwave);
174 static LRESULT WID_Open(WINMM_OpenInfo *info);
175 static LRESULT WID_Close(HWAVEIN hwave);
177 void WINMM_DeleteWaveform(void)
179 /* FIXME: Free g_(in,out)_mmdevices? */
180 DeleteCriticalSection(&g_devthread_lock);
183 static inline HWAVE WINMM_MakeHWAVE(UINT mmdevice, BOOL is_out, UINT device)
185 return ULongToHandle((1 << 15) | ((!!is_out) << 14) |
186 (mmdevice << 8) | device);
189 static inline void WINMM_DecomposeHWAVE(HWAVE hwave, UINT *mmdevice_index,
190 BOOL *is_out, UINT *device_index, UINT *junk)
192 ULONG32 l = HandleToULong(hwave);
193 *device_index = l & 0xFF;
194 *mmdevice_index = (l >> 8) & 0x3F;
195 *is_out = (l >> 14) & 0x1;
196 *junk = l >> 15;
199 static void WINMM_InitDevice(WINMM_Device *device,
200 WINMM_MMDevice *parent, HWAVE hwave)
202 InitializeCriticalSection(&device->lock);
203 device->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM_Device.lock");
205 device->handle = hwave;
206 device->parent = parent;
209 /* finds the first unused Device, marks it as "open", and returns
210 * a pointer to the device
212 * IMPORTANT: it is the caller's responsibility to release the device's lock
213 * on success
215 static WINMM_Device *WINMM_FindUnusedDevice(BOOL is_out, UINT mmdevice_index)
217 WINMM_MMDevice *mmdevice;
218 UINT i;
220 if(is_out)
221 mmdevice = &g_out_mmdevices[mmdevice_index];
222 else
223 mmdevice = &g_in_mmdevices[mmdevice_index];
225 EnterCriticalSection(&mmdevice->lock);
226 for(i = 0; i < MAX_DEVICES; ++i){
227 WINMM_Device *device = mmdevice->devices[i];
229 if(!device){
230 device = mmdevice->devices[i] = HeapAlloc(GetProcessHeap(),
231 HEAP_ZERO_MEMORY, sizeof(WINMM_Device));
232 if(!device){
233 LeaveCriticalSection(&mmdevice->lock);
234 return NULL;
237 WINMM_InitDevice(device, mmdevice,
238 WINMM_MakeHWAVE(mmdevice_index, is_out, i));
239 EnterCriticalSection(&device->lock);
240 }else
241 EnterCriticalSection(&device->lock);
243 if(!device->open){
244 LeaveCriticalSection(&mmdevice->lock);
245 device->open = TRUE;
246 TRACE("Found free device: mmdevice: %u, device id: %u\n",
247 mmdevice_index, i);
248 return device;
251 LeaveCriticalSection(&device->lock);
254 LeaveCriticalSection(&mmdevice->lock);
256 TRACE("All devices in use: mmdevice: %u\n", mmdevice_index);
258 return NULL;
261 static inline BOOL WINMM_ValidateAndLock(WINMM_Device *device)
263 if(!device)
264 return FALSE;
266 EnterCriticalSection(&device->lock);
268 if(!device->open){
269 LeaveCriticalSection(&device->lock);
270 return FALSE;
273 return TRUE;
276 static WINMM_Device *WINMM_GetDeviceFromHWAVE(HWAVE hwave)
278 WINMM_MMDevice *mmdevice;
279 WINMM_Device *device;
280 UINT mmdevice_index, device_index, junk;
281 BOOL is_out;
283 WINMM_DecomposeHWAVE(hwave, &mmdevice_index, &is_out, &device_index, &junk);
285 if(junk != 0x1)
286 return NULL;
288 if(mmdevice_index >= (is_out ? g_outmmdevices_count : g_inmmdevices_count))
289 return NULL;
291 if(is_out)
292 mmdevice = &g_out_mmdevices[mmdevice_index];
293 else
294 mmdevice = &g_in_mmdevices[mmdevice_index];
296 EnterCriticalSection(&mmdevice->lock);
298 device = mmdevice->devices[device_index];
300 LeaveCriticalSection(&mmdevice->lock);
302 return device;
305 /* Note: NotifyClient should never be called while holding the device lock
306 * since the client may call wave* functions from within the callback. */
307 static inline void WINMM_NotifyClient(WINMM_CBInfo *info, WORD msg, DWORD_PTR param1,
308 DWORD_PTR param2)
310 DriverCallback(info->callback, info->flags, (HDRVR)info->hwave,
311 msg, info->user, param1, param2);
314 static HRESULT WINMM_GetFriendlyName(IMMDevice *device, WCHAR *out,
315 UINT outlen)
317 IPropertyStore *ps;
318 PROPVARIANT var;
319 HRESULT hr;
321 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
322 if(FAILED(hr))
323 return hr;
325 PropVariantInit(&var);
327 hr = IPropertyStore_GetValue(ps,
328 (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &var);
329 if(FAILED(hr)){
330 IPropertyStore_Release(ps);
331 return hr;
334 lstrcpynW(out, var.u.pwszVal, outlen);
336 PropVariantClear(&var);
338 IPropertyStore_Release(ps);
340 return S_OK;
343 static HRESULT WINMM_TestFormat(IAudioClient *client, DWORD rate, DWORD depth,
344 WORD channels)
346 WAVEFORMATEX fmt, *junk;
347 HRESULT hr;
349 fmt.wFormatTag = WAVE_FORMAT_PCM;
350 fmt.nChannels = channels;
351 fmt.nSamplesPerSec = rate;
352 fmt.wBitsPerSample = depth;
353 fmt.nBlockAlign = (channels * depth) / 8;
354 fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
355 fmt.cbSize = 0;
357 hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED,
358 &fmt, &junk);
359 if(SUCCEEDED(hr))
360 CoTaskMemFree(junk);
362 return hr;
365 static struct _TestFormat {
366 DWORD flag;
367 DWORD rate;
368 DWORD depth;
369 WORD channels;
370 } formats_to_test[] = {
371 { WAVE_FORMAT_1M08, 11025, 8, 1 },
372 { WAVE_FORMAT_1M16, 11025, 16, 1 },
373 { WAVE_FORMAT_1S08, 11025, 8, 2 },
374 { WAVE_FORMAT_1S16, 11025, 16, 2 },
375 { WAVE_FORMAT_2M08, 22050, 8, 1 },
376 { WAVE_FORMAT_2M16, 22050, 16, 1 },
377 { WAVE_FORMAT_2S08, 22050, 8, 2 },
378 { WAVE_FORMAT_2S16, 22050, 16, 2 },
379 { WAVE_FORMAT_4M08, 44100, 8, 1 },
380 { WAVE_FORMAT_4M16, 44100, 16, 1 },
381 { WAVE_FORMAT_4S08, 44100, 8, 2 },
382 { WAVE_FORMAT_4S16, 44100, 16, 2 },
383 { WAVE_FORMAT_48M08, 48000, 8, 1 },
384 { WAVE_FORMAT_48M16, 48000, 16, 1 },
385 { WAVE_FORMAT_48S08, 48000, 8, 2 },
386 { WAVE_FORMAT_48S16, 48000, 16, 2 },
387 { WAVE_FORMAT_96M08, 96000, 8, 1 },
388 { WAVE_FORMAT_96M16, 96000, 16, 1 },
389 { WAVE_FORMAT_96S08, 96000, 8, 2 },
390 { WAVE_FORMAT_96S16, 96000, 16, 2 },
394 static DWORD WINMM_GetSupportedFormats(IMMDevice *device)
396 DWORD flags = 0;
397 HRESULT hr;
398 struct _TestFormat *fmt;
399 IAudioClient *client;
401 hr = IMMDevice_Activate(device, &IID_IAudioClient,
402 CLSCTX_INPROC_SERVER, NULL, (void**)&client);
403 if(FAILED(hr))
404 return 0;
406 for(fmt = formats_to_test; fmt->flag; ++fmt){
407 hr = WINMM_TestFormat(client, fmt->rate, fmt->depth, fmt->channels);
408 if(hr == S_OK)
409 flags |= fmt->flag;
412 IAudioClient_Release(client);
414 return flags;
417 static HRESULT WINMM_InitMMDevice(EDataFlow flow, IMMDevice *device,
418 WINMM_MMDevice *dev, UINT index)
420 HRESULT hr;
422 if(flow == eRender){
423 dev->out_caps.wMid = 0xFF;
424 dev->out_caps.wPid = 0xFF;
425 dev->out_caps.vDriverVersion = 0x00010001;
426 dev->out_caps.dwFormats = WINMM_GetSupportedFormats(device);
427 dev->out_caps.wReserved1 = 0;
428 dev->out_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
429 WAVECAPS_SAMPLEACCURATE;
430 dev->out_caps.wChannels = 2;
431 dev->out_caps.szPname[0] = '\0';
433 hr = WINMM_GetFriendlyName(device, dev->out_caps.szPname,
434 sizeof(dev->out_caps.szPname) /
435 sizeof(*dev->out_caps.szPname));
436 if(FAILED(hr))
437 return hr;
438 }else{
439 dev->in_caps.wMid = 0xFF;
440 dev->in_caps.wPid = 0xFF;
441 dev->in_caps.vDriverVersion = 0x00010001;
442 dev->in_caps.dwFormats = WINMM_GetSupportedFormats(device);
443 dev->in_caps.wReserved1 = 0;
444 dev->in_caps.wChannels = 2;
445 dev->in_caps.szPname[0] = '\0';
447 hr = WINMM_GetFriendlyName(device, dev->in_caps.szPname,
448 sizeof(dev->in_caps.szPname) /
449 sizeof(*dev->in_caps.szPname));
450 if(FAILED(hr))
451 return hr;
454 hr = IMMDevice_GetId(device, &dev->dev_id);
455 if(FAILED(hr))
456 return hr;
458 CoCreateGuid(&dev->session);
460 InitializeCriticalSection(&dev->lock);
461 dev->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM_Device.lock");
463 return S_OK;
466 static HRESULT WINMM_EnumDevices(WINMM_MMDevice **devices, UINT *devcount,
467 EDataFlow flow, IMMDeviceEnumerator *devenum)
469 IMMDeviceCollection *devcoll;
470 HRESULT hr;
472 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flow,
473 DEVICE_STATE_ACTIVE, &devcoll);
474 if(FAILED(hr))
475 return hr;
477 hr = IMMDeviceCollection_GetCount(devcoll, devcount);
478 if(FAILED(hr)){
479 IMMDeviceCollection_Release(devcoll);
480 return hr;
483 if(*devcount > 0){
484 UINT n, count;
485 IMMDevice *def_dev = NULL;
487 *devices = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
488 sizeof(WINMM_MMDevice) * (*devcount));
489 if(!*devices){
490 IMMDeviceCollection_Release(devcoll);
491 return E_OUTOFMEMORY;
494 count = 0;
496 /* make sure that device 0 is the default device */
497 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum,
498 flow, eConsole, &def_dev);
499 if(SUCCEEDED(hr)){
500 WINMM_InitMMDevice(flow, def_dev, &(*devices)[0], 0);
501 count = 1;
504 for(n = 0; n < *devcount; ++n){
505 IMMDevice *device;
507 hr = IMMDeviceCollection_Item(devcoll, n, &device);
508 if(SUCCEEDED(hr)){
509 if(device != def_dev){
510 WINMM_InitMMDevice(flow, device, &(*devices)[count], count);
511 ++count;
514 IMMDevice_Release(device);
518 if(def_dev)
519 IMMDevice_Release(def_dev);
521 *devcount = count;
524 IMMDeviceCollection_Release(devcoll);
526 return S_OK;
529 static HRESULT WINMM_InitMMDevices(void)
531 HRESULT hr, init_hr;
532 IMMDeviceEnumerator *devenum = NULL;
534 if(g_outmmdevices_count || g_inmmdevices_count)
535 return S_FALSE;
537 init_hr = CoInitialize(NULL);
539 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
540 CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&devenum);
541 if(FAILED(hr))
542 goto exit;
544 hr = WINMM_EnumDevices(&g_out_mmdevices, &g_outmmdevices_count, eRender, devenum);
545 if(FAILED(hr)){
546 g_outmmdevices_count = 0;
547 g_inmmdevices_count = 0;
548 goto exit;
551 hr = WINMM_EnumDevices(&g_in_mmdevices, &g_inmmdevices_count, eCapture, devenum);
552 if(FAILED(hr)){
553 g_inmmdevices_count = 0;
554 goto exit;
557 exit:
558 if(devenum)
559 IMMDeviceEnumerator_Release(devenum);
560 if(SUCCEEDED(init_hr))
561 CoUninitialize();
563 return hr;
566 static inline BOOL WINMM_IsMapper(UINT device)
568 return (device == WAVE_MAPPER || device == (UINT16)WAVE_MAPPER);
571 static MMRESULT WINMM_TryDeviceMapping(WINMM_OpenInfo *info, WORD channels,
572 DWORD freq, DWORD bits_per_samp, BOOL is_out)
574 WINMM_Device *device;
575 WAVEFORMATEX target;
576 MMRESULT mr;
577 UINT i;
579 TRACE("format: %u, channels: %u, sample rate: %u, bit depth: %u\n",
580 WAVE_FORMAT_PCM, channels, freq, bits_per_samp);
582 target.wFormatTag = WAVE_FORMAT_PCM;
583 target.nChannels = channels;
584 target.nSamplesPerSec = freq;
585 target.wBitsPerSample = bits_per_samp;
586 target.nBlockAlign = (target.nChannels * target.wBitsPerSample) / 8;
587 target.nAvgBytesPerSec = target.nSamplesPerSec * target.nBlockAlign;
588 target.cbSize = 0;
590 if(is_out)
591 mr = acmStreamOpen(NULL, NULL, info->format, &target, NULL, 0,
592 0, ACM_STREAMOPENF_QUERY);
593 else
594 mr = acmStreamOpen(NULL, NULL, &target, info->format, NULL, 0,
595 0, ACM_STREAMOPENF_QUERY);
596 if(mr != MMSYSERR_NOERROR)
597 return mr;
599 /* ACM can convert from src->dst, so try to find a device
600 * that supports dst */
601 if(is_out){
602 if(WINMM_IsMapper(info->req_device)){
603 for(i = 0; i < g_outmmdevices_count; ++i){
604 WINMM_OpenInfo l_info = *info;
605 l_info.req_device = i;
606 l_info.format = &target;
607 mr = WOD_Open(&l_info);
608 if(mr == MMSYSERR_NOERROR){
609 info->handle = l_info.handle;
610 break;
613 }else{
614 WINMM_OpenInfo l_info = *info;
615 l_info.flags &= ~WAVE_MAPPED;
616 l_info.format = &target;
617 mr = WOD_Open(&l_info);
618 if(mr == MMSYSERR_NOERROR)
619 info->handle = l_info.handle;
621 }else{
622 if(WINMM_IsMapper(info->req_device)){
623 for(i = 0; i < g_inmmdevices_count; ++i){
624 WINMM_OpenInfo l_info = *info;
625 l_info.req_device = i;
626 l_info.format = &target;
627 mr = WID_Open(&l_info);
628 if(mr == MMSYSERR_NOERROR){
629 info->handle = l_info.handle;
630 break;
633 }else{
634 WINMM_OpenInfo l_info = *info;
635 l_info.flags &= ~WAVE_MAPPED;
636 l_info.format = &target;
637 mr = WID_Open(&l_info);
638 if(mr == MMSYSERR_NOERROR)
639 info->handle = l_info.handle;
642 if(mr != MMSYSERR_NOERROR)
643 return WAVERR_BADFORMAT;
645 device = WINMM_GetDeviceFromHWAVE(info->handle);
646 if(!device)
647 return MMSYSERR_INVALHANDLE;
649 /* set up the ACM stream */
650 if(is_out)
651 mr = acmStreamOpen(&device->acm_handle, NULL, info->format, &target,
652 NULL, 0, 0, 0);
653 else
654 mr = acmStreamOpen(&device->acm_handle, NULL, &target, info->format,
655 NULL, 0, 0, 0);
656 if(mr != MMSYSERR_NOERROR){
657 if(is_out)
658 WOD_Close((HWAVEOUT)info->handle);
659 else
660 WID_Close((HWAVEIN)info->handle);
661 return mr;
664 TRACE("Success\n");
665 return MMSYSERR_NOERROR;
668 static MMRESULT WINMM_MapDevice(WINMM_OpenInfo *info, BOOL is_out)
670 UINT i;
671 MMRESULT mr;
672 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)info->format;
674 TRACE("(%p, %d)\n", info, is_out);
676 /* try to find a direct match */
677 if(is_out){
678 WINMM_OpenInfo l_info = *info;
679 if(WINMM_IsMapper(info->req_device)){
680 for(i = 0; i < g_outmmdevices_count; ++i){
681 l_info.req_device = i;
682 mr = WOD_Open(&l_info);
683 if(mr == MMSYSERR_NOERROR){
684 info->handle = l_info.handle;
685 return mr;
688 }else{
689 l_info.flags &= ~WAVE_MAPPED;
690 mr = WOD_Open(&l_info);
691 if(mr == MMSYSERR_NOERROR){
692 info->handle = l_info.handle;
693 return mr;
696 }else{
697 WINMM_OpenInfo l_info = *info;
698 if(WINMM_IsMapper(info->req_device)){
699 for(i = 0; i < g_inmmdevices_count; ++i){
700 l_info.req_device = i;
701 mr = WID_Open(&l_info);
702 if(mr == MMSYSERR_NOERROR){
703 info->handle = l_info.handle;
704 return mr;
707 }else{
708 l_info.flags &= ~WAVE_MAPPED;
709 mr = WID_Open(&l_info);
710 if(mr == MMSYSERR_NOERROR){
711 info->handle = l_info.handle;
712 return mr;
717 /* no direct match, so set up the ACM stream */
718 if(info->format->wFormatTag != WAVE_FORMAT_PCM &&
719 !(info->format->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
720 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
721 /* convert to PCM format if it's not already */
722 mr = WINMM_TryDeviceMapping(info, info->format->nChannels,
723 info->format->nSamplesPerSec, 16, is_out);
724 if(mr == MMSYSERR_NOERROR)
725 return mr;
727 mr = WINMM_TryDeviceMapping(info, info->format->nChannels,
728 info->format->nSamplesPerSec, 8, is_out);
729 if(mr == MMSYSERR_NOERROR)
730 return mr;
731 }else{
732 WORD channels;
734 /* first try just changing bit depth and channels */
735 channels = info->format->nChannels;
736 mr = WINMM_TryDeviceMapping(info, channels,
737 info->format->nSamplesPerSec, 16, is_out);
738 if(mr == MMSYSERR_NOERROR)
739 return mr;
740 mr = WINMM_TryDeviceMapping(info, channels,
741 info->format->nSamplesPerSec, 8, is_out);
742 if(mr == MMSYSERR_NOERROR)
743 return mr;
745 channels = (channels == 2) ? 1 : 2;
746 mr = WINMM_TryDeviceMapping(info, channels,
747 info->format->nSamplesPerSec, 16, is_out);
748 if(mr == MMSYSERR_NOERROR)
749 return mr;
750 mr = WINMM_TryDeviceMapping(info, channels,
751 info->format->nSamplesPerSec, 8, is_out);
752 if(mr == MMSYSERR_NOERROR)
753 return mr;
755 /* that didn't work, so now try different sample rates */
756 channels = info->format->nChannels;
757 mr = WINMM_TryDeviceMapping(info, channels, 96000, 16, is_out);
758 if(mr == MMSYSERR_NOERROR)
759 return mr;
760 mr = WINMM_TryDeviceMapping(info, channels, 48000, 16, is_out);
761 if(mr == MMSYSERR_NOERROR)
762 return mr;
763 mr = WINMM_TryDeviceMapping(info, channels, 44100, 16, is_out);
764 if(mr == MMSYSERR_NOERROR)
765 return mr;
766 mr = WINMM_TryDeviceMapping(info, channels, 22050, 16, is_out);
767 if(mr == MMSYSERR_NOERROR)
768 return mr;
769 mr = WINMM_TryDeviceMapping(info, channels, 11025, 16, is_out);
770 if(mr == MMSYSERR_NOERROR)
771 return mr;
773 channels = (channels == 2) ? 1 : 2;
774 mr = WINMM_TryDeviceMapping(info, channels, 96000, 16, is_out);
775 if(mr == MMSYSERR_NOERROR)
776 return mr;
777 mr = WINMM_TryDeviceMapping(info, channels, 48000, 16, is_out);
778 if(mr == MMSYSERR_NOERROR)
779 return mr;
780 mr = WINMM_TryDeviceMapping(info, channels, 44100, 16, is_out);
781 if(mr == MMSYSERR_NOERROR)
782 return mr;
783 mr = WINMM_TryDeviceMapping(info, channels, 22050, 16, is_out);
784 if(mr == MMSYSERR_NOERROR)
785 return mr;
786 mr = WINMM_TryDeviceMapping(info, channels, 11025, 16, is_out);
787 if(mr == MMSYSERR_NOERROR)
788 return mr;
790 channels = info->format->nChannels;
791 mr = WINMM_TryDeviceMapping(info, channels, 96000, 8, is_out);
792 if(mr == MMSYSERR_NOERROR)
793 return mr;
794 mr = WINMM_TryDeviceMapping(info, channels, 48000, 8, is_out);
795 if(mr == MMSYSERR_NOERROR)
796 return mr;
797 mr = WINMM_TryDeviceMapping(info, channels, 44100, 8, is_out);
798 if(mr == MMSYSERR_NOERROR)
799 return mr;
800 mr = WINMM_TryDeviceMapping(info, channels, 22050, 8, is_out);
801 if(mr == MMSYSERR_NOERROR)
802 return mr;
803 mr = WINMM_TryDeviceMapping(info, channels, 11025, 8, is_out);
804 if(mr == MMSYSERR_NOERROR)
805 return mr;
807 channels = (channels == 2) ? 1 : 2;
808 mr = WINMM_TryDeviceMapping(info, channels, 96000, 8, is_out);
809 if(mr == MMSYSERR_NOERROR)
810 return mr;
811 mr = WINMM_TryDeviceMapping(info, channels, 48000, 8, is_out);
812 if(mr == MMSYSERR_NOERROR)
813 return mr;
814 mr = WINMM_TryDeviceMapping(info, channels, 44100, 8, is_out);
815 if(mr == MMSYSERR_NOERROR)
816 return mr;
817 mr = WINMM_TryDeviceMapping(info, channels, 22050, 8, is_out);
818 if(mr == MMSYSERR_NOERROR)
819 return mr;
820 mr = WINMM_TryDeviceMapping(info, channels, 11025, 8, is_out);
821 if(mr == MMSYSERR_NOERROR)
822 return mr;
825 WARN("Unable to find compatible device!\n");
826 return WAVERR_BADFORMAT;
829 static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_MMDevice *mmdevice,
830 WINMM_OpenInfo *info)
832 WAVEFORMATEX *closer_fmt = NULL, fmt, *passed_fmt;
833 LRESULT ret = MMSYSERR_ERROR;
834 HRESULT hr;
836 hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id,
837 &device->device);
838 if(FAILED(hr)){
839 WARN("Device %s (%s) unavailable: %08x\n",
840 wine_dbgstr_w(mmdevice->dev_id),
841 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
842 goto error;
845 hr = IMMDevice_Activate(device->device, &IID_IAudioClient,
846 CLSCTX_INPROC_SERVER, NULL, (void**)&device->client);
847 if(FAILED(hr)){
848 WARN("Activate failed: %08x\n", hr);
849 goto error;
852 if(info->format->wFormatTag == WAVE_FORMAT_PCM){
853 /* we aren't guaranteed that the struct in lpFormat is a full
854 * WAVEFORMATEX struct, which IAC::IsFormatSupported requires */
855 passed_fmt = &fmt;
856 memcpy(passed_fmt, info->format, sizeof(PCMWAVEFORMAT));
857 passed_fmt->cbSize = 0;
858 if(fmt.wBitsPerSample % 8 != 0){
859 WARN("Fixing bad wBitsPerSample (%u)\n", fmt.wBitsPerSample);
860 fmt.wBitsPerSample = (fmt.wBitsPerSample + 7) & ~7;
862 /* winmm ignores broken blockalign and avgbytes */
863 if(fmt.nBlockAlign != fmt.nChannels * fmt.wBitsPerSample/8){
864 WARN("Fixing bad nBlockAlign (%u)\n", fmt.nBlockAlign);
865 fmt.nBlockAlign = fmt.nChannels * fmt.wBitsPerSample/8;
867 if (fmt.nAvgBytesPerSec != fmt.nSamplesPerSec * fmt.nBlockAlign) {
868 WARN("Fixing bad nAvgBytesPerSec (%u)\n", fmt.nAvgBytesPerSec);
869 fmt.nAvgBytesPerSec = fmt.nSamplesPerSec * fmt.nBlockAlign;
871 }else
872 passed_fmt = info->format;
874 hr = IAudioClient_IsFormatSupported(device->client,
875 AUDCLNT_SHAREMODE_SHARED, passed_fmt, &closer_fmt);
876 if(closer_fmt)
877 CoTaskMemFree(closer_fmt);
878 if(FAILED(hr) && hr != AUDCLNT_E_UNSUPPORTED_FORMAT){
879 WARN("IsFormatSupported failed: %08x\n", hr);
880 goto error;
882 if(hr == S_FALSE || hr == AUDCLNT_E_UNSUPPORTED_FORMAT){
883 ret = WAVERR_BADFORMAT;
884 goto error;
886 if(info->flags & WAVE_FORMAT_QUERY){
887 ret = MMSYSERR_NOERROR;
888 goto error;
891 /* buffer size = 10 * 100000 (100 ns) = 0.1 seconds */
892 hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED,
893 AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
894 10 * 100000, 50000, passed_fmt, &device->parent->session);
895 if(FAILED(hr)){
896 WARN("Initialize failed: %08x\n", hr);
897 goto error;
900 hr = IAudioClient_GetService(device->client, &IID_IAudioClock,
901 (void**)&device->clock);
902 if(FAILED(hr)){
903 WARN("GetService failed: %08x\n", hr);
904 goto error;
907 if(!device->event){
908 device->event = CreateEventW(NULL, FALSE, FALSE, NULL);
909 if(!device->event){
910 WARN("CreateEvent failed: %08x\n", hr);
911 goto error;
914 /* As the devices thread is waiting on g_device_handles, it can
915 * only be modified from within this same thread. */
916 if(g_device_handles){
917 g_device_handles = HeapReAlloc(GetProcessHeap(), 0, g_device_handles,
918 sizeof(HANDLE) * (g_devhandle_count + 1));
919 g_handle_devices = HeapReAlloc(GetProcessHeap(), 0, g_handle_devices,
920 sizeof(WINMM_Device *) * (g_devhandle_count + 1));
921 }else{
922 g_device_handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE));
923 g_handle_devices = HeapAlloc(GetProcessHeap(), 0,
924 sizeof(WINMM_Device *));
926 g_device_handles[g_devhandle_count] = device->event;
927 g_handle_devices[g_devhandle_count] = device;
928 ++g_devhandle_count;
931 hr = IAudioClient_SetEventHandle(device->client, device->event);
932 if(FAILED(hr)){
933 WARN("SetEventHandle failed: %08x\n", hr);
934 goto error;
937 device->bytes_per_frame = passed_fmt->nBlockAlign;
938 device->samples_per_sec = passed_fmt->nSamplesPerSec;
940 device->played_frames = 0;
941 device->last_clock_pos = 0;
942 device->ofs_bytes = 0;
943 device->loop_counter = 0;
944 device->stopped = TRUE;
945 device->first = device->last = device->playing = device->loop_start = NULL;
947 device->cb_info.flags = HIWORD(info->flags & CALLBACK_TYPEMASK);
948 device->cb_info.callback = info->callback;
949 device->cb_info.user = info->cb_user;
950 device->cb_info.hwave = device->handle;
952 info->handle = device->handle;
954 return MMSYSERR_NOERROR;
956 error:
957 if(device->client){
958 IAudioClient_Release(device->client);
959 device->client = NULL;
961 if(device->device){
962 IMMDevice_Release(device->device);
963 device->device = NULL;
966 return ret;
969 static LRESULT WOD_Open(WINMM_OpenInfo *info)
971 WINMM_MMDevice *mmdevice;
972 WINMM_Device *device = NULL;
973 LRESULT ret = MMSYSERR_ERROR;
974 HRESULT hr;
976 TRACE("(%u, %p, %08x)\n", info->req_device, info, info->flags);
978 if(WINMM_IsMapper(info->req_device) || (info->flags & WAVE_MAPPED))
979 return WINMM_MapDevice(info, TRUE);
981 if(info->req_device >= g_outmmdevices_count)
982 return MMSYSERR_BADDEVICEID;
984 mmdevice = &g_out_mmdevices[info->req_device];
986 if(!mmdevice->out_caps.szPname[0])
987 return MMSYSERR_NOTENABLED;
989 device = WINMM_FindUnusedDevice(TRUE, info->req_device);
990 if(!device)
991 return MMSYSERR_ALLOCATED;
993 ret = WINMM_OpenDevice(device, mmdevice, info);
994 if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
995 goto error;
996 ret = MMSYSERR_ERROR;
998 hr = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
999 (void**)&device->render);
1000 if(FAILED(hr)){
1001 WARN("GetService(RenderClient) failed: %08x\n", hr);
1002 goto error;
1005 hr = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
1006 (void**)&device->volume);
1007 if(FAILED(hr)){
1008 WARN("GetService(StreamVolume) failed: %08x\n", hr);
1009 goto error;
1012 LeaveCriticalSection(&device->lock);
1014 return MMSYSERR_NOERROR;
1016 error:
1017 if(device->device){
1018 IMMDevice_Release(device->device);
1019 device->device = NULL;
1021 if(device->client){
1022 IAudioClient_Release(device->client);
1023 device->client = NULL;
1025 if(device->render){
1026 IAudioRenderClient_Release(device->render);
1027 device->render = NULL;
1029 if(device->volume){
1030 IAudioStreamVolume_Release(device->volume);
1031 device->volume = NULL;
1033 if(device->clock){
1034 IAudioClock_Release(device->clock);
1035 device->clock = NULL;
1037 device->open = FALSE;
1038 LeaveCriticalSection(&device->lock);
1039 return ret;
1042 static LRESULT WID_Open(WINMM_OpenInfo *info)
1044 WINMM_MMDevice *mmdevice;
1045 WINMM_Device *device = NULL;
1046 LRESULT ret = MMSYSERR_ERROR;
1047 HRESULT hr;
1049 TRACE("(%u, %p, %08x)\n", info->req_device, info, info->flags);
1051 if(WINMM_IsMapper(info->req_device) || info->flags & WAVE_MAPPED)
1052 return WINMM_MapDevice(info, FALSE);
1054 if(info->req_device >= g_inmmdevices_count)
1055 return MMSYSERR_BADDEVICEID;
1057 mmdevice = &g_in_mmdevices[info->req_device];
1059 if(!mmdevice->in_caps.szPname[0])
1060 return MMSYSERR_NOTENABLED;
1062 device = WINMM_FindUnusedDevice(FALSE, info->req_device);
1063 if(!device)
1064 return MMSYSERR_ALLOCATED;
1066 ret = WINMM_OpenDevice(device, mmdevice, info);
1067 if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
1068 goto error;
1069 ret = MMSYSERR_ERROR;
1071 hr = IAudioClient_GetService(device->client, &IID_IAudioCaptureClient,
1072 (void**)&device->capture);
1073 if(FAILED(hr)){
1074 WARN("GetService failed: %08x\n", hr);
1075 goto error;
1078 LeaveCriticalSection(&device->lock);
1080 return MMSYSERR_NOERROR;
1082 error:
1083 if(device->device){
1084 IMMDevice_Release(device->device);
1085 device->device = NULL;
1087 if(device->client){
1088 IAudioClient_Release(device->client);
1089 device->client = NULL;
1091 if(device->capture){
1092 IAudioCaptureClient_Release(device->capture);
1093 device->capture = NULL;
1095 if(device->clock){
1096 IAudioClock_Release(device->clock);
1097 device->clock = NULL;
1099 device->open = FALSE;
1100 LeaveCriticalSection(&device->lock);
1101 return ret;
1104 static HRESULT WINMM_CloseDevice(WINMM_Device *device)
1106 device->open = FALSE;
1108 if(!device->stopped){
1109 IAudioClient_Stop(device->client);
1110 device->stopped = TRUE;
1113 if(device->acm_handle){
1114 acmStreamClose(device->acm_handle, 0);
1115 device->acm_handle = NULL;
1118 IMMDevice_Release(device->device);
1119 device->device = NULL;
1121 IAudioClient_Release(device->client);
1122 device->client = NULL;
1124 IAudioClock_Release(device->clock);
1125 device->clock = NULL;
1127 return S_OK;
1130 static LRESULT WOD_Close(HWAVEOUT hwave)
1132 WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1134 TRACE("(%p)\n", hwave);
1136 if(!WINMM_ValidateAndLock(device))
1137 return MMSYSERR_INVALHANDLE;
1139 WINMM_CloseDevice(device);
1141 IAudioRenderClient_Release(device->render);
1142 device->render = NULL;
1144 IAudioStreamVolume_Release(device->volume);
1145 device->volume = NULL;
1147 LeaveCriticalSection(&device->lock);
1149 return MMSYSERR_NOERROR;
1152 static LRESULT WID_Close(HWAVEIN hwave)
1154 WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1156 TRACE("(%p)\n", hwave);
1158 if(!WINMM_ValidateAndLock(device))
1159 return MMSYSERR_INVALHANDLE;
1161 WINMM_CloseDevice(device);
1163 IAudioCaptureClient_Release(device->capture);
1164 device->capture = NULL;
1166 LeaveCriticalSection(&device->lock);
1168 return MMSYSERR_NOERROR;
1171 static LRESULT WINMM_PrepareHeader(HWAVE hwave, WAVEHDR *header)
1173 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1175 TRACE("(%p, %p)\n", hwave, header);
1177 if(!WINMM_ValidateAndLock(device))
1178 return MMSYSERR_INVALHANDLE;
1180 if(device->render && device->acm_handle){
1181 ACMSTREAMHEADER *ash;
1182 DWORD size;
1183 MMRESULT mr;
1185 mr = acmStreamSize(device->acm_handle, header->dwBufferLength, &size,
1186 ACM_STREAMSIZEF_SOURCE);
1187 if(mr != MMSYSERR_NOERROR){
1188 LeaveCriticalSection(&device->lock);
1189 return mr;
1192 ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + size);
1193 if(!ash){
1194 LeaveCriticalSection(&device->lock);
1195 return MMSYSERR_NOMEM;
1198 ash->cbStruct = sizeof(*ash);
1199 ash->fdwStatus = 0;
1200 ash->dwUser = (DWORD_PTR)header;
1201 ash->pbSrc = (BYTE*)header->lpData;
1202 ash->cbSrcLength = header->dwBufferLength;
1203 ash->dwSrcUser = header->dwUser;
1204 ash->pbDst = (BYTE*)ash + sizeof(ACMSTREAMHEADER);
1205 ash->cbDstLength = size;
1206 ash->dwDstUser = 0;
1208 mr = acmStreamPrepareHeader(device->acm_handle, ash, 0);
1209 if(mr != MMSYSERR_NOERROR){
1210 HeapFree(GetProcessHeap(), 0, ash);
1211 LeaveCriticalSection(&device->lock);
1212 return mr;
1215 header->reserved = (DWORD_PTR)ash;
1218 LeaveCriticalSection(&device->lock);
1220 header->dwFlags |= WHDR_PREPARED;
1221 header->dwFlags &= ~WHDR_DONE;
1223 return MMSYSERR_NOERROR;
1226 static LRESULT WINMM_UnprepareHeader(HWAVE hwave, WAVEHDR *header)
1228 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1230 TRACE("(%p, %p)\n", hwave, header);
1232 if(!WINMM_ValidateAndLock(device))
1233 return MMSYSERR_INVALHANDLE;
1235 if(device->render && device->acm_handle){
1236 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1238 acmStreamUnprepareHeader(device->acm_handle, ash, 0);
1240 HeapFree(GetProcessHeap(), 0, ash);
1243 LeaveCriticalSection(&device->lock);
1245 header->dwFlags &= ~WHDR_PREPARED;
1246 header->dwFlags |= WHDR_DONE;
1248 return MMSYSERR_NOERROR;
1251 static UINT32 WINMM_HeaderLenBytes(WINMM_Device *device, WAVEHDR *header)
1253 if(device->acm_handle){
1254 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1255 return ash->cbDstLengthUsed;
1258 return header->dwBufferLength;
1261 static UINT32 WINMM_HeaderLenFrames(WINMM_Device *device, WAVEHDR *header)
1263 return WINMM_HeaderLenBytes(device, header) / device->bytes_per_frame;
1266 static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device)
1268 HRESULT hr;
1269 WAVEHDR *first = device->first, *queue = first, *last = NULL;
1270 UINT64 clock_freq, clock_pos, clock_frames;
1271 UINT32 nloops, queue_frames = 0;
1273 hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
1274 if(FAILED(hr)){
1275 WARN("GetFrequency failed: %08x\n", hr);
1276 return NULL;
1279 hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
1280 if(FAILED(hr)){
1281 WARN("GetPosition failed: %08x\n", hr);
1282 return NULL;
1285 clock_frames = (clock_pos * device->samples_per_sec) / clock_freq;
1287 nloops = device->loop_counter;
1288 while(queue &&
1289 (queue_frames += WINMM_HeaderLenFrames(device, queue)) <=
1290 clock_frames - device->last_clock_pos){
1291 if(!nloops){
1292 last = queue;
1293 device->last_clock_pos += queue_frames;
1294 queue_frames = 0;
1295 queue = device->first = queue->lpNext;
1296 }else{
1297 if(queue->dwFlags & WHDR_BEGINLOOP){
1298 if(queue->dwFlags & WHDR_ENDLOOP)
1299 --nloops;
1300 else
1301 queue = queue->lpNext;
1302 }else if(queue->dwFlags & WHDR_ENDLOOP){
1303 queue = device->loop_start;
1304 --nloops;
1309 if(last){
1310 last->lpNext = NULL;
1311 return first;
1312 }else
1313 return NULL;
1316 static void WOD_PushData(WINMM_Device *device)
1318 WINMM_CBInfo cb_info;
1319 HRESULT hr;
1320 UINT32 pad, bufsize, avail_frames, queue_frames, written, ofs;
1321 UINT32 queue_bytes, nloops;
1322 BYTE *data;
1323 WAVEHDR *queue, *first = NULL;
1325 TRACE("(%p)\n", device->handle);
1327 EnterCriticalSection(&device->lock);
1329 if(!device->device)
1330 goto exit;
1332 if(!device->first){
1333 device->stopped = TRUE;
1334 device->last_clock_pos = 0;
1335 IAudioClient_Stop(device->client);
1336 IAudioClient_Reset(device->client);
1337 goto exit;
1340 hr = IAudioClient_GetBufferSize(device->client, &bufsize);
1341 if(FAILED(hr)){
1342 WARN("GetBufferSize failed: %08x\n", hr);
1343 goto exit;
1346 hr = IAudioClient_GetCurrentPadding(device->client, &pad);
1347 if(FAILED(hr)){
1348 WARN("GetCurrentPadding failed: %08x\n", hr);
1349 goto exit;
1352 first = WOD_MarkDoneHeaders(device);
1354 /* determine which is larger between the available buffer size and
1355 * the amount of data left in the queue */
1356 avail_frames = bufsize - pad;
1358 queue = device->playing;
1359 ofs = device->ofs_bytes;
1360 queue_frames = 0;
1361 nloops = 0;
1362 while(queue && queue_frames < avail_frames){
1363 queue_bytes = WINMM_HeaderLenBytes(device, queue);
1364 queue_frames += (queue_bytes - ofs) / device->bytes_per_frame;
1365 ofs = 0;
1367 if(queue->dwFlags & WHDR_ENDLOOP && nloops < device->loop_counter){
1368 queue = device->loop_start;
1369 ++nloops;
1370 }else
1371 queue = queue->lpNext;
1374 if(queue_frames < avail_frames)
1375 avail_frames = queue_frames;
1376 if(avail_frames == 0)
1377 goto exit;
1379 hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
1380 if(FAILED(hr)){
1381 WARN("GetBuffer failed: %08x\n", hr);
1382 goto exit;
1385 written = 0;
1386 while(device->playing && written < avail_frames){
1387 UINT32 copy_frames, copy_bytes;
1388 BYTE *queue_data;
1390 queue = device->playing;
1392 if(device->acm_handle){
1393 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)queue->reserved;
1394 queue_bytes = ash->cbDstLengthUsed;
1395 queue_data = ash->pbDst;
1396 }else{
1397 queue_bytes = queue->dwBufferLength;
1398 queue_data = (BYTE*)queue->lpData;
1401 queue_frames = (queue_bytes - device->ofs_bytes) /
1402 device->bytes_per_frame;
1404 copy_frames = queue_frames < (avail_frames - written) ?
1405 queue_frames : avail_frames - written;
1406 copy_bytes = copy_frames * device->bytes_per_frame;
1408 memcpy(data, queue_data + device->ofs_bytes, copy_bytes);
1410 data += copy_bytes;
1411 written += copy_frames;
1412 device->ofs_bytes += copy_bytes;
1414 if(device->ofs_bytes >= queue_bytes){
1415 device->ofs_bytes = 0;
1417 if(!(queue->dwFlags & (WHDR_BEGINLOOP | WHDR_ENDLOOP)))
1418 device->playing = queue->lpNext;
1419 else{
1420 if(queue->dwFlags & WHDR_BEGINLOOP){
1421 device->loop_start = device->playing;
1422 device->playing = queue->lpNext;
1423 device->loop_counter = queue->dwLoops;
1425 if(queue->dwFlags & WHDR_ENDLOOP){
1426 --device->loop_counter;
1427 if(device->loop_counter)
1428 device->playing = device->loop_start;
1429 else
1430 device->loop_start = device->playing = queue->lpNext;
1436 hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames, 0);
1437 if(FAILED(hr)){
1438 WARN("ReleaseBuffer failed: %08x\n", hr);
1439 goto exit;
1442 device->played_frames += avail_frames;
1444 exit:
1445 cb_info = device->cb_info;
1447 LeaveCriticalSection(&device->lock);
1449 while(first){
1450 WAVEHDR *next = first->lpNext;
1451 first->dwFlags &= ~WHDR_INQUEUE;
1452 first->dwFlags |= WHDR_DONE;
1453 WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1454 first = next;
1458 static void WID_PullACMData(WINMM_Device *device)
1460 UINT32 packet, packet_bytes;
1461 DWORD flags;
1462 BYTE *data;
1463 WAVEHDR *queue;
1464 HRESULT hr;
1465 MMRESULT mr;
1467 if(device->acm_hdr.cbDstLength == 0){
1468 hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
1469 &flags, NULL, NULL);
1470 if(hr != S_OK){
1471 if(FAILED(hr))
1472 WARN("GetBuffer failed: %08x\n", hr);
1473 return;
1476 acmStreamSize(device->acm_handle, packet * device->bytes_per_frame,
1477 &packet_bytes, ACM_STREAMSIZEF_SOURCE);
1479 device->acm_offs = 0;
1481 device->acm_hdr.cbStruct = sizeof(device->acm_hdr);
1482 device->acm_hdr.fdwStatus = 0;
1483 device->acm_hdr.dwUser = 0;
1484 device->acm_hdr.pbSrc = data;
1485 device->acm_hdr.cbSrcLength = packet * device->bytes_per_frame;
1486 device->acm_hdr.cbSrcLengthUsed = 0;
1487 device->acm_hdr.dwSrcUser = 0;
1488 device->acm_hdr.pbDst = HeapAlloc(GetProcessHeap(), 0, packet_bytes);
1489 device->acm_hdr.cbDstLength = packet_bytes;
1490 device->acm_hdr.cbDstLengthUsed = 0;
1491 device->acm_hdr.dwDstUser = 0;
1493 mr = acmStreamPrepareHeader(device->acm_handle, &device->acm_hdr, 0);
1494 if(mr != MMSYSERR_NOERROR){
1495 WARN("acmStreamPrepareHeader failed: %d\n", mr);
1496 return;
1499 mr = acmStreamConvert(device->acm_handle, &device->acm_hdr, 0);
1500 if(mr != MMSYSERR_NOERROR){
1501 WARN("acmStreamConvert failed: %d\n", mr);
1502 return;
1505 hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet);
1506 if(FAILED(hr))
1507 WARN("ReleaseBuffer failed: %08x\n", hr);
1509 device->played_frames += packet;
1512 queue = device->first;
1513 while(queue){
1514 UINT32 to_copy_bytes;
1516 to_copy_bytes = min(queue->dwBufferLength - queue->dwBytesRecorded,
1517 device->acm_hdr.cbDstLengthUsed - device->acm_offs);
1519 memcpy(queue->lpData + queue->dwBytesRecorded,
1520 device->acm_hdr.pbDst + device->acm_offs, to_copy_bytes);
1522 queue->dwBytesRecorded += to_copy_bytes;
1523 device->acm_offs += to_copy_bytes;
1525 if(queue->dwBufferLength - queue->dwBytesRecorded <
1526 device->bytes_per_frame){
1527 queue->dwFlags &= ~WHDR_INQUEUE;
1528 queue->dwFlags |= WHDR_DONE;
1529 device->first = queue = queue->lpNext;
1532 if(device->acm_offs >= device->acm_hdr.cbDstLengthUsed){
1533 acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1534 HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1535 device->acm_hdr.cbDstLength = 0;
1536 device->acm_hdr.cbDstLengthUsed = 0;
1538 /* done with this ACM Header, so try to pull more data */
1539 WID_PullACMData(device);
1540 return;
1544 /* out of WAVEHDRs to write into, so toss the rest of this packet */
1545 acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1546 HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1547 device->acm_hdr.cbDstLength = 0;
1548 device->acm_hdr.cbDstLengthUsed = 0;
1551 static void WID_PullData(WINMM_Device *device)
1553 WINMM_CBInfo cb_info;
1554 WAVEHDR *queue, *first = NULL, *last = NULL;
1555 HRESULT hr;
1557 TRACE("(%p)\n", device->handle);
1559 EnterCriticalSection(&device->lock);
1561 if(!device->device || !device->first)
1562 goto exit;
1564 first = device->first;
1566 if(device->acm_handle){
1567 WID_PullACMData(device);
1568 goto exit;
1571 while(device->first){
1572 BYTE *data;
1573 UINT32 packet_len, packet;
1574 DWORD flags;
1576 hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet_len,
1577 &flags, NULL, NULL);
1578 if(hr != S_OK){
1579 if(FAILED(hr))
1580 WARN("GetBuffer failed: %08x\n", hr);
1581 else /* AUDCLNT_S_BUFFER_EMPTY success code */
1582 IAudioCaptureClient_ReleaseBuffer(device->capture, 0);
1583 goto exit;
1586 packet = packet_len;
1587 queue = device->first;
1588 while(queue && packet > 0){
1589 UINT32 to_copy_bytes;
1591 to_copy_bytes = min(packet * device->bytes_per_frame,
1592 queue->dwBufferLength - queue->dwBytesRecorded);
1594 memcpy(queue->lpData + queue->dwBytesRecorded,
1595 data + (packet_len - packet) * device->bytes_per_frame,
1596 to_copy_bytes);
1598 queue->dwBytesRecorded += to_copy_bytes;
1600 if(queue->dwBufferLength - queue->dwBytesRecorded <
1601 device->bytes_per_frame){
1602 last = queue;
1603 device->first = queue = queue->lpNext;
1606 packet -= to_copy_bytes / device->bytes_per_frame;
1609 hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet_len);
1610 if(FAILED(hr))
1611 WARN("ReleaseBuffer failed: %08x\n", hr);
1613 if(packet > 0)
1614 WARN("losing %u frames\n", packet);
1615 device->played_frames += packet_len;
1618 exit:
1619 cb_info = device->cb_info;
1621 LeaveCriticalSection(&device->lock);
1623 if(last){
1624 last->lpNext = NULL;
1625 while(first){
1626 WAVEHDR *next = first->lpNext;
1627 first->dwFlags &= ~WHDR_INQUEUE;
1628 first->dwFlags |= WHDR_DONE;
1629 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1630 first = next;
1635 static HRESULT WINMM_BeginPlaying(WINMM_Device *device)
1637 HRESULT hr;
1639 TRACE("(%p)\n", device->handle);
1641 if(device->render)
1642 /* prebuffer data before starting */
1643 WOD_PushData(device);
1645 if(device->stopped){
1646 device->stopped = FALSE;
1648 hr = IAudioClient_Start(device->client);
1649 if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){
1650 device->stopped = TRUE;
1651 WARN("Start failed: %08x\n", hr);
1652 return hr;
1656 return S_OK;
1659 static LRESULT WINMM_Pause(HWAVE hwave)
1661 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1662 HRESULT hr;
1664 TRACE("(%p)\n", hwave);
1666 if(!WINMM_ValidateAndLock(device))
1667 return MMSYSERR_INVALHANDLE;
1669 hr = IAudioClient_Stop(device->client);
1670 if(FAILED(hr)){
1671 LeaveCriticalSection(&device->lock);
1672 WARN("Stop failed: %08x\n", hr);
1673 return MMSYSERR_ERROR;
1676 device->stopped = FALSE;
1678 LeaveCriticalSection(&device->lock);
1680 return MMSYSERR_NOERROR;
1683 static LRESULT WINMM_Reset(HWAVE hwave)
1685 WINMM_CBInfo cb_info;
1686 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1687 BOOL is_out;
1688 WAVEHDR *first;
1689 HRESULT hr;
1691 TRACE("(%p)\n", hwave);
1693 if(!WINMM_ValidateAndLock(device))
1694 return MMSYSERR_INVALHANDLE;
1696 hr = IAudioClient_Stop(device->client);
1697 if(FAILED(hr)){
1698 LeaveCriticalSection(&device->lock);
1699 WARN("Stop failed: %08x\n", hr);
1700 return MMSYSERR_ERROR;
1702 device->stopped = TRUE;
1704 first = device->first;
1705 device->first = device->last = device->playing = NULL;
1706 device->ofs_bytes = 0;
1707 device->played_frames = 0;
1708 device->loop_counter = 0;
1709 device->last_clock_pos = 0;
1710 IAudioClient_Reset(device->client);
1712 cb_info = device->cb_info;
1713 is_out = device->render ? TRUE : FALSE;
1715 LeaveCriticalSection(&device->lock);
1717 while(first){
1718 WAVEHDR *next = first->lpNext;
1719 first->dwFlags &= ~WHDR_INQUEUE;
1720 first->dwFlags |= WHDR_DONE;
1721 if(is_out)
1722 WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1723 else
1724 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1725 first = next;
1728 return MMSYSERR_NOERROR;
1731 static MMRESULT WINMM_FramesToMMTime(MMTIME *time, UINT32 played_frames,
1732 UINT32 sample_rate, UINT32 bytes_per_frame)
1734 switch(time->wType){
1735 case TIME_SAMPLES:
1736 time->u.sample = played_frames;
1737 return MMSYSERR_NOERROR;
1738 case TIME_MS:
1739 time->u.ms = (UINT64)played_frames * 1000 / sample_rate;
1740 return MMSYSERR_NOERROR;
1741 case TIME_SMPTE:
1742 time->u.smpte.fps = 30;
1743 played_frames += sample_rate / time->u.smpte.fps - 1; /* round up */
1744 time->u.smpte.frame = (played_frames % sample_rate) * time->u.smpte.fps / sample_rate;
1745 played_frames /= sample_rate; /* yields seconds */
1746 time->u.smpte.sec = played_frames % 60;
1747 played_frames /= 60;
1748 time->u.smpte.min = played_frames % 60;
1749 time->u.smpte.hour= played_frames / 60;
1750 return MMSYSERR_NOERROR;
1751 default:
1752 time->wType = TIME_BYTES;
1753 /* fall through */
1754 case TIME_BYTES:
1755 time->u.cb = played_frames * bytes_per_frame;
1756 return MMSYSERR_NOERROR;
1760 static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time)
1762 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1763 UINT32 played_frames, sample_rate, bytes_per_frame;
1765 TRACE("(%p, %p)\n", hwave, time);
1767 if(!WINMM_ValidateAndLock(device))
1768 return MMSYSERR_INVALHANDLE;
1770 played_frames = device->played_frames;
1771 sample_rate = device->samples_per_sec;
1772 bytes_per_frame = device->bytes_per_frame;
1774 LeaveCriticalSection(&device->lock);
1776 return WINMM_FramesToMMTime(time, played_frames, sample_rate,
1777 bytes_per_frame);
1780 static WINMM_MMDevice *WINMM_GetMixerMMDevice(HMIXEROBJ hmix, DWORD flags,
1781 UINT *mmdev_index)
1783 UINT mmdev, dev, junk, *out;
1784 BOOL is_out;
1786 if(!mmdev_index)
1787 out = &mmdev;
1788 else
1789 out = mmdev_index;
1791 switch(flags & 0xF0000000){
1792 case MIXER_OBJECTF_MIXER: /* == 0 */
1793 *out = HandleToULong(hmix);
1794 if(*out < g_outmmdevices_count)
1795 return &g_out_mmdevices[*out];
1796 if(*out - g_outmmdevices_count < g_inmmdevices_count){
1797 *out -= g_outmmdevices_count;
1798 return &g_in_mmdevices[*out];
1800 /* fall through -- if it's not a valid mixer device, then
1801 * it could be a valid mixer handle. windows seems to do
1802 * this as well. */
1803 case MIXER_OBJECTF_HMIXER:
1804 case MIXER_OBJECTF_HWAVEOUT:
1805 case MIXER_OBJECTF_HWAVEIN:
1806 WINMM_DecomposeHWAVE((HWAVE)hmix, out, &is_out, &dev, &junk);
1807 if(junk != 0x1 || (is_out && *out >= g_outmmdevices_count) ||
1808 (!is_out && *out >= g_inmmdevices_count))
1809 return NULL;
1810 if(is_out)
1811 return &g_out_mmdevices[*out];
1812 return &g_in_mmdevices[*out];
1813 case MIXER_OBJECTF_WAVEOUT:
1814 *out = HandleToULong(hmix);
1815 if(*out < g_outmmdevices_count)
1816 return &g_out_mmdevices[*out];
1817 return NULL;
1818 case MIXER_OBJECTF_WAVEIN:
1819 *out = HandleToULong(hmix);
1820 if(*out < g_inmmdevices_count)
1821 return &g_in_mmdevices[*out];
1822 return NULL;
1825 return NULL;
1828 static MMRESULT WINMM_SetupMMDeviceVolume(WINMM_MMDevice *mmdevice)
1830 IAudioSessionManager *sesman;
1831 IMMDevice *device;
1832 HRESULT hr;
1834 hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id, &device);
1835 if(FAILED(hr)){
1836 WARN("Device %s (%s) unavailable: %08x\n",
1837 wine_dbgstr_w(mmdevice->dev_id),
1838 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
1839 return MMSYSERR_ERROR;
1842 hr = IMMDevice_Activate(device, &IID_IAudioSessionManager,
1843 CLSCTX_INPROC_SERVER, NULL, (void**)&sesman);
1844 if(FAILED(hr)){
1845 WARN("Activate failed: %08x\n", hr);
1846 IMMDevice_Release(device);
1847 return MMSYSERR_ERROR;
1850 IMMDevice_Release(device);
1852 hr = IAudioSessionManager_GetSimpleAudioVolume(sesman, &mmdevice->session,
1853 FALSE, &mmdevice->volume);
1854 IAudioSessionManager_Release(sesman);
1855 if(FAILED(hr)){
1856 WARN("GetSimpleAudioVolume failed: %08x\n", hr);
1857 return MMSYSERR_ERROR;
1860 return MMSYSERR_NOERROR;
1863 static LRESULT MXD_GetControlDetails(WINMM_ControlDetails *details)
1865 WINMM_MMDevice *mmdevice;
1866 MIXERCONTROLDETAILS *control = details->details;
1867 HRESULT hr;
1869 TRACE("(%p)\n", details->hmix);
1871 mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
1872 if(!mmdevice)
1873 return MMSYSERR_INVALHANDLE;
1875 EnterCriticalSection(&mmdevice->lock);
1877 if(!mmdevice->volume){
1878 MMRESULT mr;
1880 mr = WINMM_SetupMMDeviceVolume(mmdevice);
1881 if(mr != MMSYSERR_NOERROR){
1882 LeaveCriticalSection(&mmdevice->lock);
1883 return mr;
1887 if(control->dwControlID == 0){
1888 float vol;
1889 MIXERCONTROLDETAILS_UNSIGNED *udet;
1891 if(!control->paDetails ||
1892 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
1893 LeaveCriticalSection(&mmdevice->lock);
1894 return MMSYSERR_INVALPARAM;
1897 hr = ISimpleAudioVolume_GetMasterVolume(mmdevice->volume, &vol);
1898 if(FAILED(hr)){
1899 WARN("GetMasterVolume failed: %08x\n", hr);
1900 LeaveCriticalSection(&mmdevice->lock);
1901 return MMSYSERR_ERROR;
1904 udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
1905 udet->dwValue = vol * ((unsigned int)0xFFFF);
1906 }else if(control->dwControlID == 1){
1907 BOOL mute;
1908 MIXERCONTROLDETAILS_BOOLEAN *bdet;
1910 if(!control->paDetails ||
1911 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
1912 LeaveCriticalSection(&mmdevice->lock);
1913 return MMSYSERR_INVALPARAM;
1916 hr = ISimpleAudioVolume_GetMute(mmdevice->volume, &mute);
1917 if(FAILED(hr)){
1918 WARN("GetMute failed: %08x\n", hr);
1919 LeaveCriticalSection(&mmdevice->lock);
1920 return MMSYSERR_ERROR;
1923 bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
1924 bdet->fValue = mute;
1925 }else if(control->dwControlID == 2 || control->dwControlID == 3){
1926 FIXME("What should the sw-side mixer controls map to?\n");
1927 }else{
1928 LeaveCriticalSection(&mmdevice->lock);
1929 return MIXERR_INVALCONTROL;
1932 LeaveCriticalSection(&mmdevice->lock);
1934 return MMSYSERR_NOERROR;
1937 static LRESULT MXD_SetControlDetails(WINMM_ControlDetails *details)
1939 WINMM_MMDevice *mmdevice;
1940 MIXERCONTROLDETAILS *control = details->details;
1941 HRESULT hr;
1943 TRACE("(%p)\n", details->hmix);
1945 mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
1946 if(!mmdevice)
1947 return MMSYSERR_INVALHANDLE;
1949 EnterCriticalSection(&mmdevice->lock);
1951 if(!mmdevice->volume){
1952 MMRESULT mr;
1954 mr = WINMM_SetupMMDeviceVolume(mmdevice);
1955 if(mr != MMSYSERR_NOERROR){
1956 LeaveCriticalSection(&mmdevice->lock);
1957 return mr;
1961 if(control->dwControlID == 0){
1962 float vol;
1963 MIXERCONTROLDETAILS_UNSIGNED *udet;
1965 if(!control->paDetails ||
1966 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
1967 LeaveCriticalSection(&mmdevice->lock);
1968 return MMSYSERR_INVALPARAM;
1971 udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
1973 if(udet->dwValue > 65535){
1974 LeaveCriticalSection(&mmdevice->lock);
1975 return MMSYSERR_INVALPARAM;
1978 vol = udet->dwValue / 65535.f;
1980 hr = ISimpleAudioVolume_SetMasterVolume(mmdevice->volume, vol, NULL);
1981 if(FAILED(hr)){
1982 WARN("SetMasterVolume failed: %08x\n", hr);
1983 LeaveCriticalSection(&mmdevice->lock);
1984 return MMSYSERR_ERROR;
1986 }else if(control->dwControlID == 1){
1987 BOOL mute;
1988 MIXERCONTROLDETAILS_BOOLEAN *bdet;
1990 if(!control->paDetails ||
1991 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
1992 LeaveCriticalSection(&mmdevice->lock);
1993 return MMSYSERR_INVALPARAM;
1996 bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
1997 mute = bdet->fValue;
1999 hr = ISimpleAudioVolume_SetMute(mmdevice->volume, mute, NULL);
2000 if(FAILED(hr)){
2001 WARN("SetMute failed: %08x\n", hr);
2002 LeaveCriticalSection(&mmdevice->lock);
2003 return MMSYSERR_ERROR;
2005 }else if(control->dwControlID == 2 || control->dwControlID == 3){
2006 FIXME("What should the sw-side mixer controls map to?\n");
2007 }else{
2008 LeaveCriticalSection(&mmdevice->lock);
2009 return MIXERR_INVALCONTROL;
2012 LeaveCriticalSection(&mmdevice->lock);
2014 return MMSYSERR_NOERROR;
2017 static LRESULT CALLBACK WINMM_DevicesMsgProc(HWND hwnd, UINT msg, WPARAM wparam,
2018 LPARAM lparam)
2020 switch(msg){
2021 case WODM_OPEN:
2022 return WOD_Open((WINMM_OpenInfo*)wparam);
2023 case WODM_CLOSE:
2024 return WOD_Close((HWAVEOUT)wparam);
2025 case WIDM_OPEN:
2026 return WID_Open((WINMM_OpenInfo*)wparam);
2027 case WIDM_CLOSE:
2028 return WID_Close((HWAVEIN)wparam);
2029 case MXDM_GETCONTROLDETAILS:
2030 return MXD_GetControlDetails((WINMM_ControlDetails*)wparam);
2031 case MXDM_SETCONTROLDETAILS:
2032 return MXD_SetControlDetails((WINMM_ControlDetails*)wparam);
2034 return DefWindowProcW(hwnd, msg, wparam, lparam);
2037 static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
2039 HANDLE evt = arg;
2040 HRESULT hr;
2041 static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0};
2043 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
2044 if(FAILED(hr)){
2045 WARN("CoInitializeEx failed: %08x\n", hr);
2046 return 1;
2049 hr = WINMM_InitMMDevices();
2050 if(FAILED(hr)){
2051 CoUninitialize();
2052 return 1;
2055 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
2056 CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&g_devenum);
2057 if(FAILED(hr)){
2058 WARN("CoCreateInstance failed: %08x\n", hr);
2059 CoUninitialize();
2060 return 1;
2063 g_devices_hwnd = CreateWindowW(messageW, NULL, 0, 0, 0, 0, 0,
2064 HWND_MESSAGE, NULL, NULL, NULL);
2065 if(!g_devices_hwnd){
2066 WARN("CreateWindow failed: %d\n", GetLastError());
2067 IMMDeviceEnumerator_Release(g_devenum);
2068 CoUninitialize();
2069 return 1;
2072 SetWindowLongPtrW(g_devices_hwnd, GWLP_WNDPROC,
2073 (LONG_PTR)WINMM_DevicesMsgProc);
2075 /* inform caller that the thread is ready to process messages */
2076 SetEvent(evt);
2077 evt = NULL; /* do not use after this point */
2079 while(1){
2080 DWORD wait;
2081 wait = MsgWaitForMultipleObjects(g_devhandle_count, g_device_handles,
2082 FALSE, INFINITE, QS_ALLINPUT);
2083 if(wait == g_devhandle_count + WAIT_OBJECT_0){
2084 MSG msg;
2085 if(PeekMessageW(&msg, g_devices_hwnd, 0, 0, PM_REMOVE))
2086 WARN("Unexpected message: 0x%x\n", msg.message);
2087 }else if(wait < g_devhandle_count + WAIT_OBJECT_0){
2088 WINMM_Device *device = g_handle_devices[wait - WAIT_OBJECT_0];
2089 if(device->render)
2090 WOD_PushData(device);
2091 else
2092 WID_PullData(device);
2093 }else
2094 WARN("Unexpected MsgWait result 0x%x, GLE: %d\n", wait,
2095 GetLastError());
2098 DestroyWindow(g_devices_hwnd);
2100 IMMDeviceEnumerator_Release(g_devenum);
2102 CoUninitialize();
2104 return 0;
2107 static BOOL WINMM_StartDevicesThread(void)
2109 HANDLE events[2];
2110 DWORD wait;
2112 EnterCriticalSection(&g_devthread_lock);
2114 if(g_devices_thread){
2115 DWORD wait;
2117 wait = WaitForSingleObject(g_devices_thread, 0);
2118 if(wait == WAIT_TIMEOUT){
2119 LeaveCriticalSection(&g_devthread_lock);
2120 return TRUE;
2122 if(wait != WAIT_OBJECT_0){
2123 LeaveCriticalSection(&g_devthread_lock);
2124 return FALSE;
2127 g_devices_thread = NULL;
2128 g_devices_hwnd = NULL;
2131 TRACE("Starting up devices thread\n");
2133 events[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
2135 g_devices_thread = CreateThread(NULL, 0, WINMM_DevicesThreadProc,
2136 events[0], 0, NULL);
2137 if(!g_devices_thread){
2138 LeaveCriticalSection(&g_devthread_lock);
2139 CloseHandle(events[0]);
2140 return FALSE;
2143 events[1] = g_devices_thread;
2144 wait = WaitForMultipleObjects(2, events, FALSE, INFINITE);
2145 CloseHandle(events[0]);
2146 if(wait != WAIT_OBJECT_0){
2147 if(wait == 1 + WAIT_OBJECT_0){
2148 CloseHandle(g_devices_thread);
2149 g_devices_thread = NULL;
2150 g_devices_hwnd = NULL;
2152 LeaveCriticalSection(&g_devthread_lock);
2153 return FALSE;
2156 LeaveCriticalSection(&g_devthread_lock);
2158 return TRUE;
2161 /**************************************************************************
2162 * waveOutGetNumDevs [WINMM.@]
2164 UINT WINAPI waveOutGetNumDevs(void)
2166 HRESULT hr = WINMM_InitMMDevices();
2167 if(FAILED(hr))
2168 return 0;
2170 TRACE("count: %u\n", g_outmmdevices_count);
2172 return g_outmmdevices_count;
2175 /**************************************************************************
2176 * waveOutGetDevCapsA [WINMM.@]
2178 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
2179 UINT uSize)
2181 WAVEOUTCAPSW wocW;
2182 UINT ret;
2184 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2186 if(!lpCaps)
2187 return MMSYSERR_INVALPARAM;
2189 ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
2191 if (ret == MMSYSERR_NOERROR) {
2192 WAVEOUTCAPSA wocA;
2193 wocA.wMid = wocW.wMid;
2194 wocA.wPid = wocW.wPid;
2195 wocA.vDriverVersion = wocW.vDriverVersion;
2196 WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,
2197 sizeof(wocA.szPname), NULL, NULL );
2198 wocA.dwFormats = wocW.dwFormats;
2199 wocA.wChannels = wocW.wChannels;
2200 wocA.dwSupport = wocW.dwSupport;
2201 memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));
2203 return ret;
2206 /**************************************************************************
2207 * waveOutGetDevCapsW [WINMM.@]
2209 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
2210 UINT uSize)
2212 WAVEOUTCAPSW mapper_caps, *caps;
2213 HRESULT hr;
2215 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2217 hr = WINMM_InitMMDevices();
2218 if(FAILED(hr))
2219 return MMSYSERR_ERROR;
2221 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2223 if(WINMM_IsMapper(uDeviceID)){
2224 /* FIXME: Should be localized */
2225 static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2226 'n','d',' ','M','a','p','p','e','r',0};
2228 mapper_caps.wMid = 0xFF;
2229 mapper_caps.wPid = 0xFF;
2230 mapper_caps.vDriverVersion = 0x00010001;
2231 mapper_caps.dwFormats = 0xFFFFFFFF;
2232 mapper_caps.wReserved1 = 0;
2233 mapper_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
2234 WAVECAPS_SAMPLEACCURATE;
2235 mapper_caps.wChannels = 2;
2236 lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2238 caps = &mapper_caps;
2239 }else{
2240 if(uDeviceID >= g_outmmdevices_count)
2241 return MMSYSERR_BADDEVICEID;
2243 caps = &g_out_mmdevices[uDeviceID].out_caps;
2246 memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2248 return MMSYSERR_NOERROR;
2251 /**************************************************************************
2252 * waveOutGetErrorTextA [WINMM.@]
2253 * waveInGetErrorTextA [WINMM.@]
2255 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2257 UINT ret;
2259 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2260 else if (uSize == 0) ret = MMSYSERR_NOERROR;
2261 else
2263 LPWSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
2264 if (!xstr) ret = MMSYSERR_NOMEM;
2265 else
2267 ret = waveOutGetErrorTextW(uError, xstr, uSize);
2268 if (ret == MMSYSERR_NOERROR)
2269 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
2270 HeapFree(GetProcessHeap(), 0, xstr);
2273 return ret;
2276 /**************************************************************************
2277 * waveOutGetErrorTextW [WINMM.@]
2278 * waveInGetErrorTextW [WINMM.@]
2280 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2282 UINT ret = MMSYSERR_BADERRNUM;
2284 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2285 else if (uSize == 0) ret = MMSYSERR_NOERROR;
2286 else if (
2287 /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
2288 * a warning for the test was always true */
2289 (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
2290 (uError >= WAVERR_BASE && uError <= WAVERR_LASTERROR)) {
2291 if (LoadStringW(hWinMM32Instance,
2292 uError, lpText, uSize) > 0) {
2293 ret = MMSYSERR_NOERROR;
2296 return ret;
2299 /**************************************************************************
2300 * waveOutOpen [WINMM.@]
2301 * All the args/structs have the same layout as the win16 equivalents
2303 MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
2304 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2305 DWORD_PTR dwInstance, DWORD dwFlags)
2307 LRESULT res;
2308 WINMM_OpenInfo info;
2309 WINMM_CBInfo cb_info;
2311 TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut, uDeviceID, lpFormat,
2312 dwCallback, dwInstance, dwFlags);
2314 if(!WINMM_StartDevicesThread())
2315 return MMSYSERR_ERROR;
2317 if(!lphWaveOut && !(dwFlags & WAVE_FORMAT_QUERY))
2318 return MMSYSERR_INVALPARAM;
2320 res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2321 if(res != MMSYSERR_NOERROR)
2322 return res;
2324 info.format = (WAVEFORMATEX*)lpFormat;
2325 info.callback = dwCallback;
2326 info.cb_user = dwInstance;
2327 info.req_device = uDeviceID;
2328 info.flags = dwFlags;
2330 res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0);
2331 if(res != MMSYSERR_NOERROR)
2332 return res;
2334 if(lphWaveOut)
2335 *lphWaveOut = (HWAVEOUT)info.handle;
2337 cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2338 cb_info.callback = dwCallback;
2339 cb_info.user = dwInstance;
2340 cb_info.hwave = info.handle;
2342 WINMM_NotifyClient(&cb_info, WOM_OPEN, 0, 0);
2344 return res;
2347 /**************************************************************************
2348 * waveOutClose [WINMM.@]
2350 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
2352 UINT res;
2353 WINMM_Device *device;
2354 WINMM_CBInfo cb_info;
2356 TRACE("(%p)\n", hWaveOut);
2358 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2360 if(!WINMM_ValidateAndLock(device))
2361 return MMSYSERR_INVALHANDLE;
2363 cb_info = device->cb_info;
2365 LeaveCriticalSection(&device->lock);
2367 res = SendMessageW(g_devices_hwnd, WODM_CLOSE, (WPARAM)hWaveOut, 0);
2369 if(res == MMSYSERR_NOERROR)
2370 WINMM_NotifyClient(&cb_info, WOM_CLOSE, 0, 0);
2372 return res;
2375 /**************************************************************************
2376 * waveOutPrepareHeader [WINMM.@]
2378 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
2379 WAVEHDR* lpWaveOutHdr, UINT uSize)
2381 TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2383 if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2384 return MMSYSERR_INVALPARAM;
2386 if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2387 return WAVERR_STILLPLAYING;
2389 return WINMM_PrepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2392 /**************************************************************************
2393 * waveOutUnprepareHeader [WINMM.@]
2395 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
2396 LPWAVEHDR lpWaveOutHdr, UINT uSize)
2398 TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2400 if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2401 return MMSYSERR_INVALPARAM;
2403 if(!(lpWaveOutHdr->dwFlags & WHDR_PREPARED))
2404 return MMSYSERR_NOERROR;
2406 if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2407 return WAVERR_STILLPLAYING;
2409 return WINMM_UnprepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2412 /**************************************************************************
2413 * waveOutWrite [WINMM.@]
2415 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, WAVEHDR *header, UINT uSize)
2417 WINMM_Device *device;
2418 HRESULT hr;
2420 TRACE("(%p, %p, %u)\n", hWaveOut, header, uSize);
2422 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2424 if(!WINMM_ValidateAndLock(device))
2425 return MMSYSERR_INVALHANDLE;
2427 if(!header->lpData || !(header->dwFlags & WHDR_PREPARED)){
2428 LeaveCriticalSection(&device->lock);
2429 return WAVERR_UNPREPARED;
2432 if(header->dwFlags & WHDR_INQUEUE){
2433 LeaveCriticalSection(&device->lock);
2434 return WAVERR_STILLPLAYING;
2437 if(device->acm_handle){
2438 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
2439 MMRESULT mr;
2441 ash->cbSrcLength = header->dwBufferLength;
2442 mr = acmStreamConvert(device->acm_handle, ash, 0);
2443 if(mr != MMSYSERR_NOERROR){
2444 LeaveCriticalSection(&device->lock);
2445 return mr;
2449 if(device->first){
2450 device->last->lpNext = header;
2451 device->last = header;
2452 if(!device->playing)
2453 device->playing = header;
2454 }else{
2455 device->playing = device->first = device->last = header;
2456 if(header->dwFlags & WHDR_BEGINLOOP){
2457 device->loop_counter = header->dwLoops;
2458 device->loop_start = header;
2462 header->lpNext = NULL;
2463 header->dwFlags &= ~WHDR_DONE;
2464 header->dwFlags |= WHDR_INQUEUE;
2466 hr = WINMM_BeginPlaying(device);
2467 if(FAILED(hr)){
2468 LeaveCriticalSection(&device->lock);
2469 return MMSYSERR_ERROR;
2472 LeaveCriticalSection(&device->lock);
2474 return MMSYSERR_NOERROR;
2477 /**************************************************************************
2478 * waveOutBreakLoop [WINMM.@]
2480 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
2482 WINMM_Device *device;
2484 TRACE("(%p)\n", hWaveOut);
2486 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2488 if(!WINMM_ValidateAndLock(device))
2489 return MMSYSERR_INVALHANDLE;
2491 device->loop_counter = 0;
2493 LeaveCriticalSection(&device->lock);
2495 return MMSYSERR_NOERROR;
2498 /**************************************************************************
2499 * waveOutPause [WINMM.@]
2501 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
2503 TRACE("(%p)\n", hWaveOut);
2505 return WINMM_Pause((HWAVE)hWaveOut);
2508 /**************************************************************************
2509 * waveOutReset [WINMM.@]
2511 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2513 TRACE("(%p)\n", hWaveOut);
2515 return WINMM_Reset((HWAVE)hWaveOut);
2518 /**************************************************************************
2519 * waveOutRestart [WINMM.@]
2521 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2523 WINMM_Device *device;
2524 HRESULT hr;
2526 TRACE("(%p)\n", hWaveOut);
2528 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2530 if(!WINMM_ValidateAndLock(device))
2531 return MMSYSERR_INVALHANDLE;
2533 device->stopped = TRUE;
2535 hr = WINMM_BeginPlaying(device);
2536 if(FAILED(hr)){
2537 LeaveCriticalSection(&device->lock);
2538 return MMSYSERR_ERROR;
2541 LeaveCriticalSection(&device->lock);
2543 return MMSYSERR_NOERROR;
2546 /**************************************************************************
2547 * waveOutGetPosition [WINMM.@]
2549 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2550 UINT uSize)
2552 TRACE("(%p, %p, %u)\n", hWaveOut, lpTime, uSize);
2554 if(!uSize || !lpTime || uSize != sizeof(MMTIME))
2555 return MMSYSERR_INVALPARAM;
2557 return WINMM_GetPosition((HWAVE)hWaveOut, lpTime);
2560 /**************************************************************************
2561 * waveOutGetPitch [WINMM.@]
2563 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2565 TRACE("(%p, %p)\n", hWaveOut, lpdw);
2566 return MMSYSERR_NOTSUPPORTED;
2569 /**************************************************************************
2570 * waveOutSetPitch [WINMM.@]
2572 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
2574 TRACE("(%p, %08x)\n", hWaveOut, dw);
2576 return MMSYSERR_NOTSUPPORTED;
2579 /**************************************************************************
2580 * waveOutGetPlaybackRate [WINMM.@]
2582 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
2584 TRACE("(%p, %p)\n", hWaveOut, lpdw);
2586 return MMSYSERR_NOTSUPPORTED;
2589 /**************************************************************************
2590 * waveOutSetPlaybackRate [WINMM.@]
2592 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
2594 TRACE("(%p, %08x)\n", hWaveOut, dw);
2596 return MMSYSERR_NOTSUPPORTED;
2599 /**************************************************************************
2600 * waveOutGetVolume [WINMM.@]
2602 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, DWORD *out)
2604 WINMM_Device *device;
2605 UINT32 channels;
2606 float *vols;
2607 HRESULT hr;
2609 TRACE("(%p, %p)\n", hWaveOut, out);
2611 if(!out)
2612 return MMSYSERR_INVALPARAM;
2614 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2616 if(!WINMM_ValidateAndLock(device))
2617 return MMSYSERR_INVALHANDLE;
2619 hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2620 if(FAILED(hr)){
2621 LeaveCriticalSection(&device->lock);
2622 WARN("GetChannelCount failed: %08x\n", hr);
2623 return MMSYSERR_ERROR;
2626 vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2627 if(!vols){
2628 LeaveCriticalSection(&device->lock);
2629 return MMSYSERR_NOMEM;
2632 hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2633 if(FAILED(hr)){
2634 LeaveCriticalSection(&device->lock);
2635 HeapFree(GetProcessHeap(), 0, vols);
2636 WARN("GetAllVolumes failed: %08x\n", hr);
2637 return MMSYSERR_ERROR;
2640 LeaveCriticalSection(&device->lock);
2642 *out = ((UINT16)(vols[0] * (DWORD)0xFFFF));
2643 if(channels > 1)
2644 *out |= ((UINT16)(vols[1] * (DWORD)0xFFFF)) << 16;
2646 HeapFree(GetProcessHeap(), 0, vols);
2648 return MMSYSERR_NOERROR;
2651 /**************************************************************************
2652 * waveOutSetVolume [WINMM.@]
2654 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD in)
2656 WINMM_Device *device;
2657 UINT32 channels;
2658 float *vols;
2659 HRESULT hr;
2661 TRACE("(%p, %08x)\n", hWaveOut, in);
2663 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2665 if(!WINMM_ValidateAndLock(device))
2666 return MMSYSERR_INVALHANDLE;
2668 hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2669 if(FAILED(hr)){
2670 LeaveCriticalSection(&device->lock);
2671 WARN("GetChannelCount failed: %08x\n", hr);
2672 return MMSYSERR_ERROR;
2675 vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2676 if(!vols){
2677 LeaveCriticalSection(&device->lock);
2678 return MMSYSERR_NOMEM;
2681 hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2682 if(FAILED(hr)){
2683 LeaveCriticalSection(&device->lock);
2684 HeapFree(GetProcessHeap(), 0, vols);
2685 WARN("GetAllVolumes failed: %08x\n", hr);
2686 return MMSYSERR_ERROR;
2689 vols[0] = (float)((DWORD)(in & 0xFFFF) / (float)0xFFFF);
2690 if(channels > 1)
2691 vols[1] = (float)((DWORD)(in >> 16) / (float)0xFFFF);
2693 hr = IAudioStreamVolume_SetAllVolumes(device->volume, channels, vols);
2694 if(FAILED(hr)){
2695 LeaveCriticalSection(&device->lock);
2696 HeapFree(GetProcessHeap(), 0, vols);
2697 WARN("SetAllVolumes failed: %08x\n", hr);
2698 return MMSYSERR_ERROR;
2701 LeaveCriticalSection(&device->lock);
2703 HeapFree(GetProcessHeap(), 0, vols);
2705 return MMSYSERR_NOERROR;
2708 /**************************************************************************
2709 * waveOutGetID [WINMM.@]
2711 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
2713 WINMM_Device *device;
2714 UINT dev, junk;
2715 BOOL is_out;
2717 TRACE("(%p, %p)\n", hWaveOut, lpuDeviceID);
2719 if(!lpuDeviceID)
2720 return MMSYSERR_INVALPARAM;
2722 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2723 if(!WINMM_ValidateAndLock(device))
2724 return MMSYSERR_INVALHANDLE;
2726 LeaveCriticalSection(&device->lock);
2728 WINMM_DecomposeHWAVE((HWAVE)hWaveOut, lpuDeviceID, &is_out, &dev, &junk);
2730 return MMSYSERR_NOERROR;
2733 static UINT WINMM_QueryInstanceIDSize(UINT device, DWORD_PTR *len, BOOL is_out)
2735 UINT count;
2736 WINMM_MMDevice *devices;
2738 TRACE("(%u, %p, %d)\n", device, len, is_out);
2740 if(is_out){
2741 count = g_outmmdevices_count;
2742 devices = g_out_mmdevices;
2743 }else{
2744 count = g_inmmdevices_count;
2745 devices = g_in_mmdevices;
2748 if(device >= count)
2749 return MMSYSERR_INVALHANDLE;
2751 *len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2753 return MMSYSERR_NOERROR;
2756 static UINT WINMM_QueryInstanceID(UINT device, WCHAR *str, DWORD_PTR len,
2757 BOOL is_out)
2759 UINT count, id_len;
2760 WINMM_MMDevice *devices;
2762 TRACE("(%u, %p, %d)\n", device, str, is_out);
2764 if(is_out){
2765 count = g_outmmdevices_count;
2766 devices = g_out_mmdevices;
2767 }else{
2768 count = g_inmmdevices_count;
2769 devices = g_in_mmdevices;
2772 if(device >= count)
2773 return MMSYSERR_INVALHANDLE;
2775 id_len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2776 if(len < id_len)
2777 return MMSYSERR_ERROR;
2779 memcpy(str, devices[device].dev_id, id_len);
2781 return MMSYSERR_NOERROR;
2784 /**************************************************************************
2785 * waveOutMessage [WINMM.@]
2787 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2788 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2790 TRACE("(%p, %u, %lx, %lx)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2792 switch(uMessage){
2793 case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
2794 return WINMM_QueryInstanceIDSize(HandleToULong(hWaveOut),
2795 (DWORD_PTR*)dwParam1, TRUE);
2796 case DRV_QUERYFUNCTIONINSTANCEID:
2797 return WINMM_QueryInstanceID(HandleToULong(hWaveOut), (WCHAR*)dwParam1, dwParam2, TRUE);
2798 case DRV_QUERYMAPPABLE:
2799 return MMSYSERR_NOERROR;
2802 TRACE("Message not supported: %u\n", uMessage);
2804 return MMSYSERR_NOTSUPPORTED;
2807 /**************************************************************************
2808 * waveInGetNumDevs [WINMM.@]
2810 UINT WINAPI waveInGetNumDevs(void)
2812 HRESULT hr = WINMM_InitMMDevices();
2813 if(FAILED(hr))
2814 return 0;
2816 TRACE("count: %u\n", g_inmmdevices_count);
2818 return g_inmmdevices_count;
2821 /**************************************************************************
2822 * waveInGetDevCapsW [WINMM.@]
2824 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2826 WAVEINCAPSW mapper_caps, *caps;
2827 HRESULT hr;
2829 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2831 hr = WINMM_InitMMDevices();
2832 if(FAILED(hr))
2833 return MMSYSERR_ERROR;
2835 if(!lpCaps)
2836 return MMSYSERR_INVALPARAM;
2838 if(WINMM_IsMapper(uDeviceID)){
2839 /* FIXME: Should be localized */
2840 static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2841 'n','d',' ','M','a','p','p','e','r',0};
2843 mapper_caps.wMid = 0xFF;
2844 mapper_caps.wPid = 0xFF;
2845 mapper_caps.vDriverVersion = 0x00010001;
2846 mapper_caps.dwFormats = 0xFFFFFFFF;
2847 mapper_caps.wReserved1 = 0;
2848 mapper_caps.wChannels = 2;
2849 lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2851 caps = &mapper_caps;
2852 }else{
2853 if(uDeviceID >= g_inmmdevices_count)
2854 return MMSYSERR_BADDEVICEID;
2856 caps = &g_in_mmdevices[uDeviceID].in_caps;
2859 memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2861 return MMSYSERR_NOERROR;
2864 /**************************************************************************
2865 * waveInGetDevCapsA [WINMM.@]
2867 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2869 UINT ret;
2870 WAVEINCAPSW wicW;
2872 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2874 if(!lpCaps)
2875 return MMSYSERR_INVALPARAM;
2877 ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2879 if (ret == MMSYSERR_NOERROR) {
2880 WAVEINCAPSA wicA;
2881 wicA.wMid = wicW.wMid;
2882 wicA.wPid = wicW.wPid;
2883 wicA.vDriverVersion = wicW.vDriverVersion;
2884 WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2885 sizeof(wicA.szPname), NULL, NULL );
2886 wicA.dwFormats = wicW.dwFormats;
2887 wicA.wChannels = wicW.wChannels;
2888 memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2890 return ret;
2893 /**************************************************************************
2894 * waveInOpen [WINMM.@]
2896 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2897 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2898 DWORD_PTR dwInstance, DWORD dwFlags)
2900 LRESULT res;
2901 WINMM_OpenInfo info;
2902 WINMM_CBInfo cb_info;
2904 TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
2905 dwCallback, dwInstance, dwFlags);
2907 if(!WINMM_StartDevicesThread())
2908 return MMSYSERR_ERROR;
2910 if(!lphWaveIn && !(dwFlags & WAVE_FORMAT_QUERY))
2911 return MMSYSERR_INVALPARAM;
2913 res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2914 if(res != MMSYSERR_NOERROR)
2915 return res;
2917 info.format = (WAVEFORMATEX*)lpFormat;
2918 info.callback = dwCallback;
2919 info.cb_user = dwInstance;
2920 info.req_device = uDeviceID;
2921 info.flags = dwFlags;
2923 res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
2924 if(res != MMSYSERR_NOERROR)
2925 return res;
2927 if(lphWaveIn)
2928 *lphWaveIn = (HWAVEIN)info.handle;
2930 cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2931 cb_info.callback = dwCallback;
2932 cb_info.user = dwInstance;
2933 cb_info.hwave = info.handle;
2935 WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
2937 return res;
2940 /**************************************************************************
2941 * waveInClose [WINMM.@]
2943 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2945 WINMM_Device *device;
2946 WINMM_CBInfo cb_info;
2947 UINT res;
2949 TRACE("(%p)\n", hWaveIn);
2951 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
2953 if(!WINMM_ValidateAndLock(device))
2954 return MMSYSERR_INVALHANDLE;
2956 cb_info = device->cb_info;
2958 LeaveCriticalSection(&device->lock);
2960 res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
2962 if(res == MMSYSERR_NOERROR)
2963 WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
2965 return res;
2968 /**************************************************************************
2969 * waveInPrepareHeader [WINMM.@]
2971 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2972 UINT uSize)
2974 TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2976 if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2977 return MMSYSERR_INVALPARAM;
2979 if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2980 return WAVERR_STILLPLAYING;
2982 return WINMM_PrepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
2985 /**************************************************************************
2986 * waveInUnprepareHeader [WINMM.@]
2988 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2989 UINT uSize)
2991 TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2993 if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2994 return MMSYSERR_INVALPARAM;
2996 if(!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
2997 return MMSYSERR_NOERROR;
2999 if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
3000 return WAVERR_STILLPLAYING;
3002 return WINMM_UnprepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
3005 /**************************************************************************
3006 * waveInAddBuffer [WINMM.@]
3008 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn, WAVEHDR *header, UINT uSize)
3010 WINMM_Device *device;
3012 TRACE("(%p, %p, %u)\n", hWaveIn, header, uSize);
3014 if(!header || uSize < sizeof(WAVEHDR))
3015 return MMSYSERR_INVALPARAM;
3017 if(!(header->dwFlags & WHDR_PREPARED))
3018 return WAVERR_UNPREPARED;
3020 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3022 if(!WINMM_ValidateAndLock(device))
3023 return MMSYSERR_INVALHANDLE;
3025 if(!device->first)
3026 device->first = device->last = header;
3027 else{
3028 device->last->lpNext = header;
3029 device->last = header;
3032 header->dwBytesRecorded = 0;
3033 header->lpNext = NULL;
3034 header->dwFlags &= ~WHDR_DONE;
3035 header->dwFlags |= WHDR_INQUEUE;
3037 LeaveCriticalSection(&device->lock);
3039 return MMSYSERR_NOERROR;
3042 /**************************************************************************
3043 * waveInReset [WINMM.@]
3045 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
3047 TRACE("(%p)\n", hWaveIn);
3049 return WINMM_Reset((HWAVE)hWaveIn);
3052 /**************************************************************************
3053 * waveInStart [WINMM.@]
3055 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
3057 WINMM_Device *device;
3058 HRESULT hr;
3060 TRACE("(%p)\n", hWaveIn);
3062 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3064 if(!WINMM_ValidateAndLock(device))
3065 return MMSYSERR_INVALHANDLE;
3067 hr = WINMM_BeginPlaying(device);
3068 if(FAILED(hr)){
3069 LeaveCriticalSection(&device->lock);
3070 return MMSYSERR_ERROR;
3073 LeaveCriticalSection(&device->lock);
3075 return MMSYSERR_NOERROR;
3078 /**************************************************************************
3079 * waveInStop [WINMM.@]
3081 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
3083 WINMM_CBInfo cb_info;
3084 WINMM_Device *device;
3085 WAVEHDR *buf;
3086 HRESULT hr;
3088 TRACE("(%p)\n", hWaveIn);
3090 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3092 if(!WINMM_ValidateAndLock(device))
3093 return MMSYSERR_INVALHANDLE;
3095 hr = WINMM_Pause((HWAVE)hWaveIn);
3096 if(FAILED(hr)){
3097 LeaveCriticalSection(&device->lock);
3098 return MMSYSERR_ERROR;
3100 device->stopped = TRUE;
3102 buf = device->first;
3103 if(buf && buf->dwBytesRecorded > 0){
3104 device->first = buf->lpNext;
3105 }else
3106 buf = NULL;
3108 cb_info = device->cb_info;
3110 LeaveCriticalSection(&device->lock);
3112 if(buf){
3113 buf->dwFlags &= ~WHDR_INQUEUE;
3114 buf->dwFlags |= WHDR_DONE;
3115 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
3118 return MMSYSERR_NOERROR;
3121 /**************************************************************************
3122 * waveInGetPosition [WINMM.@]
3124 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
3125 UINT uSize)
3127 TRACE("(%p, %p, %u)\n", hWaveIn, lpTime, uSize);
3129 if(!uSize || !lpTime || uSize != sizeof(MMTIME))
3130 return MMSYSERR_INVALPARAM;
3132 return WINMM_GetPosition((HWAVE)hWaveIn, lpTime);
3135 /**************************************************************************
3136 * waveInGetID [WINMM.@]
3138 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
3140 UINT dev, junk;
3141 BOOL is_out;
3142 WINMM_Device *device;
3144 TRACE("(%p, %p)\n", hWaveIn, lpuDeviceID);
3146 if(!lpuDeviceID)
3147 return MMSYSERR_INVALPARAM;
3149 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3150 if(!WINMM_ValidateAndLock(device))
3151 return MMSYSERR_INVALHANDLE;
3153 LeaveCriticalSection(&device->lock);
3155 WINMM_DecomposeHWAVE((HWAVE)hWaveIn, lpuDeviceID, &is_out, &dev, &junk);
3157 return MMSYSERR_NOERROR;
3160 /**************************************************************************
3161 * waveInMessage [WINMM.@]
3163 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
3164 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3166 TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
3168 switch(uMessage){
3169 case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
3170 return WINMM_QueryInstanceIDSize(HandleToULong(hWaveIn),
3171 (DWORD_PTR*)dwParam1, FALSE);
3172 case DRV_QUERYFUNCTIONINSTANCEID:
3173 return WINMM_QueryInstanceID(HandleToULong(hWaveIn), (WCHAR*)dwParam1, dwParam2, FALSE);
3174 case DRV_QUERYMAPPABLE:
3175 return MMSYSERR_NOERROR;
3178 TRACE("Message not supported: %u\n", uMessage);
3180 return MMSYSERR_NOTSUPPORTED;
3183 UINT WINAPI mixerGetNumDevs(void)
3185 HRESULT hr;
3187 TRACE("\n");
3189 hr = WINMM_InitMMDevices();
3190 if(FAILED(hr))
3191 return 0;
3193 return g_outmmdevices_count + g_inmmdevices_count;
3196 /**************************************************************************
3197 * mixerGetDevCapsA [WINMM.@]
3199 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
3201 MIXERCAPSW micW;
3202 UINT ret;
3204 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3206 if(!lpCaps)
3207 return MMSYSERR_INVALPARAM;
3209 ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
3211 if (ret == MMSYSERR_NOERROR) {
3212 MIXERCAPSA micA;
3213 micA.wMid = micW.wMid;
3214 micA.wPid = micW.wPid;
3215 micA.vDriverVersion = micW.vDriverVersion;
3216 WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
3217 sizeof(micA.szPname), NULL, NULL );
3218 micA.fdwSupport = micW.fdwSupport;
3219 micA.cDestinations = micW.cDestinations;
3220 memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
3222 return ret;
3225 /**************************************************************************
3226 * mixerGetDevCapsW [WINMM.@]
3228 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
3230 WINMM_MMDevice *mmdevice;
3231 MIXERCAPSW caps;
3232 HRESULT hr;
3234 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3236 hr = WINMM_InitMMDevices();
3237 if(FAILED(hr))
3238 return MMSYSERR_ERROR;
3240 if(!lpCaps)
3241 return MMSYSERR_INVALPARAM;
3243 if(!uSize)
3244 return MMSYSERR_NOERROR;
3246 if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3247 return MMSYSERR_BADDEVICEID;
3249 if(uDeviceID < g_outmmdevices_count){
3250 mmdevice = &g_out_mmdevices[uDeviceID];
3251 memcpy(caps.szPname, mmdevice->out_caps.szPname, sizeof(caps.szPname));
3252 }else{
3253 mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3254 memcpy(caps.szPname, mmdevice->in_caps.szPname, sizeof(caps.szPname));
3257 caps.wMid = 0xFF;
3258 caps.wPid = 0xFF;
3259 caps.vDriverVersion = 0x00010001;
3260 caps.fdwSupport = 0;
3261 caps.cDestinations = 1;
3263 memcpy(lpCaps, &caps, uSize);
3265 return MMSYSERR_NOERROR;
3268 /**************************************************************************
3269 * mixerOpen [WINMM.@]
3271 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
3272 DWORD_PTR dwInstance, DWORD fdwOpen)
3274 WINMM_MMDevice *mmdevice;
3275 MMRESULT mr;
3276 HRESULT hr;
3278 TRACE("(%p, %d, %lx, %lx, %x)\n", lphMix, uDeviceID, dwCallback,
3279 dwInstance, fdwOpen);
3281 hr = WINMM_InitMMDevices();
3282 if(FAILED(hr))
3283 return MMSYSERR_ERROR;
3285 if(!lphMix)
3286 return MMSYSERR_INVALPARAM;
3288 mr = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
3289 if(mr != MMSYSERR_NOERROR)
3290 return mr;
3292 if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3293 return MMSYSERR_BADDEVICEID;
3295 if(uDeviceID < g_outmmdevices_count){
3296 mmdevice = &g_out_mmdevices[uDeviceID];
3297 *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID, TRUE,
3298 mmdevice->mixer_count);
3299 }else{
3300 mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3301 *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID - g_outmmdevices_count,
3302 FALSE, mmdevice->mixer_count);
3305 ++mmdevice->mixer_count;
3307 return MMSYSERR_NOERROR;
3310 /**************************************************************************
3311 * mixerClose [WINMM.@]
3313 UINT WINAPI mixerClose(HMIXER hMix)
3315 TRACE("(%p)\n", hMix);
3317 return MMSYSERR_NOERROR;
3320 /**************************************************************************
3321 * mixerGetID [WINMM.@]
3323 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
3325 WINMM_MMDevice *mmdevice;
3326 HRESULT hr;
3328 TRACE("(%p, %p, %x)\n", hmix, lpid, fdwID);
3330 hr = WINMM_InitMMDevices();
3331 if(FAILED(hr))
3332 return MMSYSERR_ERROR;
3334 if(!lpid)
3335 return MMSYSERR_INVALPARAM;
3337 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwID, lpid);
3338 if(!mmdevice)
3339 return MMSYSERR_INVALHANDLE;
3341 if(mmdevice->in_caps.szPname[0] != '\0')
3342 *lpid += g_outmmdevices_count;
3344 return MMSYSERR_NOERROR;
3347 /**************************************************************************
3348 * mixerGetControlDetailsW [WINMM.@]
3350 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
3351 DWORD fdwDetails)
3353 WINMM_ControlDetails details;
3354 HRESULT hr;
3356 TRACE("(%p, %p, %x)\n", hmix, lpmcdW, fdwDetails);
3358 hr = WINMM_InitMMDevices();
3359 if(FAILED(hr))
3360 return MMSYSERR_ERROR;
3362 if(!lpmcdW)
3363 return MMSYSERR_INVALPARAM;
3365 TRACE("dwControlID: %u\n", lpmcdW->dwControlID);
3367 details.hmix = hmix;
3368 details.details = lpmcdW;
3369 details.flags = fdwDetails;
3371 return SendMessageW(g_devices_hwnd, MXDM_GETCONTROLDETAILS,
3372 (DWORD_PTR)&details, 0);
3375 /**************************************************************************
3376 * mixerGetControlDetailsA [WINMM.@]
3378 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
3379 DWORD fdwDetails)
3381 UINT ret = MMSYSERR_NOTSUPPORTED;
3383 TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
3385 if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
3386 return MMSYSERR_INVALPARAM;
3388 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
3389 case MIXER_GETCONTROLDETAILSF_VALUE:
3390 /* can safely use A structure as it is, no string inside */
3391 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3392 break;
3393 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
3395 MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = lpmcdA->paDetails;
3396 MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
3397 int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3398 unsigned int i;
3400 if (lpmcdA->u.cMultipleItems != 0) {
3401 size *= lpmcdA->u.cMultipleItems;
3403 pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
3404 lpmcdA->paDetails = pDetailsW;
3405 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3406 /* set up lpmcd->paDetails */
3407 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3408 /* copy from lpmcd->paDetails back to paDetailsW; */
3409 if (ret == MMSYSERR_NOERROR) {
3410 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
3411 pDetailsA->dwParam1 = pDetailsW->dwParam1;
3412 pDetailsA->dwParam2 = pDetailsW->dwParam2;
3413 WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
3414 pDetailsA->szName,
3415 sizeof(pDetailsA->szName), NULL, NULL );
3416 pDetailsA++;
3417 pDetailsW++;
3419 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3420 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3422 HeapFree(GetProcessHeap(), 0, pDetailsW);
3423 lpmcdA->paDetails = pDetailsA;
3424 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
3426 break;
3427 default:
3428 WARN("Unsupported fdwDetails=0x%08x\n", fdwDetails);
3431 return ret;
3434 /**************************************************************************
3435 * mixerGetLineControlsA [WINMM.@]
3437 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
3438 DWORD fdwControls)
3440 MIXERLINECONTROLSW mlcW;
3441 DWORD ret;
3442 unsigned int i;
3444 TRACE("(%p, %p, %x)\n", hmix, lpmlcA, fdwControls);
3446 if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
3447 lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
3448 return MMSYSERR_INVALPARAM;
3450 mlcW.cbStruct = sizeof(mlcW);
3451 mlcW.dwLineID = lpmlcA->dwLineID;
3452 mlcW.u.dwControlID = lpmlcA->u.dwControlID;
3453 mlcW.u.dwControlType = lpmlcA->u.dwControlType;
3455 /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
3456 the control count is assumed to be 1 - This is relied upon by a game,
3457 "Dynomite Deluze" */
3458 if (MIXER_GETLINECONTROLSF_ONEBYTYPE == (fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK)) {
3459 mlcW.cControls = 1;
3460 } else {
3461 mlcW.cControls = lpmlcA->cControls;
3463 mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
3464 mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
3465 mlcW.cControls * mlcW.cbmxctrl);
3467 ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
3469 if (ret == MMSYSERR_NOERROR) {
3470 lpmlcA->dwLineID = mlcW.dwLineID;
3471 lpmlcA->u.dwControlID = mlcW.u.dwControlID;
3472 lpmlcA->u.dwControlType = mlcW.u.dwControlType;
3474 for (i = 0; i < mlcW.cControls; i++) {
3475 lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
3476 lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
3477 lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
3478 lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
3479 lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
3480 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
3481 lpmlcA->pamxctrl[i].szShortName,
3482 sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
3483 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
3484 lpmlcA->pamxctrl[i].szName,
3485 sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
3486 /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
3487 * sizeof(mlcW.pamxctrl[i].Bounds) */
3488 memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
3489 sizeof(mlcW.pamxctrl[i].Bounds));
3490 /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
3491 * sizeof(mlcW.pamxctrl[i].Metrics) */
3492 memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
3493 sizeof(mlcW.pamxctrl[i].Metrics));
3497 HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
3499 return ret;
3502 static UINT WINMM_GetVolumeLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3503 MIXERCONTROLW *ctl, DWORD flags)
3505 ctl->dwControlID = (line == 0xFFFF0000) ? 0 : 2;
3506 ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
3507 ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3508 ctl->cMultipleItems = 0;
3509 lstrcpyW(ctl->szShortName, volumeW);
3510 lstrcpyW(ctl->szName, volumeW);
3511 ctl->Bounds.s1.dwMinimum = 0;
3512 ctl->Bounds.s1.dwMaximum = 0xFFFF;
3513 ctl->Metrics.cSteps = 192;
3515 return MMSYSERR_NOERROR;
3518 static UINT WINMM_GetMuteLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3519 MIXERCONTROLW *ctl, DWORD flags)
3521 ctl->dwControlID = (line == 0xFFFF0000) ? 1 : 3;
3522 ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
3523 ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3524 ctl->cMultipleItems = 0;
3525 lstrcpyW(ctl->szShortName, muteW);
3526 lstrcpyW(ctl->szName, muteW);
3527 ctl->Bounds.s1.dwMinimum = 0;
3528 ctl->Bounds.s1.dwMaximum = 1;
3529 ctl->Metrics.cSteps = 0;
3531 return MMSYSERR_NOERROR;
3534 /**************************************************************************
3535 * mixerGetLineControlsW [WINMM.@]
3537 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
3538 DWORD fdwControls)
3540 WINMM_MMDevice *mmdevice;
3541 HRESULT hr;
3543 TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
3545 hr = WINMM_InitMMDevices();
3546 if(FAILED(hr))
3547 return MMSYSERR_ERROR;
3549 if(fdwControls & ~(MIXER_GETLINECONTROLSF_ALL |
3550 MIXER_GETLINECONTROLSF_ONEBYID |
3551 MIXER_GETLINECONTROLSF_ONEBYTYPE |
3552 MIXER_OBJECTF_HMIXER |
3553 MIXER_OBJECTF_MIXER)){
3554 WARN("Unknown GetLineControls flag: %x\n", fdwControls);
3555 return MMSYSERR_INVALFLAG;
3558 if(!lpmlcW || lpmlcW->cbStruct < sizeof(*lpmlcW) || !lpmlcW->pamxctrl)
3559 return MMSYSERR_INVALPARAM;
3561 TRACE("dwLineID: %u\n", lpmlcW->dwLineID);
3562 TRACE("dwControl: %x\n", lpmlcW->u.dwControlID);
3563 TRACE("cControls: %u\n", lpmlcW->cControls);
3565 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwControls, NULL);
3566 if(!mmdevice)
3567 return MMSYSERR_INVALHANDLE;
3569 switch(fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK){
3570 case MIXER_GETLINECONTROLSF_ALL:
3571 if(lpmlcW->cControls != 2)
3572 return MMSYSERR_INVALPARAM;
3573 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3574 return MMSYSERR_INVALPARAM;
3575 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3576 return MIXERR_INVALLINE;
3577 WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3578 &lpmlcW->pamxctrl[0], fdwControls);
3579 WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3580 &lpmlcW->pamxctrl[1], fdwControls);
3581 return MMSYSERR_NOERROR;
3582 case MIXER_GETLINECONTROLSF_ONEBYID:
3583 if(lpmlcW->cControls != 1)
3584 return MMSYSERR_INVALPARAM;
3585 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3586 return MMSYSERR_INVALPARAM;
3587 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3588 return MIXERR_INVALLINE;
3589 if(lpmlcW->u.dwControlID == 0)
3590 return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3591 lpmlcW->pamxctrl, fdwControls);
3592 if(lpmlcW->u.dwControlID == 1)
3593 return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3594 lpmlcW->pamxctrl, fdwControls);
3595 return MMSYSERR_NOTSUPPORTED;
3596 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
3597 if(lpmlcW->cControls != 1)
3598 return MMSYSERR_INVALPARAM;
3599 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3600 return MMSYSERR_INVALPARAM;
3601 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3602 return MIXERR_INVALLINE;
3603 if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
3604 return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3605 lpmlcW->pamxctrl, fdwControls);
3606 if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
3607 return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3608 lpmlcW->pamxctrl, fdwControls);
3609 return MMSYSERR_NOTSUPPORTED;
3612 return MMSYSERR_NOTSUPPORTED;
3615 static UINT WINMM_GetSourceLineInfo(WINMM_MMDevice *mmdevice, UINT mmdev_index,
3616 MIXERLINEW *info, DWORD flags)
3618 BOOL is_out = TRUE;
3619 if(mmdevice->in_caps.szPname[0] != '\0')
3620 is_out = FALSE;
3622 if(info->dwSource != 0)
3623 return MIXERR_INVALLINE;
3625 info->dwDestination = 0;
3626 info->dwLineID = 0;
3627 info->fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
3628 info->cConnections = 0;
3629 info->cControls = 2;
3630 /* volume & mute always affect all channels, so claim 1 channel */
3631 info->cChannels = 1;
3632 info->Target.dwDeviceID = mmdev_index;
3633 info->Target.wMid = ~0;
3634 info->Target.wPid = ~0;
3635 info->Target.vDriverVersion = 0;
3637 lstrcpyW(info->szShortName, volumeW);
3638 lstrcpyW(info->szName, mastervolumeW);
3640 if(is_out){
3641 info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
3642 info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
3643 memcpy(info->Target.szPname, mmdevice->out_caps.szPname,
3644 sizeof(info->Target.szPname));
3645 }else{
3646 info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
3647 info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3648 info->Target.szPname[0] = '\0';
3651 return MMSYSERR_NOERROR;
3654 static UINT WINMM_GetDestinationLineInfo(WINMM_MMDevice *mmdevice,
3655 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3657 BOOL is_out = TRUE;
3658 if(mmdevice->in_caps.szPname[0] != '\0')
3659 is_out = FALSE;
3661 if(info->dwDestination != 0)
3662 return MIXERR_INVALLINE;
3664 info->dwSource = 0xFFFFFFFF;
3665 info->dwLineID = 0xFFFF0000;
3666 info->fdwLine = MIXERLINE_LINEF_ACTIVE;
3667 info->cConnections = 1;
3668 info->cControls = 2;
3670 lstrcpyW(info->szShortName, volumeW);
3671 lstrcpyW(info->szName, mastervolumeW);
3673 info->Target.dwDeviceID = mmdev_index;
3674 info->Target.wMid = ~0;
3675 info->Target.wPid = ~0;
3676 info->Target.vDriverVersion = 0;
3678 if(is_out){
3679 info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
3680 info->cChannels = mmdevice->out_caps.wChannels;
3681 info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3682 info->Target.szPname[0] = '\0';
3683 }else{
3684 info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
3685 info->cChannels = mmdevice->in_caps.wChannels;
3686 info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
3687 memcpy(info->Target.szPname, mmdevice->in_caps.szPname,
3688 sizeof(info->Target.szPname));
3691 return MMSYSERR_NOERROR;
3694 static UINT WINMM_GetComponentTypeLineInfo(WINMM_MMDevice *mmdevice,
3695 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3697 BOOL is_out = TRUE;
3698 if(mmdevice->in_caps.szPname[0] != '\0')
3699 is_out = FALSE;
3701 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN){
3702 if(is_out)
3703 return MIXERR_INVALLINE;
3704 info->dwDestination = 0;
3705 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3708 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS){
3709 if(!is_out)
3710 return MIXERR_INVALLINE;
3711 info->dwDestination = 0;
3712 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3715 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE){
3716 if(is_out)
3717 return MIXERR_INVALLINE;
3718 info->dwSource = 0;
3719 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3722 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
3723 if(!is_out)
3724 return MIXERR_INVALLINE;
3725 info->dwSource = 0;
3726 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3729 TRACE("Returning INVALLINE on this component type: %u\n",
3730 info->dwComponentType);
3732 return MIXERR_INVALLINE;
3735 static UINT WINMM_GetLineIDLineInfo(WINMM_MMDevice *mmdevice,
3736 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3738 if(info->dwLineID == 0xFFFF0000){
3739 info->dwDestination = 0;
3740 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3743 if(info->dwLineID == 0){
3744 info->dwSource = 0;
3745 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3748 TRACE("Returning INVALLINE on this dwLineID: %u\n", info->dwLineID);
3749 return MIXERR_INVALLINE;
3752 /**************************************************************************
3753 * mixerGetLineInfoW [WINMM.@]
3755 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
3757 UINT mmdev_index;
3758 WINMM_MMDevice *mmdevice;
3759 HRESULT hr;
3761 TRACE("(%p, %p, %x)\n", hmix, lpmliW, fdwInfo);
3763 hr = WINMM_InitMMDevices();
3764 if(FAILED(hr))
3765 return MMSYSERR_ERROR;
3767 if(!lpmliW || lpmliW->cbStruct < sizeof(MIXERLINEW))
3768 return MMSYSERR_INVALPARAM;
3770 TRACE("dwDestination: %u\n", lpmliW->dwDestination);
3771 TRACE("dwSource: %u\n", lpmliW->dwSource);
3772 TRACE("dwLineID: %u\n", lpmliW->dwLineID);
3773 TRACE("fdwLine: 0x%x\n", lpmliW->fdwLine);
3774 TRACE("dwComponentType: 0x%x\n", lpmliW->dwComponentType);
3776 if(fdwInfo & ~(MIXER_GETLINEINFOF_COMPONENTTYPE |
3777 MIXER_GETLINEINFOF_DESTINATION |
3778 MIXER_GETLINEINFOF_LINEID |
3779 MIXER_GETLINEINFOF_SOURCE |
3780 MIXER_GETLINEINFOF_TARGETTYPE |
3781 MIXER_OBJECTF_HMIXER |
3782 MIXER_OBJECTF_MIXER)){
3783 WARN("Unknown GetLineInfo flag: %x\n", fdwInfo);
3784 return MMSYSERR_INVALFLAG;
3787 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwInfo, &mmdev_index);
3788 if(!mmdevice)
3789 return MMSYSERR_INVALHANDLE;
3791 switch(fdwInfo & MIXER_GETLINEINFOF_QUERYMASK){
3792 case MIXER_GETLINEINFOF_DESTINATION:
3793 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, lpmliW,
3794 fdwInfo);
3795 case MIXER_GETLINEINFOF_SOURCE:
3796 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3797 case MIXER_GETLINEINFOF_COMPONENTTYPE:
3798 return WINMM_GetComponentTypeLineInfo(mmdevice, mmdev_index, lpmliW,
3799 fdwInfo);
3800 case MIXER_GETLINEINFOF_LINEID:
3801 return WINMM_GetLineIDLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3802 case MIXER_GETLINEINFOF_TARGETTYPE:
3803 FIXME("TARGETTYPE flag not implemented!\n");
3804 return MIXERR_INVALLINE;
3807 TRACE("Returning INVALFLAG on these flags: %x\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
3808 return MMSYSERR_INVALFLAG;
3811 /**************************************************************************
3812 * mixerGetLineInfoA [WINMM.@]
3814 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
3815 DWORD fdwInfo)
3817 MIXERLINEW mliW;
3818 UINT ret;
3820 TRACE("(%p, %p, %x)\n", hmix, lpmliA, fdwInfo);
3822 if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
3823 return MMSYSERR_INVALPARAM;
3825 mliW.cbStruct = sizeof(mliW);
3826 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
3827 case MIXER_GETLINEINFOF_COMPONENTTYPE:
3828 mliW.dwComponentType = lpmliA->dwComponentType;
3829 break;
3830 case MIXER_GETLINEINFOF_DESTINATION:
3831 mliW.dwDestination = lpmliA->dwDestination;
3832 break;
3833 case MIXER_GETLINEINFOF_LINEID:
3834 mliW.dwLineID = lpmliA->dwLineID;
3835 break;
3836 case MIXER_GETLINEINFOF_SOURCE:
3837 mliW.dwDestination = lpmliA->dwDestination;
3838 mliW.dwSource = lpmliA->dwSource;
3839 break;
3840 case MIXER_GETLINEINFOF_TARGETTYPE:
3841 mliW.Target.dwType = lpmliA->Target.dwType;
3842 mliW.Target.wMid = lpmliA->Target.wMid;
3843 mliW.Target.wPid = lpmliA->Target.wPid;
3844 mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
3845 MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname)/sizeof(WCHAR));
3846 break;
3847 default:
3848 WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
3849 return MMSYSERR_INVALFLAG;
3852 ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
3854 if(ret == MMSYSERR_NOERROR)
3856 lpmliA->dwDestination = mliW.dwDestination;
3857 lpmliA->dwSource = mliW.dwSource;
3858 lpmliA->dwLineID = mliW.dwLineID;
3859 lpmliA->fdwLine = mliW.fdwLine;
3860 lpmliA->dwUser = mliW.dwUser;
3861 lpmliA->dwComponentType = mliW.dwComponentType;
3862 lpmliA->cChannels = mliW.cChannels;
3863 lpmliA->cConnections = mliW.cConnections;
3864 lpmliA->cControls = mliW.cControls;
3865 WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
3866 sizeof(lpmliA->szShortName), NULL, NULL);
3867 WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
3868 sizeof(lpmliA->szName), NULL, NULL );
3869 lpmliA->Target.dwType = mliW.Target.dwType;
3870 lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
3871 lpmliA->Target.wMid = mliW.Target.wMid;
3872 lpmliA->Target.wPid = mliW.Target.wPid;
3873 lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
3874 WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
3875 sizeof(lpmliA->Target.szPname), NULL, NULL );
3877 return ret;
3880 /**************************************************************************
3881 * mixerSetControlDetails [WINMM.@]
3883 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
3884 DWORD fdwDetails)
3886 WINMM_ControlDetails details;
3888 TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
3890 if(!WINMM_StartDevicesThread())
3891 return MMSYSERR_ERROR;
3893 if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
3894 MIXER_SETCONTROLDETAILSF_CUSTOM)
3895 return MMSYSERR_NOTSUPPORTED;
3897 if(!lpmcd)
3898 return MMSYSERR_INVALPARAM;
3900 TRACE("dwControlID: %u\n", lpmcd->dwControlID);
3902 details.hmix = hmix;
3903 details.details = lpmcd;
3904 details.flags = fdwDetails;
3906 return SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
3907 (DWORD_PTR)&details, 0);
3910 /**************************************************************************
3911 * mixerMessage [WINMM.@]
3913 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3915 TRACE("(%p, %d, %lx, %lx)\n", hmix, uMsg, dwParam1, dwParam2);
3917 return MMSYSERR_NOTSUPPORTED;