dsound: Always enumerate the default device first.
[wine] / dlls / winmm / waveform.c
1 /*
2  * Copyright 1993      Martin Ayotte
3  *           1998-2002 Eric Pouech
4  *           2011 Andrew Eikum for CodeWeavers
5  *
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.
10  *
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.
15  *
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
19  */
20
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
24
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"
37
38 #include "winemm.h"
39
40 #include "ole2.h"
41 #include "initguid.h"
42 #include "devpkey.h"
43 #include "mmdeviceapi.h"
44 #include "audioclient.h"
45 #include "audiopolicy.h"
46
47 #include "wine/debug.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
50
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};
56
57 /* HWAVE (and HMIXER) format:
58  *
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
65  *
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.
69  */
70
71 #define MAX_DEVICES 256
72
73 typedef struct _WINMM_CBInfo {
74     DWORD_PTR callback;
75     DWORD_PTR user;
76     DWORD flags;
77     HWAVE hwave;
78 } WINMM_CBInfo;
79
80 struct _WINMM_MMDevice;
81 typedef struct _WINMM_MMDevice WINMM_MMDevice;
82
83 typedef struct _WINMM_Device {
84     WINMM_CBInfo cb_info;
85
86     HWAVE handle;
87
88     BOOL open;
89
90     IMMDevice *device;
91     IAudioClient *client;
92     IAudioRenderClient *render;
93     IAudioCaptureClient *capture;
94     IAudioClock *clock;
95     IAudioStreamVolume *volume;
96
97     HACMSTREAM acm_handle;
98     ACMSTREAMHEADER acm_hdr;
99     UINT32 acm_offs;
100
101     WAVEHDR *first, *last, *playing, *loop_start;
102
103     BOOL stopped;
104     DWORD loop_counter;
105     UINT32 bytes_per_frame, samples_per_sec, ofs_bytes, played_frames;
106
107     /* stored in frames of sample rate, *not* AC::GetFrequency */
108     UINT64 last_clock_pos;
109
110     HANDLE event;
111     CRITICAL_SECTION lock;
112
113     WINMM_MMDevice *parent;
114 } WINMM_Device;
115
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;
120
121     ISimpleAudioVolume *volume;
122
123     GUID session;
124
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;
128
129     CRITICAL_SECTION lock;
130
131     WINMM_Device *devices[MAX_DEVICES];
132 };
133
134 static WINMM_MMDevice *g_out_mmdevices;
135 static UINT g_outmmdevices_count;
136
137 static WINMM_MMDevice *g_in_mmdevices;
138 static UINT g_inmmdevices_count;
139
140 static IMMDeviceEnumerator *g_devenum;
141
142 static CRITICAL_SECTION g_devthread_lock;
143 static HANDLE g_devices_thread;
144 static HWND g_devices_hwnd;
145
146 static UINT g_devhandle_count;
147 static HANDLE *g_device_handles;
148 static WINMM_Device **g_handle_devices;
149
150 typedef struct _WINMM_OpenInfo {
151     HWAVE handle;
152     UINT req_device;
153     WAVEFORMATEX *format;
154     DWORD_PTR callback;
155     DWORD_PTR cb_user;
156     DWORD flags;
157 } WINMM_OpenInfo;
158
159 typedef struct _WINMM_ControlDetails {
160     HMIXEROBJ hmix;
161     MIXERCONTROLDETAILS *details;
162     DWORD flags;
163 } WINMM_ControlDetails;
164
165 static LRESULT WOD_Open(WINMM_OpenInfo *info);
166 static LRESULT WOD_Close(HWAVEOUT hwave);
167 static LRESULT WID_Open(WINMM_OpenInfo *info);
168 static LRESULT WID_Close(HWAVEIN hwave);
169
170 BOOL WINMM_InitWaveform(void)
171 {
172     InitializeCriticalSection(&g_devthread_lock);
173     return TRUE;
174 }
175
176 static inline HWAVE WINMM_MakeHWAVE(UINT mmdevice, BOOL is_out, UINT device)
177 {
178     return ULongToHandle((1 << 15) | ((!!is_out) << 14) |
179             (mmdevice << 8) | device);
180 }
181
182 static inline void WINMM_DecomposeHWAVE(HWAVE hwave, UINT *mmdevice_index,
183         BOOL *is_out, UINT *device_index, UINT *junk)
184 {
185     ULONG32 l = HandleToULong(hwave);
186     *device_index = l & 0xFF;
187     *mmdevice_index = (l >> 8) & 0x3F;
188     *is_out = (l >> 14) & 0x1;
189     *junk = l >> 15;
190 }
191
192 static void WINMM_InitDevice(WINMM_Device *device,
193         WINMM_MMDevice *parent, HWAVE hwave)
194 {
195     InitializeCriticalSection(&device->lock);
196     device->handle = hwave;
197     device->parent = parent;
198 }
199
200 /* finds the first unused Device, marks it as "open", and returns
201  * a pointer to the device
202  *
203  * IMPORTANT: it is the caller's responsibility to release the device's lock
204  * on success
205  */
206 static WINMM_Device *WINMM_FindUnusedDevice(BOOL is_out, UINT mmdevice_index)
207 {
208     WINMM_MMDevice *mmdevice;
209     UINT i;
210
211     if(is_out)
212         mmdevice = &g_out_mmdevices[mmdevice_index];
213     else
214         mmdevice = &g_in_mmdevices[mmdevice_index];
215
216     EnterCriticalSection(&mmdevice->lock);
217     for(i = 0; i < MAX_DEVICES; ++i){
218         WINMM_Device *device = mmdevice->devices[i];
219
220         if(!device){
221             device = mmdevice->devices[i] = HeapAlloc(GetProcessHeap(),
222                     HEAP_ZERO_MEMORY, sizeof(WINMM_Device));
223             if(!device){
224                 LeaveCriticalSection(&mmdevice->lock);
225                 return NULL;
226             }
227
228             WINMM_InitDevice(device, mmdevice,
229                     WINMM_MakeHWAVE(mmdevice_index, is_out, i));
230             EnterCriticalSection(&device->lock);
231         }else
232             EnterCriticalSection(&device->lock);
233
234         if(!device->open){
235             LeaveCriticalSection(&mmdevice->lock);
236             device->open = TRUE;
237             TRACE("Found free device: mmdevice: %u, device id: %u\n",
238                     mmdevice_index, i);
239             return device;
240         }
241
242         LeaveCriticalSection(&device->lock);
243     }
244
245     LeaveCriticalSection(&mmdevice->lock);
246
247     TRACE("All devices in use: mmdevice: %u\n", mmdevice_index);
248
249     return NULL;
250 }
251
252 static inline BOOL WINMM_ValidateAndLock(WINMM_Device *device)
253 {
254     if(!device)
255         return FALSE;
256
257     EnterCriticalSection(&device->lock);
258
259     if(!device->open){
260         LeaveCriticalSection(&device->lock);
261         return FALSE;
262     }
263
264     return TRUE;
265 }
266
267 static WINMM_Device *WINMM_GetDeviceFromHWAVE(HWAVE hwave)
268 {
269     WINMM_MMDevice *mmdevice;
270     WINMM_Device *device;
271     UINT mmdevice_index, device_index, junk;
272     BOOL is_out;
273
274     WINMM_DecomposeHWAVE(hwave, &mmdevice_index, &is_out, &device_index, &junk);
275
276     if(junk != 0x1)
277         return NULL;
278
279     if(mmdevice_index >= (is_out ? g_outmmdevices_count : g_inmmdevices_count))
280         return NULL;
281
282     if(is_out)
283         mmdevice = &g_out_mmdevices[mmdevice_index];
284     else
285         mmdevice = &g_in_mmdevices[mmdevice_index];
286
287     EnterCriticalSection(&mmdevice->lock);
288
289     device = mmdevice->devices[device_index];
290
291     LeaveCriticalSection(&mmdevice->lock);
292
293     return device;
294 }
295
296 /* Note: NotifyClient should never be called while holding the device lock
297  * since the client may call wave* functions from within the callback. */
298 static inline void WINMM_NotifyClient(WINMM_CBInfo *info, WORD msg, DWORD_PTR param1,
299         DWORD_PTR param2)
300 {
301     DriverCallback(info->callback, info->flags, (HDRVR)info->hwave,
302         msg, info->user, param1, param2);
303 }
304
305 static HRESULT WINMM_GetFriendlyName(IMMDevice *device, WCHAR *out,
306         UINT outlen)
307 {
308     IPropertyStore *ps;
309     PROPVARIANT var;
310     HRESULT hr;
311
312     hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
313     if(FAILED(hr))
314         return hr;
315
316     PropVariantInit(&var);
317
318     hr = IPropertyStore_GetValue(ps,
319             (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &var);
320     if(FAILED(hr)){
321         IPropertyStore_Release(ps);
322         return hr;
323     }
324
325     lstrcpynW(out, var.u.pwszVal, outlen);
326
327     PropVariantClear(&var);
328
329     IPropertyStore_Release(ps);
330
331     return S_OK;
332 }
333
334 static HRESULT WINMM_TestFormat(IAudioClient *client, DWORD rate, DWORD depth,
335         WORD channels)
336 {
337     WAVEFORMATEX fmt, *junk;
338     HRESULT hr;
339
340     fmt.wFormatTag = WAVE_FORMAT_PCM;
341     fmt.nChannels = channels;
342     fmt.nSamplesPerSec = rate;
343     fmt.wBitsPerSample = depth;
344     fmt.nBlockAlign = (channels * depth) / 8;
345     fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
346     fmt.cbSize = 0;
347
348     hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED,
349             &fmt, &junk);
350     if(SUCCEEDED(hr))
351         CoTaskMemFree(junk);
352
353     return hr;
354 }
355
356 static struct _TestFormat {
357     DWORD flag;
358     DWORD rate;
359     DWORD depth;
360     WORD channels;
361 } formats_to_test[] = {
362     { WAVE_FORMAT_1M08, 11025, 8, 1 },
363     { WAVE_FORMAT_1M16, 11025, 16, 1 },
364     { WAVE_FORMAT_1S08, 11025, 8, 2 },
365     { WAVE_FORMAT_1S16, 11025, 16, 2 },
366     { WAVE_FORMAT_2M08, 22050, 8, 1 },
367     { WAVE_FORMAT_2M16, 22050, 16, 1 },
368     { WAVE_FORMAT_2S08, 22050, 8, 2 },
369     { WAVE_FORMAT_2S16, 22050, 16, 2 },
370     { WAVE_FORMAT_4M08, 44100, 8, 1 },
371     { WAVE_FORMAT_4M16, 44100, 16, 1 },
372     { WAVE_FORMAT_4S08, 44100, 8, 2 },
373     { WAVE_FORMAT_4S16, 44100, 16, 2 },
374     { WAVE_FORMAT_48M08, 48000, 8, 1 },
375     { WAVE_FORMAT_48M16, 48000, 16, 1 },
376     { WAVE_FORMAT_48S08, 48000, 8, 2 },
377     { WAVE_FORMAT_48S16, 48000, 16, 2 },
378     { WAVE_FORMAT_96M08, 96000, 8, 1 },
379     { WAVE_FORMAT_96M16, 96000, 16, 1 },
380     { WAVE_FORMAT_96S08, 96000, 8, 2 },
381     { WAVE_FORMAT_96S16, 96000, 16, 2 },
382     {0}
383 };
384
385 static DWORD WINMM_GetSupportedFormats(IMMDevice *device)
386 {
387     DWORD flags = 0;
388     HRESULT hr;
389     struct _TestFormat *fmt;
390     IAudioClient *client;
391
392     hr = IMMDevice_Activate(device, &IID_IAudioClient,
393             CLSCTX_INPROC_SERVER, NULL, (void**)&client);
394     if(FAILED(hr))
395         return 0;
396
397     for(fmt = formats_to_test; fmt->flag; ++fmt){
398         hr = WINMM_TestFormat(client, fmt->rate, fmt->depth, fmt->channels);
399         if(hr == S_OK)
400             flags |= fmt->flag;
401     }
402
403     IAudioClient_Release(client);
404
405     return flags;
406 }
407
408 static HRESULT WINMM_InitMMDevice(EDataFlow flow, IMMDevice *device,
409         WINMM_MMDevice *dev, UINT index)
410 {
411     HRESULT hr;
412
413     if(flow == eRender){
414         dev->out_caps.wMid = 0xFF;
415         dev->out_caps.wPid = 0xFF;
416         dev->out_caps.vDriverVersion = 0x00010001;
417         dev->out_caps.dwFormats = WINMM_GetSupportedFormats(device);
418         dev->out_caps.wReserved1 = 0;
419         dev->out_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
420             WAVECAPS_SAMPLEACCURATE;
421         dev->out_caps.wChannels = 2;
422         dev->out_caps.szPname[0] = '\0';
423
424         hr = WINMM_GetFriendlyName(device, dev->out_caps.szPname,
425                 sizeof(dev->out_caps.szPname) /
426                 sizeof(*dev->out_caps.szPname));
427         if(FAILED(hr))
428             return hr;
429     }else{
430         dev->in_caps.wMid = 0xFF;
431         dev->in_caps.wPid = 0xFF;
432         dev->in_caps.vDriverVersion = 0x00010001;
433         dev->in_caps.dwFormats = WINMM_GetSupportedFormats(device);
434         dev->in_caps.wReserved1 = 0;
435         dev->in_caps.wChannels = 2;
436         dev->in_caps.szPname[0] = '\0';
437
438         hr = WINMM_GetFriendlyName(device, dev->in_caps.szPname,
439                 sizeof(dev->in_caps.szPname) /
440                 sizeof(*dev->in_caps.szPname));
441         if(FAILED(hr))
442             return hr;
443     }
444
445     hr = IMMDevice_GetId(device, &dev->dev_id);
446     if(FAILED(hr))
447         return hr;
448
449     CoCreateGuid(&dev->session);
450
451     InitializeCriticalSection(&dev->lock);
452
453     return S_OK;
454 }
455
456 static HRESULT WINMM_EnumDevices(WINMM_MMDevice **devices, UINT *devcount,
457         EDataFlow flow)
458 {
459     IMMDeviceCollection *devcoll;
460     HRESULT hr;
461
462     hr = IMMDeviceEnumerator_EnumAudioEndpoints(g_devenum, flow,
463             DEVICE_STATE_ACTIVE, &devcoll);
464     if(FAILED(hr))
465         return hr;
466
467     hr = IMMDeviceCollection_GetCount(devcoll, devcount);
468     if(FAILED(hr)){
469         IMMDeviceCollection_Release(devcoll);
470         return hr;
471     }
472
473     if(*devcount > 0){
474         UINT n, count;
475         IMMDevice *def_dev = NULL;
476
477         *devices = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
478                 sizeof(WINMM_MMDevice) * (*devcount));
479         if(!*devices){
480             IMMDeviceCollection_Release(devcoll);
481             return E_OUTOFMEMORY;
482         }
483
484         count = 0;
485
486         /* make sure that device 0 is the default device */
487         hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(g_devenum,
488                 flow, eConsole, &def_dev);
489         if(SUCCEEDED(hr)){
490             WINMM_InitMMDevice(flow, def_dev, &(*devices)[0], 0);
491             count = 1;
492         }
493
494         for(n = 0; n < *devcount; ++n){
495             IMMDevice *device;
496
497             hr = IMMDeviceCollection_Item(devcoll, n, &device);
498             if(SUCCEEDED(hr)){
499                 if(device != def_dev){
500                     WINMM_InitMMDevice(flow, device, &(*devices)[count], count);
501                     ++count;
502                 }
503
504                 IMMDevice_Release(device);
505             }
506         }
507
508         if(def_dev)
509             IMMDevice_Release(def_dev);
510
511         *devcount = count;
512     }
513
514     IMMDeviceCollection_Release(devcoll);
515
516     return S_OK;
517 }
518
519 static HRESULT WINMM_InitMMDevices(void)
520 {
521     HRESULT hr;
522
523     hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
524             CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&g_devenum);
525     if(FAILED(hr))
526         return hr;
527
528     hr = WINMM_EnumDevices(&g_out_mmdevices, &g_outmmdevices_count, eRender);
529     if(FAILED(hr)){
530         g_outmmdevices_count = 0;
531         g_inmmdevices_count = 0;
532         return hr;
533     }
534
535     hr = WINMM_EnumDevices(&g_in_mmdevices, &g_inmmdevices_count, eCapture);
536     if(FAILED(hr)){
537         g_inmmdevices_count = 0;
538         return hr;
539     }
540
541     return S_OK;
542 }
543
544 static inline BOOL WINMM_IsMapper(UINT device)
545 {
546     return (device == WAVE_MAPPER || device == (UINT16)WAVE_MAPPER);
547 }
548
549 static MMRESULT WINMM_TryDeviceMapping(WINMM_OpenInfo *info, WORD channels,
550         DWORD freq, DWORD bits_per_samp, BOOL is_out)
551 {
552     WINMM_Device *device;
553     WAVEFORMATEX target;
554     MMRESULT mr;
555     UINT i;
556
557     TRACE("format: %u, channels: %u, sample rate: %u, bit depth: %u\n",
558             WAVE_FORMAT_PCM, channels, freq, bits_per_samp);
559
560     target.wFormatTag = WAVE_FORMAT_PCM;
561     target.nChannels = channels;
562     target.nSamplesPerSec = freq;
563     target.wBitsPerSample = bits_per_samp;
564     target.nBlockAlign = (target.nChannels * target.wBitsPerSample) / 8;
565     target.nAvgBytesPerSec = target.nSamplesPerSec * target.nBlockAlign;
566     target.cbSize = 0;
567
568     if(is_out)
569         mr = acmStreamOpen(NULL, NULL, info->format, &target, NULL, 0,
570                 0, ACM_STREAMOPENF_QUERY);
571     else
572         mr = acmStreamOpen(NULL, NULL, &target, info->format, NULL, 0,
573                 0, ACM_STREAMOPENF_QUERY);
574     if(mr != MMSYSERR_NOERROR)
575         return mr;
576
577     /* ACM can convert from src->dst, so try to find a device
578      * that supports dst */
579     if(is_out){
580         if(WINMM_IsMapper(info->req_device)){
581             for(i = 0; i < g_outmmdevices_count; ++i){
582                 WINMM_OpenInfo l_info = *info;
583                 l_info.req_device = i;
584                 l_info.format = &target;
585                 mr = WOD_Open(&l_info);
586                 if(mr == MMSYSERR_NOERROR){
587                     info->handle = l_info.handle;
588                     break;
589                 }
590             }
591         }else{
592             WINMM_OpenInfo l_info = *info;
593             l_info.flags &= ~WAVE_MAPPED;
594             l_info.format = &target;
595             mr = WOD_Open(&l_info);
596             if(mr == MMSYSERR_NOERROR)
597                 info->handle = l_info.handle;
598         }
599     }else{
600         if(WINMM_IsMapper(info->req_device)){
601             for(i = 0; i < g_inmmdevices_count; ++i){
602                 WINMM_OpenInfo l_info = *info;
603                 l_info.req_device = i;
604                 l_info.format = &target;
605                 mr = WID_Open(&l_info);
606                 if(mr == MMSYSERR_NOERROR){
607                     info->handle = l_info.handle;
608                     break;
609                 }
610             }
611         }else{
612             WINMM_OpenInfo l_info = *info;
613             l_info.flags &= ~WAVE_MAPPED;
614             l_info.format = &target;
615             mr = WID_Open(&l_info);
616             if(mr == MMSYSERR_NOERROR)
617                 info->handle = l_info.handle;
618         }
619     }
620     if(mr != MMSYSERR_NOERROR)
621         return WAVERR_BADFORMAT;
622
623     device = WINMM_GetDeviceFromHWAVE(info->handle);
624     if(!device)
625         return MMSYSERR_INVALHANDLE;
626
627     /* set up the ACM stream */
628     if(is_out)
629         mr = acmStreamOpen(&device->acm_handle, NULL, info->format, &target,
630                 NULL, 0, 0, 0);
631     else
632         mr = acmStreamOpen(&device->acm_handle, NULL, &target, info->format,
633                 NULL, 0, 0, 0);
634     if(mr != MMSYSERR_NOERROR){
635         if(is_out)
636             WOD_Close((HWAVEOUT)info->handle);
637         else
638             WID_Close((HWAVEIN)info->handle);
639         return mr;
640     }
641
642     TRACE("Success\n");
643     return MMSYSERR_NOERROR;
644 }
645
646 static MMRESULT WINMM_MapDevice(WINMM_OpenInfo *info, BOOL is_out)
647 {
648     UINT i;
649     MMRESULT mr;
650     WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)info->format;
651
652     TRACE("(%p, %d)\n", info, is_out);
653
654     /* try to find a direct match */
655     if(is_out){
656         WINMM_OpenInfo l_info = *info;
657         if(WINMM_IsMapper(info->req_device)){
658             for(i = 0; i < g_outmmdevices_count; ++i){
659                 l_info.req_device = i;
660                 mr = WOD_Open(&l_info);
661                 if(mr == MMSYSERR_NOERROR){
662                     info->handle = l_info.handle;
663                     return mr;
664                 }
665             }
666         }else{
667             l_info.flags &= ~WAVE_MAPPED;
668             mr = WOD_Open(&l_info);
669             if(mr == MMSYSERR_NOERROR){
670                 info->handle = l_info.handle;
671                 return mr;
672             }
673         }
674     }else{
675         WINMM_OpenInfo l_info = *info;
676         if(WINMM_IsMapper(info->req_device)){
677             for(i = 0; i < g_inmmdevices_count; ++i){
678                 l_info.req_device = i;
679                 mr = WID_Open(&l_info);
680                 if(mr == MMSYSERR_NOERROR){
681                     info->handle = l_info.handle;
682                     return mr;
683                 }
684             }
685         }else{
686             l_info.flags &= ~WAVE_MAPPED;
687             mr = WID_Open(&l_info);
688             if(mr == MMSYSERR_NOERROR){
689                 info->handle = l_info.handle;
690                 return mr;
691             }
692         }
693     }
694
695     /* no direct match, so set up the ACM stream */
696     if(info->format->wFormatTag != WAVE_FORMAT_PCM &&
697             !(info->format->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
698               IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
699         /* convert to PCM format if it's not already */
700         mr = WINMM_TryDeviceMapping(info, info->format->nChannels,
701                 info->format->nSamplesPerSec, 16, is_out);
702         if(mr == MMSYSERR_NOERROR)
703             return mr;
704
705         mr = WINMM_TryDeviceMapping(info, info->format->nChannels,
706                 info->format->nSamplesPerSec, 8, is_out);
707         if(mr == MMSYSERR_NOERROR)
708             return mr;
709     }else{
710         WORD channels;
711
712         /* first try just changing bit depth and channels */
713         channels = info->format->nChannels;
714         mr = WINMM_TryDeviceMapping(info, channels,
715                 info->format->nSamplesPerSec, 16, is_out);
716         if(mr == MMSYSERR_NOERROR)
717             return mr;
718         mr = WINMM_TryDeviceMapping(info, channels,
719                 info->format->nSamplesPerSec, 8, is_out);
720         if(mr == MMSYSERR_NOERROR)
721             return mr;
722
723         channels = (channels == 2) ? 1 : 2;
724         mr = WINMM_TryDeviceMapping(info, channels,
725                 info->format->nSamplesPerSec, 16, is_out);
726         if(mr == MMSYSERR_NOERROR)
727             return mr;
728         mr = WINMM_TryDeviceMapping(info, channels,
729                 info->format->nSamplesPerSec, 8, is_out);
730         if(mr == MMSYSERR_NOERROR)
731             return mr;
732
733         /* that didn't work, so now try different sample rates */
734         channels = info->format->nChannels;
735         mr = WINMM_TryDeviceMapping(info, channels, 96000, 16, is_out);
736         if(mr == MMSYSERR_NOERROR)
737             return mr;
738         mr = WINMM_TryDeviceMapping(info, channels, 48000, 16, is_out);
739         if(mr == MMSYSERR_NOERROR)
740             return mr;
741         mr = WINMM_TryDeviceMapping(info, channels, 44100, 16, is_out);
742         if(mr == MMSYSERR_NOERROR)
743             return mr;
744         mr = WINMM_TryDeviceMapping(info, channels, 22050, 16, is_out);
745         if(mr == MMSYSERR_NOERROR)
746             return mr;
747         mr = WINMM_TryDeviceMapping(info, channels, 11025, 16, is_out);
748         if(mr == MMSYSERR_NOERROR)
749             return mr;
750
751         channels = (channels == 2) ? 1 : 2;
752         mr = WINMM_TryDeviceMapping(info, channels, 96000, 16, is_out);
753         if(mr == MMSYSERR_NOERROR)
754             return mr;
755         mr = WINMM_TryDeviceMapping(info, channels, 48000, 16, is_out);
756         if(mr == MMSYSERR_NOERROR)
757             return mr;
758         mr = WINMM_TryDeviceMapping(info, channels, 44100, 16, is_out);
759         if(mr == MMSYSERR_NOERROR)
760             return mr;
761         mr = WINMM_TryDeviceMapping(info, channels, 22050, 16, is_out);
762         if(mr == MMSYSERR_NOERROR)
763             return mr;
764         mr = WINMM_TryDeviceMapping(info, channels, 11025, 16, is_out);
765         if(mr == MMSYSERR_NOERROR)
766             return mr;
767
768         channels = info->format->nChannels;
769         mr = WINMM_TryDeviceMapping(info, channels, 96000, 8, is_out);
770         if(mr == MMSYSERR_NOERROR)
771             return mr;
772         mr = WINMM_TryDeviceMapping(info, channels, 48000, 8, is_out);
773         if(mr == MMSYSERR_NOERROR)
774             return mr;
775         mr = WINMM_TryDeviceMapping(info, channels, 44100, 8, is_out);
776         if(mr == MMSYSERR_NOERROR)
777             return mr;
778         mr = WINMM_TryDeviceMapping(info, channels, 22050, 8, is_out);
779         if(mr == MMSYSERR_NOERROR)
780             return mr;
781         mr = WINMM_TryDeviceMapping(info, channels, 11025, 8, is_out);
782         if(mr == MMSYSERR_NOERROR)
783             return mr;
784
785         channels = (channels == 2) ? 1 : 2;
786         mr = WINMM_TryDeviceMapping(info, channels, 96000, 8, is_out);
787         if(mr == MMSYSERR_NOERROR)
788             return mr;
789         mr = WINMM_TryDeviceMapping(info, channels, 48000, 8, is_out);
790         if(mr == MMSYSERR_NOERROR)
791             return mr;
792         mr = WINMM_TryDeviceMapping(info, channels, 44100, 8, is_out);
793         if(mr == MMSYSERR_NOERROR)
794             return mr;
795         mr = WINMM_TryDeviceMapping(info, channels, 22050, 8, is_out);
796         if(mr == MMSYSERR_NOERROR)
797             return mr;
798         mr = WINMM_TryDeviceMapping(info, channels, 11025, 8, is_out);
799         if(mr == MMSYSERR_NOERROR)
800             return mr;
801     }
802
803     WARN("Unable to find compatible device!\n");
804     return WAVERR_BADFORMAT;
805 }
806
807 static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_MMDevice *mmdevice,
808         WINMM_OpenInfo *info)
809 {
810     WAVEFORMATEX *closer_fmt = NULL, fmt, *passed_fmt;
811     LRESULT ret = MMSYSERR_ERROR;
812     HRESULT hr;
813
814     hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id,
815             &device->device);
816     if(FAILED(hr)){
817         ERR("Device %s (%s) unavailable: %08x\n",
818                 wine_dbgstr_w(mmdevice->dev_id),
819                 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
820         goto error;
821     }
822
823     hr = IMMDevice_Activate(device->device, &IID_IAudioClient,
824             CLSCTX_INPROC_SERVER, NULL, (void**)&device->client);
825     if(FAILED(hr)){
826         ERR("Activate failed: %08x\n", hr);
827         goto error;
828     }
829
830     if(info->format->wFormatTag == WAVE_FORMAT_PCM){
831         /* we aren't guaranteed that the struct in lpFormat is a full
832          * WAVEFORMATEX struct, which IAC::IsFormatSupported requires */
833         passed_fmt = &fmt;
834         memcpy(passed_fmt, info->format, sizeof(PCMWAVEFORMAT));
835         passed_fmt->cbSize = 0;
836     }else
837         passed_fmt = info->format;
838
839     hr = IAudioClient_IsFormatSupported(device->client,
840             AUDCLNT_SHAREMODE_SHARED, passed_fmt, &closer_fmt);
841     if(closer_fmt)
842         CoTaskMemFree(closer_fmt);
843     if(FAILED(hr) && hr != AUDCLNT_E_UNSUPPORTED_FORMAT){
844         ERR("IsFormatSupported failed: %08x\n", hr);
845         goto error;
846     }
847     if(hr == S_FALSE || hr == AUDCLNT_E_UNSUPPORTED_FORMAT){
848         ret = WAVERR_BADFORMAT;
849         goto error;
850     }
851     if(info->flags & WAVE_FORMAT_QUERY){
852         ret = MMSYSERR_NOERROR;
853         goto error;
854     }
855
856     /* buffer size = 10 * 100000 (100 ns) = 0.1 seconds */
857     hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED,
858             AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
859             10 * 100000, 50000, passed_fmt, &device->parent->session);
860     if(FAILED(hr)){
861         ERR("Initialize failed: %08x\n", hr);
862         goto error;
863     }
864
865     hr = IAudioClient_GetService(device->client, &IID_IAudioClock,
866             (void**)&device->clock);
867     if(FAILED(hr)){
868         ERR("GetService failed: %08x\n", hr);
869         goto error;
870     }
871
872     if(!device->event){
873         device->event = CreateEventW(NULL, FALSE, FALSE, NULL);
874         if(!device->event){
875             ERR("CreateEvent failed: %08x\n", hr);
876             goto error;
877         }
878
879         /* As the devices thread is waiting on g_device_handles, it can
880          * only be modified from within this same thread. */
881         if(g_device_handles){
882             g_device_handles = HeapReAlloc(GetProcessHeap(), 0, g_device_handles,
883                     sizeof(HANDLE) * (g_devhandle_count + 1));
884             g_handle_devices = HeapReAlloc(GetProcessHeap(), 0, g_handle_devices,
885                     sizeof(WINMM_Device *) * (g_devhandle_count + 1));
886         }else{
887             g_device_handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE));
888             g_handle_devices = HeapAlloc(GetProcessHeap(), 0,
889                     sizeof(WINMM_Device *));
890         }
891         g_device_handles[g_devhandle_count] = device->event;
892         g_handle_devices[g_devhandle_count] = device;
893         ++g_devhandle_count;
894     }
895
896     hr = IAudioClient_SetEventHandle(device->client, device->event);
897     if(FAILED(hr)){
898         ERR("SetEventHandle failed: %08x\n", hr);
899         goto error;
900     }
901
902     device->bytes_per_frame = info->format->nBlockAlign;
903     device->samples_per_sec = info->format->nSamplesPerSec;
904
905     device->played_frames = 0;
906     device->last_clock_pos = 0;
907     device->ofs_bytes = 0;
908     device->loop_counter = 0;
909     device->stopped = TRUE;
910     device->first = device->last = device->playing = device->loop_start = NULL;
911
912     device->cb_info.flags = HIWORD(info->flags & CALLBACK_TYPEMASK);
913     device->cb_info.callback = info->callback;
914     device->cb_info.user = info->cb_user;
915     device->cb_info.hwave = device->handle;
916
917     info->handle = device->handle;
918
919     return MMSYSERR_NOERROR;
920
921 error:
922     if(device->client){
923         IAudioClient_Release(device->client);
924         device->client = NULL;
925     }
926     if(device->device){
927         IMMDevice_Release(device->device);
928         device->device = NULL;
929     }
930
931     return ret;
932 }
933
934 static LRESULT WOD_Open(WINMM_OpenInfo *info)
935 {
936     WINMM_MMDevice *mmdevice;
937     WINMM_Device *device = NULL;
938     LRESULT ret = MMSYSERR_ERROR;
939     HRESULT hr;
940
941     TRACE("(%u, %p, %08x)\n", info->req_device, info, info->flags);
942
943     if(WINMM_IsMapper(info->req_device) || (info->flags & WAVE_MAPPED))
944         return WINMM_MapDevice(info, TRUE);
945
946     if(info->req_device >= g_outmmdevices_count)
947         return MMSYSERR_BADDEVICEID;
948
949     mmdevice = &g_out_mmdevices[info->req_device];
950
951     if(!mmdevice->out_caps.szPname[0])
952         return MMSYSERR_NOTENABLED;
953
954     device = WINMM_FindUnusedDevice(TRUE, info->req_device);
955     if(!device)
956         return MMSYSERR_ALLOCATED;
957
958     ret = WINMM_OpenDevice(device, mmdevice, info);
959     if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
960         goto error;
961     ret = MMSYSERR_ERROR;
962
963     hr = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
964             (void**)&device->render);
965     if(FAILED(hr)){
966         ERR("GetService failed: %08x\n", hr);
967         goto error;
968     }
969
970     hr = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
971             (void**)&device->volume);
972     if(FAILED(hr)){
973         ERR("GetService failed: %08x\n", hr);
974         goto error;
975     }
976
977     LeaveCriticalSection(&device->lock);
978
979     return MMSYSERR_NOERROR;
980
981 error:
982     if(device->device){
983         IMMDevice_Release(device->device);
984         device->device = NULL;
985     }
986     if(device->client){
987         IAudioClient_Release(device->client);
988         device->client = NULL;
989     }
990     if(device->render){
991         IAudioRenderClient_Release(device->render);
992         device->render = NULL;
993     }
994     if(device->volume){
995         IAudioStreamVolume_Release(device->volume);
996         device->volume = NULL;
997     }
998     if(device->clock){
999         IAudioClock_Release(device->clock);
1000         device->clock = NULL;
1001     }
1002     device->open = FALSE;
1003     LeaveCriticalSection(&device->lock);
1004     return ret;
1005 }
1006
1007 static LRESULT WID_Open(WINMM_OpenInfo *info)
1008 {
1009     WINMM_MMDevice *mmdevice;
1010     WINMM_Device *device = NULL;
1011     LRESULT ret = MMSYSERR_ERROR;
1012     HRESULT hr;
1013
1014     TRACE("(%u, %p, %08x)\n", info->req_device, info, info->flags);
1015
1016     if(WINMM_IsMapper(info->req_device) || info->flags & WAVE_MAPPED)
1017         return WINMM_MapDevice(info, FALSE);
1018
1019     if(info->req_device >= g_inmmdevices_count)
1020         return MMSYSERR_BADDEVICEID;
1021
1022     mmdevice = &g_in_mmdevices[info->req_device];
1023
1024     if(!mmdevice->in_caps.szPname[0])
1025         return MMSYSERR_NOTENABLED;
1026
1027     device = WINMM_FindUnusedDevice(FALSE, info->req_device);
1028     if(!device)
1029         return MMSYSERR_ALLOCATED;
1030
1031     ret = WINMM_OpenDevice(device, mmdevice, info);
1032     if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
1033         goto error;
1034     ret = MMSYSERR_ERROR;
1035
1036     hr = IAudioClient_GetService(device->client, &IID_IAudioCaptureClient,
1037             (void**)&device->capture);
1038     if(FAILED(hr)){
1039         ERR("GetService failed: %08x\n", hr);
1040         goto error;
1041     }
1042
1043     LeaveCriticalSection(&device->lock);
1044
1045     return MMSYSERR_NOERROR;
1046
1047 error:
1048     if(device->device){
1049         IMMDevice_Release(device->device);
1050         device->device = NULL;
1051     }
1052     if(device->client){
1053         IAudioClient_Release(device->client);
1054         device->client = NULL;
1055     }
1056     if(device->capture){
1057         IAudioCaptureClient_Release(device->capture);
1058         device->capture = NULL;
1059     }
1060     if(device->clock){
1061         IAudioClock_Release(device->clock);
1062         device->clock = NULL;
1063     }
1064     device->open = FALSE;
1065     LeaveCriticalSection(&device->lock);
1066     return ret;
1067 }
1068
1069 static HRESULT WINMM_CloseDevice(WINMM_Device *device)
1070 {
1071     device->open = FALSE;
1072
1073     if(!device->stopped){
1074         IAudioClient_Stop(device->client);
1075         device->stopped = TRUE;
1076     }
1077
1078     if(device->acm_handle){
1079         acmStreamClose(device->acm_handle, 0);
1080         device->acm_handle = NULL;
1081     }
1082
1083     IMMDevice_Release(device->device);
1084     device->device = NULL;
1085
1086     IAudioClient_Release(device->client);
1087     device->client = NULL;
1088
1089     IAudioClock_Release(device->clock);
1090     device->clock = NULL;
1091
1092     return S_OK;
1093 }
1094
1095 static LRESULT WOD_Close(HWAVEOUT hwave)
1096 {
1097     WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1098
1099     TRACE("(%p)\n", hwave);
1100
1101     if(!WINMM_ValidateAndLock(device))
1102         return MMSYSERR_INVALHANDLE;
1103
1104     WINMM_CloseDevice(device);
1105
1106     IAudioRenderClient_Release(device->render);
1107     device->render = NULL;
1108
1109     IAudioStreamVolume_Release(device->volume);
1110     device->volume = NULL;
1111
1112     LeaveCriticalSection(&device->lock);
1113
1114     return MMSYSERR_NOERROR;
1115 }
1116
1117 static LRESULT WID_Close(HWAVEIN hwave)
1118 {
1119     WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1120
1121     TRACE("(%p)\n", hwave);
1122
1123     if(!WINMM_ValidateAndLock(device))
1124         return MMSYSERR_INVALHANDLE;
1125
1126     WINMM_CloseDevice(device);
1127
1128     IAudioCaptureClient_Release(device->capture);
1129     device->capture = NULL;
1130
1131     LeaveCriticalSection(&device->lock);
1132
1133     return MMSYSERR_NOERROR;
1134 }
1135
1136 static LRESULT WINMM_PrepareHeader(HWAVE hwave, WAVEHDR *header)
1137 {
1138     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1139
1140     TRACE("(%p, %p)\n", hwave, header);
1141
1142     if(!WINMM_ValidateAndLock(device))
1143         return MMSYSERR_INVALHANDLE;
1144
1145     if(device->render && device->acm_handle){
1146         ACMSTREAMHEADER *ash;
1147         DWORD size;
1148         MMRESULT mr;
1149
1150         mr = acmStreamSize(device->acm_handle, header->dwBufferLength, &size,
1151                 ACM_STREAMSIZEF_SOURCE);
1152         if(mr != MMSYSERR_NOERROR){
1153             LeaveCriticalSection(&device->lock);
1154             return mr;
1155         }
1156
1157         ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + size);
1158         if(!ash){
1159             LeaveCriticalSection(&device->lock);
1160             return MMSYSERR_NOMEM;
1161         }
1162
1163         ash->cbStruct = sizeof(*ash);
1164         ash->fdwStatus = 0;
1165         ash->dwUser = (DWORD_PTR)header;
1166         ash->pbSrc = (BYTE*)header->lpData;
1167         ash->cbSrcLength = header->dwBufferLength;
1168         ash->dwSrcUser = header->dwUser;
1169         ash->pbDst = (BYTE*)ash + sizeof(ACMSTREAMHEADER);
1170         ash->cbDstLength = size;
1171         ash->dwDstUser = 0;
1172
1173         mr = acmStreamPrepareHeader(device->acm_handle, ash, 0);
1174         if(mr != MMSYSERR_NOERROR){
1175             HeapFree(GetProcessHeap(), 0, ash);
1176             LeaveCriticalSection(&device->lock);
1177             return mr;
1178         }
1179
1180         header->reserved = (DWORD_PTR)ash;
1181     }
1182
1183     LeaveCriticalSection(&device->lock);
1184
1185     header->dwFlags |= WHDR_PREPARED;
1186     header->dwFlags &= ~WHDR_DONE;
1187
1188     return MMSYSERR_NOERROR;
1189 }
1190
1191 static LRESULT WINMM_UnprepareHeader(HWAVE hwave, WAVEHDR *header)
1192 {
1193     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1194
1195     TRACE("(%p, %p)\n", hwave, header);
1196
1197     if(!WINMM_ValidateAndLock(device))
1198         return MMSYSERR_INVALHANDLE;
1199
1200     if(device->render && device->acm_handle){
1201         ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1202
1203         acmStreamUnprepareHeader(device->acm_handle, ash, 0);
1204
1205         HeapFree(GetProcessHeap(), 0, ash);
1206     }
1207
1208     LeaveCriticalSection(&device->lock);
1209
1210     header->dwFlags &= ~WHDR_PREPARED;
1211     header->dwFlags |= WHDR_DONE;
1212
1213     return MMSYSERR_NOERROR;
1214 }
1215
1216 static UINT32 WINMM_HeaderLenBytes(WINMM_Device *device, WAVEHDR *header)
1217 {
1218     if(device->acm_handle){
1219         ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1220         return ash->cbDstLengthUsed;
1221     }
1222
1223     return header->dwBufferLength;
1224 }
1225
1226 static UINT32 WINMM_HeaderLenFrames(WINMM_Device *device, WAVEHDR *header)
1227 {
1228     return WINMM_HeaderLenBytes(device, header) / device->bytes_per_frame;
1229 }
1230
1231 static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device)
1232 {
1233     HRESULT hr;
1234     WAVEHDR *first = device->first, *queue = first, *last = NULL;
1235     UINT64 clock_freq, clock_pos, clock_frames;
1236     UINT32 nloops, queue_frames = 0;
1237
1238     hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
1239     if(FAILED(hr)){
1240         ERR("GetFrequency failed: %08x\n", hr);
1241         return NULL;
1242     }
1243
1244     hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
1245     if(FAILED(hr)){
1246         ERR("GetPosition failed: %08x\n", hr);
1247         return NULL;
1248     }
1249
1250     clock_frames = (clock_pos / (double)clock_freq) * device->samples_per_sec;
1251
1252     nloops = device->loop_counter;
1253     while(queue &&
1254             (queue_frames += WINMM_HeaderLenFrames(device, queue)) <=
1255                 clock_frames - device->last_clock_pos){
1256         if(!nloops){
1257             last = queue;
1258             device->last_clock_pos += queue_frames;
1259             queue_frames = 0;
1260             queue = device->first = queue->lpNext;
1261         }else{
1262             if(queue->dwFlags & WHDR_BEGINLOOP){
1263                 if(queue->dwFlags & WHDR_ENDLOOP)
1264                     --nloops;
1265                 else
1266                     queue = queue->lpNext;
1267             }else if(queue->dwFlags & WHDR_ENDLOOP){
1268                 queue = device->loop_start;
1269                 --nloops;
1270             }
1271         }
1272     }
1273
1274     if(last){
1275         last->lpNext = NULL;
1276         return first;
1277     }else
1278         return NULL;
1279 }
1280
1281 static void WOD_PushData(WINMM_Device *device)
1282 {
1283     WINMM_CBInfo cb_info;
1284     HRESULT hr;
1285     UINT32 pad, bufsize, avail_frames, queue_frames, written, ofs;
1286     UINT32 queue_bytes, nloops;
1287     BYTE *data;
1288     WAVEHDR *queue, *first = NULL;
1289
1290     TRACE("(%p)\n", device->handle);
1291
1292     EnterCriticalSection(&device->lock);
1293
1294     if(!device->device)
1295         goto exit;
1296
1297     if(!device->first){
1298         device->stopped = TRUE;
1299         device->last_clock_pos = 0;
1300         IAudioClient_Stop(device->client);
1301         IAudioClient_Reset(device->client);
1302         goto exit;
1303     }
1304
1305     hr = IAudioClient_GetBufferSize(device->client, &bufsize);
1306     if(FAILED(hr)){
1307         ERR("GetBufferSize failed: %08x\n", hr);
1308         goto exit;
1309     }
1310
1311     hr = IAudioClient_GetCurrentPadding(device->client, &pad);
1312     if(FAILED(hr)){
1313         ERR("GetCurrentPadding failed: %08x\n", hr);
1314         goto exit;
1315     }
1316
1317     first = WOD_MarkDoneHeaders(device);
1318
1319     /* determine which is larger between the available buffer size and
1320      * the amount of data left in the queue */
1321     avail_frames = bufsize - pad;
1322
1323     queue = device->playing;
1324     ofs = device->ofs_bytes;
1325     queue_frames = 0;
1326     nloops = 0;
1327     while(queue && queue_frames < avail_frames){
1328         queue_bytes = WINMM_HeaderLenBytes(device, queue);
1329         queue_frames += (queue_bytes - ofs) / device->bytes_per_frame;
1330         ofs = 0;
1331
1332         if(queue->dwFlags & WHDR_ENDLOOP && nloops < device->loop_counter){
1333             queue = device->loop_start;
1334             ++nloops;
1335         }else
1336             queue = queue->lpNext;
1337     }
1338
1339     if(avail_frames != 0 && queue_frames == 0){
1340         hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
1341         if(FAILED(hr)){
1342             ERR("GetBuffer failed: %08x\n", hr);
1343             goto exit;
1344         }
1345
1346         hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames,
1347                 AUDCLNT_BUFFERFLAGS_SILENT);
1348         if(FAILED(hr)){
1349             ERR("ReleaseBuffer failed: %08x\n", hr);
1350             goto exit;
1351         }
1352
1353         goto exit;
1354     }
1355
1356     if(queue_frames < avail_frames)
1357         avail_frames = queue_frames;
1358     if(avail_frames == 0)
1359         goto exit;
1360
1361     hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
1362     if(FAILED(hr)){
1363         ERR("GetBuffer failed: %08x\n", hr);
1364         goto exit;
1365     }
1366
1367     written = 0;
1368     while(device->playing && written < avail_frames){
1369         UINT32 copy_frames, copy_bytes;
1370         BYTE *queue_data;
1371
1372         queue = device->playing;
1373
1374         if(device->acm_handle){
1375             ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)queue->reserved;
1376             queue_bytes = ash->cbDstLengthUsed;
1377             queue_data = ash->pbDst;
1378         }else{
1379             queue_bytes = queue->dwBufferLength;
1380             queue_data = (BYTE*)queue->lpData;
1381         }
1382
1383         queue_frames = (queue_bytes - device->ofs_bytes) /
1384             device->bytes_per_frame;
1385
1386         copy_frames = queue_frames < (avail_frames - written) ?
1387             queue_frames : avail_frames - written;
1388         copy_bytes = copy_frames * device->bytes_per_frame;
1389
1390         memcpy(data, queue_data + device->ofs_bytes, copy_bytes);
1391
1392         data += copy_bytes;
1393         written += copy_frames;
1394         device->ofs_bytes += copy_bytes;
1395
1396         if(device->ofs_bytes >= queue_bytes){
1397             device->ofs_bytes = 0;
1398
1399             if(!(queue->dwFlags & (WHDR_BEGINLOOP | WHDR_ENDLOOP)))
1400                 device->playing = queue->lpNext;
1401             else{
1402                 if(queue->dwFlags & WHDR_BEGINLOOP){
1403                     device->loop_start = device->playing;
1404                     device->playing = queue->lpNext;
1405                     device->loop_counter = queue->dwLoops;
1406                 }
1407                 if(queue->dwFlags & WHDR_ENDLOOP){
1408                     --device->loop_counter;
1409                     if(device->loop_counter)
1410                         device->playing = device->loop_start;
1411                     else
1412                         device->loop_start = device->playing = queue->lpNext;
1413                 }
1414             }
1415         }
1416     }
1417
1418     hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames, 0);
1419     if(FAILED(hr)){
1420         ERR("ReleaseBuffer failed: %08x\n", hr);
1421         goto exit;
1422     }
1423
1424     device->played_frames += avail_frames;
1425
1426 exit:
1427     cb_info = device->cb_info;
1428
1429     LeaveCriticalSection(&device->lock);
1430
1431     while(first){
1432         WAVEHDR *next = first->lpNext;
1433         first->dwFlags &= ~WHDR_INQUEUE;
1434         first->dwFlags |= WHDR_DONE;
1435         WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1436         first = next;
1437     }
1438 }
1439
1440 static void WID_PullACMData(WINMM_Device *device)
1441 {
1442     UINT32 packet, packet_bytes;
1443     DWORD flags;
1444     BYTE *data;
1445     WAVEHDR *queue;
1446     HRESULT hr;
1447     MMRESULT mr;
1448
1449     if(device->acm_hdr.cbDstLength == 0){
1450         hr = IAudioClient_GetCurrentPadding(device->client, &packet);
1451         if(FAILED(hr)){
1452             ERR("GetCurrentPadding failed: %08x\n", hr);
1453             return;
1454         }
1455
1456         if(packet == 0)
1457             return;
1458
1459         hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
1460                 &flags, NULL, NULL);
1461         if(FAILED(hr)){
1462             ERR("GetBuffer failed: %08x\n", hr);
1463             return;
1464         }
1465
1466         acmStreamSize(device->acm_handle, packet * device->bytes_per_frame,
1467                 &packet_bytes, ACM_STREAMSIZEF_SOURCE);
1468
1469         device->acm_offs = 0;
1470
1471         device->acm_hdr.cbStruct = sizeof(device->acm_hdr);
1472         device->acm_hdr.fdwStatus = 0;
1473         device->acm_hdr.dwUser = 0;
1474         device->acm_hdr.pbSrc = data;
1475         device->acm_hdr.cbSrcLength = packet * device->bytes_per_frame;
1476         device->acm_hdr.cbSrcLengthUsed = 0;
1477         device->acm_hdr.dwSrcUser = 0;
1478         device->acm_hdr.pbDst = HeapAlloc(GetProcessHeap(), 0, packet_bytes);
1479         device->acm_hdr.cbDstLength = packet_bytes;
1480         device->acm_hdr.cbDstLengthUsed = 0;
1481         device->acm_hdr.dwDstUser = 0;
1482
1483         mr = acmStreamPrepareHeader(device->acm_handle, &device->acm_hdr, 0);
1484         if(mr != MMSYSERR_NOERROR){
1485             ERR("acmStreamPrepareHeader failed: %d\n", mr);
1486             return;
1487         }
1488
1489         mr = acmStreamConvert(device->acm_handle, &device->acm_hdr, 0);
1490         if(mr != MMSYSERR_NOERROR){
1491             ERR("acmStreamConvert failed: %d\n", mr);
1492             return;
1493         }
1494
1495         hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet);
1496         if(FAILED(hr))
1497             ERR("ReleaseBuffer failed: %08x\n", hr);
1498
1499         device->played_frames += packet;
1500     }
1501
1502     queue = device->first;
1503     while(queue){
1504         UINT32 to_copy_bytes;
1505
1506         to_copy_bytes = min(queue->dwBufferLength - queue->dwBytesRecorded,
1507                 device->acm_hdr.cbDstLengthUsed - device->acm_offs);
1508
1509         memcpy(queue->lpData + queue->dwBytesRecorded,
1510                 device->acm_hdr.pbDst + device->acm_offs, to_copy_bytes);
1511
1512         queue->dwBytesRecorded += to_copy_bytes;
1513         device->acm_offs += to_copy_bytes;
1514
1515         if(queue->dwBufferLength - queue->dwBytesRecorded <
1516                 device->bytes_per_frame){
1517             queue->dwFlags &= ~WHDR_INQUEUE;
1518             queue->dwFlags |= WHDR_DONE;
1519             device->first = queue = queue->lpNext;
1520         }
1521
1522         if(device->acm_offs >= device->acm_hdr.cbDstLengthUsed){
1523             acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1524             HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1525             device->acm_hdr.cbDstLength = 0;
1526             device->acm_hdr.cbDstLengthUsed = 0;
1527
1528             /* done with this ACM Header, so try to pull more data */
1529             WID_PullACMData(device);
1530             return;
1531         }
1532     }
1533
1534     /* out of WAVEHDRs to write into, so toss the rest of this packet */
1535     acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1536     HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1537     device->acm_hdr.cbDstLength = 0;
1538     device->acm_hdr.cbDstLengthUsed = 0;
1539 }
1540
1541 static void WID_PullData(WINMM_Device *device)
1542 {
1543     WINMM_CBInfo cb_info;
1544     WAVEHDR *queue, *first = NULL, *last = NULL;
1545     HRESULT hr;
1546
1547     TRACE("(%p)\n", device->handle);
1548
1549     EnterCriticalSection(&device->lock);
1550
1551     if(!device->device || !device->first)
1552         goto exit;
1553
1554     first = device->first;
1555
1556     if(device->acm_handle){
1557         WID_PullACMData(device);
1558         goto exit;
1559     }
1560
1561     while(device->first){
1562         BYTE *data;
1563         UINT32 pad, packet_len, packet;
1564         DWORD flags;
1565
1566         hr = IAudioClient_GetCurrentPadding(device->client, &pad);
1567         if(FAILED(hr)){
1568             ERR("GetCurrentPadding failed: %08x\n", hr);
1569             goto exit;
1570         }
1571
1572         if(pad == 0)
1573             goto exit;
1574
1575         hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
1576                 &flags, NULL, NULL);
1577         if(FAILED(hr)){
1578             ERR("GetBuffer failed: %08x\n", hr);
1579             goto exit;
1580         }
1581
1582         packet_len = packet;
1583         queue = device->first;
1584         while(queue && packet > 0){
1585             UINT32 to_copy_bytes;
1586
1587             to_copy_bytes = min(packet * device->bytes_per_frame,
1588                     queue->dwBufferLength - queue->dwBytesRecorded);
1589
1590             memcpy(queue->lpData + queue->dwBytesRecorded,
1591                     data + (packet_len - packet) * device->bytes_per_frame,
1592                     to_copy_bytes);
1593
1594             queue->dwBytesRecorded += to_copy_bytes;
1595
1596             if(queue->dwBufferLength - queue->dwBytesRecorded <
1597                     device->bytes_per_frame){
1598                 last = queue;
1599                 device->first = queue = queue->lpNext;
1600             }
1601
1602             packet -= to_copy_bytes / device->bytes_per_frame;
1603         }
1604
1605         hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet_len);
1606         if(FAILED(hr))
1607             ERR("ReleaseBuffer failed: %08x\n", hr);
1608
1609         device->played_frames += packet_len;
1610     }
1611
1612 exit:
1613     cb_info = device->cb_info;
1614
1615     LeaveCriticalSection(&device->lock);
1616
1617     if(last){
1618         last->lpNext = NULL;
1619         while(first){
1620             WAVEHDR *next = first->lpNext;
1621             first->dwFlags &= ~WHDR_INQUEUE;
1622             first->dwFlags |= WHDR_DONE;
1623             WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1624             first = next;
1625         }
1626     }
1627 }
1628
1629 static HRESULT WINMM_BeginPlaying(WINMM_Device *device)
1630 {
1631     HRESULT hr;
1632
1633     TRACE("(%p)\n", device->handle);
1634
1635     if(device->render)
1636         /* prebuffer data before starting */
1637         WOD_PushData(device);
1638
1639     if(device->stopped){
1640         device->stopped = FALSE;
1641
1642         hr = IAudioClient_Start(device->client);
1643         if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){
1644             device->stopped = TRUE;
1645             ERR("Start failed: %08x\n", hr);
1646             return hr;
1647         }
1648     }
1649
1650     return S_OK;
1651 }
1652
1653 static LRESULT WINMM_Pause(HWAVE hwave)
1654 {
1655     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1656     HRESULT hr;
1657
1658     TRACE("(%p)\n", hwave);
1659
1660     if(!WINMM_ValidateAndLock(device))
1661         return MMSYSERR_INVALHANDLE;
1662
1663     hr = IAudioClient_Stop(device->client);
1664     if(FAILED(hr)){
1665         LeaveCriticalSection(&device->lock);
1666         ERR("Stop failed: %08x\n", hr);
1667         return MMSYSERR_ERROR;
1668     }
1669
1670     device->stopped = FALSE;
1671
1672     LeaveCriticalSection(&device->lock);
1673
1674     return MMSYSERR_NOERROR;
1675 }
1676
1677 static LRESULT WINMM_Reset(HWAVE hwave)
1678 {
1679     WINMM_CBInfo cb_info;
1680     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1681     BOOL is_out;
1682     WAVEHDR *first;
1683     HRESULT hr;
1684
1685     TRACE("(%p)\n", hwave);
1686
1687     if(!WINMM_ValidateAndLock(device))
1688         return MMSYSERR_INVALHANDLE;
1689
1690     hr = IAudioClient_Stop(device->client);
1691     if(FAILED(hr)){
1692         LeaveCriticalSection(&device->lock);
1693         ERR("Stop failed: %08x\n", hr);
1694         return MMSYSERR_ERROR;
1695     }
1696     device->stopped = TRUE;
1697
1698     first = device->first;
1699     device->first = device->last = device->playing = NULL;
1700     device->ofs_bytes = 0;
1701     device->played_frames = 0;
1702     device->loop_counter = 0;
1703     device->last_clock_pos = 0;
1704     IAudioClient_Reset(device->client);
1705
1706     cb_info = device->cb_info;
1707     is_out = device->render ? TRUE : FALSE;
1708
1709     LeaveCriticalSection(&device->lock);
1710
1711     while(first){
1712         WAVEHDR *next = first->lpNext;
1713         first->dwFlags &= ~WHDR_INQUEUE;
1714         first->dwFlags |= WHDR_DONE;
1715         if(is_out)
1716             WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1717         else
1718             WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1719         first = next;
1720     }
1721
1722     return MMSYSERR_NOERROR;
1723 }
1724
1725 static MMRESULT WINMM_FramesToMMTime(MMTIME *time, UINT32 played_frames,
1726         UINT32 sample_rate, UINT32 bytes_per_frame)
1727 {
1728     switch(time->wType){
1729     case TIME_SAMPLES:
1730         time->u.sample = played_frames;
1731         return MMSYSERR_NOERROR;
1732     case TIME_MS:
1733         time->u.ms = (DWORD)((played_frames / (double)sample_rate) * 1000);
1734         return MMSYSERR_NOERROR;
1735     case TIME_SMPTE:
1736         time->u.smpte.fps = 30;
1737         if(played_frames >= sample_rate){
1738             time->u.smpte.sec = played_frames / (double)sample_rate;
1739             time->u.smpte.min = time->u.smpte.sec / 60;
1740             time->u.smpte.hour = time->u.smpte.min / 60;
1741             time->u.smpte.sec %= 60;
1742             time->u.smpte.min %= 60;
1743             played_frames %= sample_rate;
1744         }else{
1745             time->u.smpte.sec = 0;
1746             time->u.smpte.min = 0;
1747             time->u.smpte.hour = 0;
1748         }
1749         time->u.smpte.frame = (played_frames / (double)sample_rate) * 30;
1750         return MMSYSERR_NOERROR;
1751     case TIME_BYTES:
1752     default:
1753         time->wType = TIME_BYTES;
1754         time->u.cb = played_frames * bytes_per_frame;
1755         return MMSYSERR_NOERROR;
1756     }
1757
1758     return MMSYSERR_ERROR;
1759 }
1760
1761 static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time)
1762 {
1763     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1764     UINT32 played_frames, sample_rate, bytes_per_frame;
1765
1766     TRACE("(%p, %p)\n", hwave, time);
1767
1768     if(!WINMM_ValidateAndLock(device))
1769         return MMSYSERR_INVALHANDLE;
1770
1771     played_frames = device->played_frames;
1772     sample_rate = device->samples_per_sec;
1773     bytes_per_frame = device->bytes_per_frame;
1774
1775     LeaveCriticalSection(&device->lock);
1776
1777     return WINMM_FramesToMMTime(time, played_frames, sample_rate,
1778             bytes_per_frame);
1779 }
1780
1781 static WINMM_MMDevice *WINMM_GetMixerMMDevice(HMIXEROBJ hmix, DWORD flags,
1782         UINT *mmdev_index)
1783 {
1784     UINT mmdev, dev, junk, *out;
1785     BOOL is_out;
1786
1787     if(!mmdev_index)
1788         out = &mmdev;
1789     else
1790         out = mmdev_index;
1791
1792     switch(flags & 0xF0000000){
1793     case MIXER_OBJECTF_MIXER: /* == 0 */
1794         *out = HandleToULong(hmix);
1795         if(*out < g_outmmdevices_count)
1796             return &g_out_mmdevices[*out];
1797         if(*out - g_outmmdevices_count < g_inmmdevices_count){
1798             *out -= g_outmmdevices_count;
1799             return &g_in_mmdevices[*out];
1800         }
1801         /* fall through -- if it's not a valid mixer device, then
1802          * it could be a valid mixer handle. windows seems to do
1803          * this as well. */
1804     case MIXER_OBJECTF_HMIXER:
1805     case MIXER_OBJECTF_HWAVEOUT:
1806     case MIXER_OBJECTF_HWAVEIN:
1807         WINMM_DecomposeHWAVE((HWAVE)hmix, out, &is_out, &dev, &junk);
1808         if(junk != 0x1 || (is_out && *out >= g_outmmdevices_count) ||
1809                (!is_out && *out >= g_inmmdevices_count))
1810             return NULL;
1811         if(is_out)
1812             return &g_out_mmdevices[*out];
1813         return &g_in_mmdevices[*out];
1814     case MIXER_OBJECTF_WAVEOUT:
1815         *out = HandleToULong(hmix);
1816         if(*out < g_outmmdevices_count)
1817             return &g_out_mmdevices[*out];
1818         return NULL;
1819     case MIXER_OBJECTF_WAVEIN:
1820         *out = HandleToULong(hmix);
1821         if(*out < g_inmmdevices_count)
1822             return &g_in_mmdevices[*out];
1823         return NULL;
1824     }
1825
1826     return NULL;
1827 }
1828
1829 static MMRESULT WINMM_SetupMMDeviceVolume(WINMM_MMDevice *mmdevice)
1830 {
1831     IAudioSessionManager *sesman;
1832     IMMDevice *device;
1833     HRESULT hr;
1834
1835     hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id, &device);
1836     if(FAILED(hr)){
1837         ERR("Device %s (%s) unavailable: %08x\n",
1838                 wine_dbgstr_w(mmdevice->dev_id),
1839                 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
1840         return MMSYSERR_ERROR;
1841     }
1842
1843     hr = IMMDevice_Activate(device, &IID_IAudioSessionManager,
1844             CLSCTX_INPROC_SERVER, NULL, (void**)&sesman);
1845     if(FAILED(hr)){
1846         ERR("Activate failed: %08x\n", hr);
1847         IMMDevice_Release(device);
1848         return MMSYSERR_ERROR;
1849     }
1850
1851     IMMDevice_Release(device);
1852
1853     hr = IAudioSessionManager_GetSimpleAudioVolume(sesman, &mmdevice->session,
1854             FALSE, &mmdevice->volume);
1855     IAudioSessionManager_Release(sesman);
1856     if(FAILED(hr)){
1857         ERR("GetSimpleAudioVolume failed: %08x\n", hr);
1858         return MMSYSERR_ERROR;
1859     }
1860
1861     return MMSYSERR_NOERROR;
1862 }
1863
1864 static LRESULT MXD_GetControlDetails(WINMM_ControlDetails *details)
1865 {
1866     WINMM_MMDevice *mmdevice;
1867     MIXERCONTROLDETAILS *control = details->details;
1868     HRESULT hr;
1869
1870     TRACE("(%p)\n", details->hmix);
1871
1872     mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
1873     if(!mmdevice)
1874         return MMSYSERR_INVALHANDLE;
1875
1876     EnterCriticalSection(&mmdevice->lock);
1877
1878     if(!mmdevice->volume){
1879         MMRESULT mr;
1880
1881         mr = WINMM_SetupMMDeviceVolume(mmdevice);
1882         if(mr != MMSYSERR_NOERROR){
1883             LeaveCriticalSection(&mmdevice->lock);
1884             return mr;
1885         }
1886     }
1887
1888     if(control->dwControlID == 0){
1889         float vol;
1890         MIXERCONTROLDETAILS_UNSIGNED *udet;
1891
1892         if(!control->paDetails ||
1893                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
1894             LeaveCriticalSection(&mmdevice->lock);
1895             return MMSYSERR_INVALPARAM;
1896         }
1897
1898         hr = ISimpleAudioVolume_GetMasterVolume(mmdevice->volume, &vol);
1899         if(FAILED(hr)){
1900             ERR("GetMasterVolume failed: %08x\n", hr);
1901             LeaveCriticalSection(&mmdevice->lock);
1902             return MMSYSERR_ERROR;
1903         }
1904
1905         udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
1906         udet->dwValue = vol * ((unsigned int)0xFFFF);
1907     }else if(control->dwControlID == 1){
1908         BOOL mute;
1909         MIXERCONTROLDETAILS_BOOLEAN *bdet;
1910
1911         if(!control->paDetails ||
1912                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
1913             LeaveCriticalSection(&mmdevice->lock);
1914             return MMSYSERR_INVALPARAM;
1915         }
1916
1917         hr = ISimpleAudioVolume_GetMute(mmdevice->volume, &mute);
1918         if(FAILED(hr)){
1919             ERR("GetMute failed: %08x\n", hr);
1920             LeaveCriticalSection(&mmdevice->lock);
1921             return MMSYSERR_ERROR;
1922         }
1923
1924         bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
1925         bdet->fValue = mute;
1926     }else if(control->dwControlID == 2 || control->dwControlID == 3){
1927         FIXME("What should the sw-side mixer controls map to?\n");
1928     }else{
1929         LeaveCriticalSection(&mmdevice->lock);
1930         return MIXERR_INVALCONTROL;
1931     }
1932
1933     LeaveCriticalSection(&mmdevice->lock);
1934
1935     return MMSYSERR_NOERROR;
1936 }
1937
1938 static LRESULT MXD_SetControlDetails(WINMM_ControlDetails *details)
1939 {
1940     WINMM_MMDevice *mmdevice;
1941     MIXERCONTROLDETAILS *control = details->details;
1942     HRESULT hr;
1943
1944     TRACE("(%p)\n", details->hmix);
1945
1946     mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
1947     if(!mmdevice)
1948         return MMSYSERR_INVALHANDLE;
1949
1950     EnterCriticalSection(&mmdevice->lock);
1951
1952     if(!mmdevice->volume){
1953         MMRESULT mr;
1954
1955         mr = WINMM_SetupMMDeviceVolume(mmdevice);
1956         if(mr != MMSYSERR_NOERROR){
1957             LeaveCriticalSection(&mmdevice->lock);
1958             return mr;
1959         }
1960     }
1961
1962     if(control->dwControlID == 0){
1963         float vol;
1964         MIXERCONTROLDETAILS_UNSIGNED *udet;
1965
1966         if(!control->paDetails ||
1967                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
1968             LeaveCriticalSection(&mmdevice->lock);
1969             return MMSYSERR_INVALPARAM;
1970         }
1971
1972         udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
1973
1974         if(udet->dwValue > 65535){
1975             LeaveCriticalSection(&mmdevice->lock);
1976             return MMSYSERR_INVALPARAM;
1977         }
1978
1979         vol = udet->dwValue / 65535.f;
1980
1981         hr = ISimpleAudioVolume_SetMasterVolume(mmdevice->volume, vol, NULL);
1982         if(FAILED(hr)){
1983             ERR("SetMasterVolume failed: %08x\n", hr);
1984             LeaveCriticalSection(&mmdevice->lock);
1985             return MMSYSERR_ERROR;
1986         }
1987     }else if(control->dwControlID == 1){
1988         BOOL mute;
1989         MIXERCONTROLDETAILS_BOOLEAN *bdet;
1990
1991         if(!control->paDetails ||
1992                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
1993             LeaveCriticalSection(&mmdevice->lock);
1994             return MMSYSERR_INVALPARAM;
1995         }
1996
1997         bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
1998         mute = bdet->fValue;
1999
2000         hr = ISimpleAudioVolume_SetMute(mmdevice->volume, mute, NULL);
2001         if(FAILED(hr)){
2002             ERR("SetMute failed: %08x\n", hr);
2003             LeaveCriticalSection(&mmdevice->lock);
2004             return MMSYSERR_ERROR;
2005         }
2006     }else if(control->dwControlID == 2 || control->dwControlID == 3){
2007         FIXME("What should the sw-side mixer controls map to?\n");
2008     }else{
2009         LeaveCriticalSection(&mmdevice->lock);
2010         return MIXERR_INVALCONTROL;
2011     }
2012
2013     LeaveCriticalSection(&mmdevice->lock);
2014
2015     return MMSYSERR_NOERROR;
2016 }
2017
2018 static LRESULT CALLBACK WINMM_DevicesMsgProc(HWND hwnd, UINT msg, WPARAM wparam,
2019         LPARAM lparam)
2020 {
2021     switch(msg){
2022     case WODM_OPEN:
2023         return WOD_Open((WINMM_OpenInfo*)wparam);
2024     case WODM_CLOSE:
2025         return WOD_Close((HWAVEOUT)wparam);
2026     case WIDM_OPEN:
2027         return WID_Open((WINMM_OpenInfo*)wparam);
2028     case WIDM_CLOSE:
2029         return WID_Close((HWAVEIN)wparam);
2030     case MXDM_GETCONTROLDETAILS:
2031         return MXD_GetControlDetails((WINMM_ControlDetails*)wparam);
2032     case MXDM_SETCONTROLDETAILS:
2033         return MXD_SetControlDetails((WINMM_ControlDetails*)wparam);
2034     }
2035     return DefWindowProcW(hwnd, msg, wparam, lparam);
2036 }
2037
2038 static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
2039 {
2040     HANDLE evt = arg;
2041     HRESULT hr;
2042     static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0};
2043
2044     hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
2045     if(FAILED(hr)){
2046         ERR("CoInitializeEx failed: %08x\n", hr);
2047         return 1;
2048     }
2049
2050     hr = WINMM_InitMMDevices();
2051     if(FAILED(hr)){
2052         CoUninitialize();
2053         return 1;
2054     }
2055
2056     g_devices_hwnd = CreateWindowW(messageW, NULL, 0, 0, 0, 0, 0,
2057             HWND_MESSAGE, NULL, NULL, NULL);
2058     if(!g_devices_hwnd){
2059         ERR("CreateWindow failed: %d\n", GetLastError());
2060         CoUninitialize();
2061         return 1;
2062     }
2063
2064     SetWindowLongPtrW(g_devices_hwnd, GWLP_WNDPROC,
2065             (LONG_PTR)WINMM_DevicesMsgProc);
2066
2067     /* inform caller that the thread is ready to process messages */
2068     SetEvent(evt);
2069     evt = NULL; /* do not use after this point */
2070
2071     while(1){
2072         DWORD wait;
2073         wait = MsgWaitForMultipleObjects(g_devhandle_count, g_device_handles,
2074                 FALSE, INFINITE, QS_ALLINPUT);
2075         if(wait == g_devhandle_count + WAIT_OBJECT_0){
2076             MSG msg;
2077             if(PeekMessageW(&msg, g_devices_hwnd, 0, 0, PM_REMOVE))
2078                 ERR("Unexpected message: 0x%x\n", msg.message);
2079         }else if(wait < g_devhandle_count + WAIT_OBJECT_0){
2080             WINMM_Device *device = g_handle_devices[wait - WAIT_OBJECT_0];
2081             if(device->render)
2082                 WOD_PushData(device);
2083             else
2084                 WID_PullData(device);
2085         }else
2086             ERR("Unexpected MsgWait result 0x%x, GLE: %d\n", wait,
2087                     GetLastError());
2088     }
2089
2090     DestroyWindow(g_devices_hwnd);
2091
2092     CoUninitialize();
2093
2094     return 0;
2095 }
2096
2097 static BOOL WINMM_StartDevicesThread(void)
2098 {
2099     HANDLE events[2];
2100     DWORD wait;
2101
2102     EnterCriticalSection(&g_devthread_lock);
2103
2104     if(g_devices_thread){
2105         DWORD wait;
2106
2107         wait = WaitForSingleObject(g_devices_thread, 0);
2108         if(wait == WAIT_TIMEOUT){
2109             LeaveCriticalSection(&g_devthread_lock);
2110             return TRUE;
2111         }
2112         if(wait != WAIT_OBJECT_0){
2113             LeaveCriticalSection(&g_devthread_lock);
2114             return FALSE;
2115         }
2116
2117         g_devices_thread = NULL;
2118         g_devices_hwnd = NULL;
2119     }
2120
2121     TRACE("Starting up devices thread\n");
2122
2123     events[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
2124
2125     g_devices_thread = CreateThread(NULL, 0, WINMM_DevicesThreadProc,
2126             events[0], 0, NULL);
2127     if(!g_devices_thread){
2128         LeaveCriticalSection(&g_devthread_lock);
2129         CloseHandle(events[0]);
2130         return FALSE;
2131     }
2132
2133     events[1] = g_devices_thread;
2134     wait = WaitForMultipleObjects(2, events, FALSE, INFINITE);
2135     CloseHandle(events[0]);
2136     if(wait != WAIT_OBJECT_0){
2137         if(wait == 1 + WAIT_OBJECT_0){
2138             CloseHandle(g_devices_thread);
2139             g_devices_thread = NULL;
2140             g_devices_hwnd = NULL;
2141         }
2142         LeaveCriticalSection(&g_devthread_lock);
2143         return FALSE;
2144     }
2145
2146     LeaveCriticalSection(&g_devthread_lock);
2147
2148     return TRUE;
2149 }
2150
2151 /**************************************************************************
2152  *                              waveOutGetNumDevs               [WINMM.@]
2153  */
2154 UINT WINAPI waveOutGetNumDevs(void)
2155 {
2156     if(!WINMM_StartDevicesThread())
2157         return 0;
2158
2159     TRACE("count: %u\n", g_outmmdevices_count);
2160
2161     return g_outmmdevices_count;
2162 }
2163
2164 /**************************************************************************
2165  *                              waveOutGetDevCapsA              [WINMM.@]
2166  */
2167 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
2168                                UINT uSize)
2169 {
2170     WAVEOUTCAPSW        wocW;
2171     UINT                ret;
2172
2173     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2174
2175     if(!WINMM_StartDevicesThread())
2176         return MMSYSERR_ERROR;
2177
2178     if(!lpCaps)
2179         return MMSYSERR_INVALPARAM;
2180
2181     ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
2182
2183     if (ret == MMSYSERR_NOERROR) {
2184         WAVEOUTCAPSA wocA;
2185         wocA.wMid           = wocW.wMid;
2186         wocA.wPid           = wocW.wPid;
2187         wocA.vDriverVersion = wocW.vDriverVersion;
2188         WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,
2189                              sizeof(wocA.szPname), NULL, NULL );
2190         wocA.dwFormats      = wocW.dwFormats;
2191         wocA.wChannels      = wocW.wChannels;
2192         wocA.dwSupport      = wocW.dwSupport;
2193         memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));
2194     }
2195     return ret;
2196 }
2197
2198 /**************************************************************************
2199  *                              waveOutGetDevCapsW              [WINMM.@]
2200  */
2201 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
2202                                UINT uSize)
2203 {
2204     WAVEOUTCAPSW mapper_caps, *caps;
2205
2206     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2207
2208     if(!WINMM_StartDevicesThread())
2209         return MMSYSERR_ERROR;
2210
2211     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2212
2213     if(WINMM_IsMapper(uDeviceID)){
2214         /* FIXME: Should be localized */
2215         static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2216             'n','d',' ','M','a','p','p','e','r',0};
2217
2218         mapper_caps.wMid = 0xFF;
2219         mapper_caps.wPid = 0xFF;
2220         mapper_caps.vDriverVersion = 0x00010001;
2221         mapper_caps.dwFormats = 0xFFFFFFFF;
2222         mapper_caps.wReserved1 = 0;
2223         mapper_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
2224             WAVECAPS_SAMPLEACCURATE;
2225         mapper_caps.wChannels = 2;
2226         lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2227
2228         caps = &mapper_caps;
2229     }else{
2230         if(uDeviceID >= g_outmmdevices_count)
2231             return MMSYSERR_BADDEVICEID;
2232
2233         caps = &g_out_mmdevices[uDeviceID].out_caps;
2234     }
2235
2236     memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2237
2238     return MMSYSERR_NOERROR;
2239 }
2240
2241 /**************************************************************************
2242  *                              waveOutGetErrorTextA    [WINMM.@]
2243  *                              waveInGetErrorTextA     [WINMM.@]
2244  */
2245 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2246 {
2247     UINT        ret;
2248
2249     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2250     else if (uSize == 0) ret = MMSYSERR_NOERROR;
2251     else
2252     {
2253         LPWSTR  xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
2254         if (!xstr) ret = MMSYSERR_NOMEM;
2255         else
2256         {
2257             ret = waveOutGetErrorTextW(uError, xstr, uSize);
2258             if (ret == MMSYSERR_NOERROR)
2259                 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
2260             HeapFree(GetProcessHeap(), 0, xstr);
2261         }
2262     }
2263     return ret;
2264 }
2265
2266 /**************************************************************************
2267  *                              waveOutGetErrorTextW    [WINMM.@]
2268  *                              waveInGetErrorTextW     [WINMM.@]
2269  */
2270 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2271 {
2272     UINT        ret = MMSYSERR_BADERRNUM;
2273
2274     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2275     else if (uSize == 0) ret = MMSYSERR_NOERROR;
2276     else if (
2277                /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
2278                 * a warning for the test was always true */
2279                (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
2280                (uError >= WAVERR_BASE  && uError <= WAVERR_LASTERROR)) {
2281         if (LoadStringW(hWinMM32Instance,
2282                         uError, lpText, uSize) > 0) {
2283             ret = MMSYSERR_NOERROR;
2284         }
2285     }
2286     return ret;
2287 }
2288
2289 /**************************************************************************
2290  *                      waveOutOpen                     [WINMM.@]
2291  * All the args/structs have the same layout as the win16 equivalents
2292  */
2293 MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
2294                        LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2295                        DWORD_PTR dwInstance, DWORD dwFlags)
2296 {
2297     LRESULT res;
2298     WINMM_OpenInfo info;
2299     WINMM_CBInfo cb_info;
2300
2301     TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut, uDeviceID, lpFormat,
2302             dwCallback, dwInstance, dwFlags);
2303
2304     if(!WINMM_StartDevicesThread())
2305         return MMSYSERR_ERROR;
2306
2307     if(!lphWaveOut && !(dwFlags & WAVE_FORMAT_QUERY))
2308         return MMSYSERR_INVALPARAM;
2309
2310     res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2311     if(res != MMSYSERR_NOERROR)
2312         return res;
2313
2314     info.format = (WAVEFORMATEX*)lpFormat;
2315     info.callback = dwCallback;
2316     info.cb_user = dwInstance;
2317     info.req_device = uDeviceID;
2318     info.flags = dwFlags;
2319
2320     res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0);
2321     if(res != MMSYSERR_NOERROR)
2322         return res;
2323
2324     if(lphWaveOut)
2325         *lphWaveOut = (HWAVEOUT)info.handle;
2326
2327     cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2328     cb_info.callback = dwCallback;
2329     cb_info.user = dwInstance;
2330     cb_info.hwave = info.handle;
2331
2332     WINMM_NotifyClient(&cb_info, WOM_OPEN, 0, 0);
2333
2334     return res;
2335 }
2336
2337 /**************************************************************************
2338  *                              waveOutClose            [WINMM.@]
2339  */
2340 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
2341 {
2342     UINT res;
2343     WINMM_Device *device;
2344     WINMM_CBInfo cb_info;
2345
2346     TRACE("(%p)\n", hWaveOut);
2347
2348     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2349
2350     if(!WINMM_ValidateAndLock(device))
2351         return MMSYSERR_INVALHANDLE;
2352
2353     cb_info = device->cb_info;
2354
2355     LeaveCriticalSection(&device->lock);
2356
2357     res = SendMessageW(g_devices_hwnd, WODM_CLOSE, (WPARAM)hWaveOut, 0);
2358
2359     if(res == MMSYSERR_NOERROR)
2360         WINMM_NotifyClient(&cb_info, WOM_CLOSE, 0, 0);
2361
2362     return res;
2363 }
2364
2365 /**************************************************************************
2366  *                              waveOutPrepareHeader    [WINMM.@]
2367  */
2368 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
2369                                  WAVEHDR* lpWaveOutHdr, UINT uSize)
2370 {
2371     TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2372
2373     if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2374         return MMSYSERR_INVALPARAM;
2375
2376     if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2377         return WAVERR_STILLPLAYING;
2378
2379     return WINMM_PrepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2380 }
2381
2382 /**************************************************************************
2383  *                              waveOutUnprepareHeader  [WINMM.@]
2384  */
2385 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
2386                                    LPWAVEHDR lpWaveOutHdr, UINT uSize)
2387 {
2388     TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2389
2390     if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2391         return MMSYSERR_INVALPARAM;
2392     
2393     if(!(lpWaveOutHdr->dwFlags & WHDR_PREPARED))
2394         return MMSYSERR_NOERROR;
2395
2396     if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2397         return WAVERR_STILLPLAYING;
2398
2399     return WINMM_UnprepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2400 }
2401
2402 /**************************************************************************
2403  *                              waveOutWrite            [WINMM.@]
2404  */
2405 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, WAVEHDR *header, UINT uSize)
2406 {
2407     WINMM_Device *device;
2408     HRESULT hr;
2409
2410     TRACE("(%p, %p, %u)\n", hWaveOut, header, uSize);
2411
2412     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2413
2414     if(!WINMM_ValidateAndLock(device))
2415         return MMSYSERR_INVALHANDLE;
2416
2417     if(!header->lpData || !(header->dwFlags & WHDR_PREPARED)){
2418         LeaveCriticalSection(&device->lock);
2419         return WAVERR_UNPREPARED;
2420     }
2421
2422     if(header->dwFlags & WHDR_INQUEUE){
2423         LeaveCriticalSection(&device->lock);
2424         return WAVERR_STILLPLAYING;
2425     }
2426
2427     if(device->acm_handle){
2428         ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
2429         MMRESULT mr;
2430
2431         ash->cbSrcLength = header->dwBufferLength;
2432         mr = acmStreamConvert(device->acm_handle, ash, 0);
2433         if(mr != MMSYSERR_NOERROR){
2434             LeaveCriticalSection(&device->lock);
2435             return mr;
2436         }
2437     }
2438
2439     if(device->first){
2440         device->last->lpNext = header;
2441         device->last = header;
2442         if(!device->playing)
2443             device->playing = header;
2444     }else{
2445         device->playing = device->first = device->last = header;
2446         if(header->dwFlags & WHDR_BEGINLOOP){
2447             device->loop_counter = header->dwLoops;
2448             device->loop_start = header;
2449         }
2450     }
2451
2452     header->lpNext = NULL;
2453     header->dwFlags &= ~WHDR_DONE;
2454     header->dwFlags |= WHDR_INQUEUE;
2455
2456     hr = WINMM_BeginPlaying(device);
2457     if(FAILED(hr)){
2458         LeaveCriticalSection(&device->lock);
2459         return MMSYSERR_ERROR;
2460     }
2461
2462     LeaveCriticalSection(&device->lock);
2463
2464     return MMSYSERR_NOERROR;
2465 }
2466
2467 /**************************************************************************
2468  *                              waveOutBreakLoop        [WINMM.@]
2469  */
2470 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
2471 {
2472     WINMM_Device *device;
2473
2474     TRACE("(%p)\n", hWaveOut);
2475
2476     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2477
2478     if(!WINMM_ValidateAndLock(device))
2479         return MMSYSERR_INVALHANDLE;
2480
2481     device->loop_counter = 0;
2482
2483     LeaveCriticalSection(&device->lock);
2484
2485     return MMSYSERR_NOERROR;
2486 }
2487
2488 /**************************************************************************
2489  *                              waveOutPause            [WINMM.@]
2490  */
2491 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
2492 {
2493     TRACE("(%p)\n", hWaveOut);
2494
2495     return WINMM_Pause((HWAVE)hWaveOut);
2496 }
2497
2498 /**************************************************************************
2499  *                              waveOutReset            [WINMM.@]
2500  */
2501 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2502 {
2503     TRACE("(%p)\n", hWaveOut);
2504
2505     return WINMM_Reset((HWAVE)hWaveOut);
2506 }
2507
2508 /**************************************************************************
2509  *                              waveOutRestart          [WINMM.@]
2510  */
2511 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2512 {
2513     WINMM_Device *device;
2514     HRESULT hr;
2515
2516     TRACE("(%p)\n", hWaveOut);
2517
2518     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2519
2520     if(!WINMM_ValidateAndLock(device))
2521         return MMSYSERR_INVALHANDLE;
2522
2523     device->stopped = TRUE;
2524
2525     hr = WINMM_BeginPlaying(device);
2526     if(FAILED(hr)){
2527         LeaveCriticalSection(&device->lock);
2528         return MMSYSERR_ERROR;
2529     }
2530
2531     LeaveCriticalSection(&device->lock);
2532
2533     return MMSYSERR_NOERROR;
2534 }
2535
2536 /**************************************************************************
2537  *                              waveOutGetPosition      [WINMM.@]
2538  */
2539 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2540                                UINT uSize)
2541 {
2542     TRACE("(%p, %p, %u)\n", hWaveOut, lpTime, uSize);
2543
2544     if(!uSize || !lpTime || uSize != sizeof(MMTIME))
2545         return MMSYSERR_INVALPARAM;
2546
2547     return WINMM_GetPosition((HWAVE)hWaveOut, lpTime);
2548 }
2549
2550 /**************************************************************************
2551  *                              waveOutGetPitch         [WINMM.@]
2552  */
2553 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2554 {
2555     TRACE("(%p, %p)\n", hWaveOut, lpdw);
2556     return MMSYSERR_NOTSUPPORTED;
2557 }
2558
2559 /**************************************************************************
2560  *                              waveOutSetPitch         [WINMM.@]
2561  */
2562 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
2563 {
2564     TRACE("(%p, %08x)\n", hWaveOut, dw);
2565
2566     return MMSYSERR_NOTSUPPORTED;
2567 }
2568
2569 /**************************************************************************
2570  *                              waveOutGetPlaybackRate  [WINMM.@]
2571  */
2572 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
2573 {
2574     TRACE("(%p, %p)\n", hWaveOut, lpdw);
2575
2576     return MMSYSERR_NOTSUPPORTED;
2577 }
2578
2579 /**************************************************************************
2580  *                              waveOutSetPlaybackRate  [WINMM.@]
2581  */
2582 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
2583 {
2584     TRACE("(%p, %08x)\n", hWaveOut, dw);
2585
2586     return MMSYSERR_NOTSUPPORTED;
2587 }
2588
2589 /**************************************************************************
2590  *                              waveOutGetVolume        [WINMM.@]
2591  */
2592 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, DWORD *out)
2593 {
2594     WINMM_Device *device;
2595     UINT32 channels;
2596     float *vols;
2597     HRESULT hr;
2598
2599     TRACE("(%p, %p)\n", hWaveOut, out);
2600
2601     if(!out)
2602         return MMSYSERR_INVALPARAM;
2603
2604     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2605
2606     if(!WINMM_ValidateAndLock(device))
2607         return MMSYSERR_INVALHANDLE;
2608
2609     hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2610     if(FAILED(hr)){
2611         LeaveCriticalSection(&device->lock);
2612         ERR("GetChannelCount failed: %08x\n", hr);
2613         return MMSYSERR_ERROR;
2614     }
2615
2616     vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2617     if(!vols){
2618         LeaveCriticalSection(&device->lock);
2619         return MMSYSERR_NOMEM;
2620     }
2621
2622     hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2623     if(FAILED(hr)){
2624         LeaveCriticalSection(&device->lock);
2625         HeapFree(GetProcessHeap(), 0, vols);
2626         ERR("GetAllVolumes failed: %08x\n", hr);
2627         return MMSYSERR_ERROR;
2628     }
2629
2630     LeaveCriticalSection(&device->lock);
2631
2632     *out = ((UINT16)(vols[0] * (DWORD)0xFFFF));
2633     if(channels > 1)
2634         *out |= ((UINT16)(vols[1] * (DWORD)0xFFFF)) << 16;
2635
2636     HeapFree(GetProcessHeap(), 0, vols);
2637
2638     return MMSYSERR_NOERROR;
2639 }
2640
2641 /**************************************************************************
2642  *                              waveOutSetVolume        [WINMM.@]
2643  */
2644 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD in)
2645 {
2646     WINMM_Device *device;
2647     UINT32 channels;
2648     float *vols;
2649     HRESULT hr;
2650
2651     TRACE("(%p, %08x)\n", hWaveOut, in);
2652
2653     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2654
2655     if(!WINMM_ValidateAndLock(device))
2656         return MMSYSERR_INVALHANDLE;
2657
2658     hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2659     if(FAILED(hr)){
2660         LeaveCriticalSection(&device->lock);
2661         ERR("GetChannelCount failed: %08x\n", hr);
2662         return MMSYSERR_ERROR;
2663     }
2664
2665     vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2666     if(!vols){
2667         LeaveCriticalSection(&device->lock);
2668         return MMSYSERR_NOMEM;
2669     }
2670
2671     hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2672     if(FAILED(hr)){
2673         LeaveCriticalSection(&device->lock);
2674         HeapFree(GetProcessHeap(), 0, vols);
2675         ERR("GetAllVolumes failed: %08x\n", hr);
2676         return MMSYSERR_ERROR;
2677     }
2678
2679     vols[0] = (float)((DWORD)(in & 0xFFFF) / (float)0xFFFF);
2680     if(channels > 1)
2681         vols[1] = (float)((DWORD)(in >> 16) / (float)0xFFFF);
2682
2683     hr = IAudioStreamVolume_SetAllVolumes(device->volume, channels, vols);
2684     if(FAILED(hr)){
2685         LeaveCriticalSection(&device->lock);
2686         HeapFree(GetProcessHeap(), 0, vols);
2687         ERR("SetAllVolumes failed: %08x\n", hr);
2688         return MMSYSERR_ERROR;
2689     }
2690
2691     LeaveCriticalSection(&device->lock);
2692
2693     HeapFree(GetProcessHeap(), 0, vols);
2694
2695     return MMSYSERR_NOERROR;
2696 }
2697
2698 /**************************************************************************
2699  *                              waveOutGetID            [WINMM.@]
2700  */
2701 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
2702 {
2703     WINMM_Device *device;
2704     UINT dev, junk;
2705     BOOL is_out;
2706
2707     TRACE("(%p, %p)\n", hWaveOut, lpuDeviceID);
2708
2709     if(!lpuDeviceID)
2710         return MMSYSERR_INVALPARAM;
2711
2712     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2713     if(!WINMM_ValidateAndLock(device))
2714         return MMSYSERR_INVALHANDLE;
2715
2716     LeaveCriticalSection(&device->lock);
2717
2718     WINMM_DecomposeHWAVE((HWAVE)hWaveOut, lpuDeviceID, &is_out, &dev, &junk);
2719
2720     return MMSYSERR_NOERROR;
2721 }
2722
2723 static UINT WINMM_QueryInstanceIDSize(UINT device, DWORD_PTR *len, BOOL is_out)
2724 {
2725     UINT count;
2726     WINMM_MMDevice *devices;
2727
2728     TRACE("(%u, %p, %d)\n", device, len, is_out);
2729
2730     if(is_out){
2731         count = g_outmmdevices_count;
2732         devices = g_out_mmdevices;
2733     }else{
2734         count = g_inmmdevices_count;
2735         devices = g_in_mmdevices;
2736     }
2737
2738     if(device >= count)
2739         return MMSYSERR_INVALHANDLE;
2740
2741     *len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2742
2743     return MMSYSERR_NOERROR;
2744 }
2745
2746 static UINT WINMM_QueryInstanceID(UINT device, WCHAR *str, DWORD_PTR len,
2747         BOOL is_out)
2748 {
2749     UINT count, id_len;
2750     WINMM_MMDevice *devices;
2751
2752     TRACE("(%u, %p, %d)\n", device, str, is_out);
2753
2754     if(is_out){
2755         count = g_outmmdevices_count;
2756         devices = g_out_mmdevices;
2757     }else{
2758         count = g_inmmdevices_count;
2759         devices = g_in_mmdevices;
2760     }
2761
2762     if(device >= count)
2763         return MMSYSERR_INVALHANDLE;
2764
2765     id_len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2766     if(len < id_len)
2767         return MMSYSERR_ERROR;
2768
2769     memcpy(str, devices[device].dev_id, id_len);
2770
2771     return MMSYSERR_NOERROR;
2772 }
2773
2774 /**************************************************************************
2775  *                              waveOutMessage          [WINMM.@]
2776  */
2777 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2778                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2779 {
2780     TRACE("(%p, %u, %lx, %lx)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2781
2782     switch(uMessage){
2783     case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
2784         return WINMM_QueryInstanceIDSize(HandleToULong(hWaveOut),
2785                 (DWORD_PTR*)dwParam1, TRUE);
2786     case DRV_QUERYFUNCTIONINSTANCEID:
2787         return WINMM_QueryInstanceID(HandleToULong(hWaveOut), (WCHAR*)dwParam1, dwParam2, TRUE);
2788     case DRV_QUERYMAPPABLE:
2789         return MMSYSERR_NOERROR;
2790     }
2791
2792     return MMSYSERR_NOTSUPPORTED;
2793 }
2794
2795 /**************************************************************************
2796  *                              waveInGetNumDevs                [WINMM.@]
2797  */
2798 UINT WINAPI waveInGetNumDevs(void)
2799 {
2800     if(!WINMM_StartDevicesThread())
2801         return 0;
2802
2803     TRACE("count: %u\n", g_inmmdevices_count);
2804
2805     return g_inmmdevices_count;
2806 }
2807
2808 /**************************************************************************
2809  *                              waveInGetDevCapsW               [WINMM.@]
2810  */
2811 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2812 {
2813     WAVEINCAPSW mapper_caps, *caps;
2814
2815     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2816
2817     if(!WINMM_StartDevicesThread())
2818         return MMSYSERR_ERROR;
2819
2820     if(!lpCaps)
2821         return MMSYSERR_INVALPARAM;
2822
2823     if(WINMM_IsMapper(uDeviceID)){
2824         /* FIXME: Should be localized */
2825         static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2826             'n','d',' ','M','a','p','p','e','r',0};
2827
2828         mapper_caps.wMid = 0xFF;
2829         mapper_caps.wPid = 0xFF;
2830         mapper_caps.vDriverVersion = 0x00010001;
2831         mapper_caps.dwFormats = 0xFFFFFFFF;
2832         mapper_caps.wReserved1 = 0;
2833         mapper_caps.wChannels = 2;
2834         lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2835
2836         caps = &mapper_caps;
2837     }else{
2838         if(uDeviceID >= g_inmmdevices_count)
2839             return MMSYSERR_BADDEVICEID;
2840
2841         caps = &g_in_mmdevices[uDeviceID].in_caps;
2842     }
2843
2844     memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2845
2846     return MMSYSERR_NOERROR;
2847 }
2848
2849 /**************************************************************************
2850  *                              waveInGetDevCapsA               [WINMM.@]
2851  */
2852 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2853 {
2854     UINT ret;
2855     WAVEINCAPSW wicW;
2856
2857     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2858
2859     if(!WINMM_StartDevicesThread())
2860         return MMSYSERR_ERROR;
2861
2862     if(!lpCaps)
2863         return MMSYSERR_INVALPARAM;
2864
2865     ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2866
2867     if (ret == MMSYSERR_NOERROR) {
2868         WAVEINCAPSA wicA;
2869         wicA.wMid           = wicW.wMid;
2870         wicA.wPid           = wicW.wPid;
2871         wicA.vDriverVersion = wicW.vDriverVersion;
2872         WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2873                              sizeof(wicA.szPname), NULL, NULL );
2874         wicA.dwFormats      = wicW.dwFormats;
2875         wicA.wChannels      = wicW.wChannels;
2876         memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2877     }
2878     return ret;
2879 }
2880
2881 /**************************************************************************
2882  *                              waveInOpen                      [WINMM.@]
2883  */
2884 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2885                            LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2886                            DWORD_PTR dwInstance, DWORD dwFlags)
2887 {
2888     LRESULT res;
2889     WINMM_OpenInfo info;
2890     WINMM_CBInfo cb_info;
2891
2892     TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
2893             dwCallback, dwInstance, dwFlags);
2894
2895     if(!WINMM_StartDevicesThread())
2896         return MMSYSERR_ERROR;
2897
2898     if(!lphWaveIn && !(dwFlags & WAVE_FORMAT_QUERY))
2899         return MMSYSERR_INVALPARAM;
2900
2901     res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2902     if(res != MMSYSERR_NOERROR)
2903         return res;
2904
2905     info.format = (WAVEFORMATEX*)lpFormat;
2906     info.callback = dwCallback;
2907     info.cb_user = dwInstance;
2908     info.req_device = uDeviceID;
2909     info.flags = dwFlags;
2910
2911     res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
2912     if(res != MMSYSERR_NOERROR)
2913         return res;
2914
2915     if(lphWaveIn)
2916         *lphWaveIn = (HWAVEIN)info.handle;
2917
2918     cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2919     cb_info.callback = dwCallback;
2920     cb_info.user = dwInstance;
2921     cb_info.hwave = info.handle;
2922
2923     WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
2924
2925     return res;
2926 }
2927
2928 /**************************************************************************
2929  *                              waveInClose                     [WINMM.@]
2930  */
2931 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2932 {
2933     WINMM_Device *device;
2934     WINMM_CBInfo cb_info;
2935     UINT res;
2936
2937     TRACE("(%p)\n", hWaveIn);
2938
2939     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
2940
2941     if(!WINMM_ValidateAndLock(device))
2942         return MMSYSERR_INVALHANDLE;
2943
2944     cb_info = device->cb_info;
2945
2946     LeaveCriticalSection(&device->lock);
2947
2948     res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
2949
2950     if(res == MMSYSERR_NOERROR)
2951         WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
2952
2953     return res;
2954 }
2955
2956 /**************************************************************************
2957  *                              waveInPrepareHeader             [WINMM.@]
2958  */
2959 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2960                                 UINT uSize)
2961 {
2962     TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2963
2964     if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2965         return MMSYSERR_INVALPARAM;
2966
2967     if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2968         return WAVERR_STILLPLAYING;
2969
2970     return WINMM_PrepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
2971 }
2972
2973 /**************************************************************************
2974  *                              waveInUnprepareHeader   [WINMM.@]
2975  */
2976 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2977                                   UINT uSize)
2978 {
2979     TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2980
2981     if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2982         return MMSYSERR_INVALPARAM;
2983
2984     if(!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
2985         return MMSYSERR_NOERROR;
2986
2987     if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2988         return WAVERR_STILLPLAYING;
2989
2990     return WINMM_UnprepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
2991 }
2992
2993 /**************************************************************************
2994  *                              waveInAddBuffer         [WINMM.@]
2995  */
2996 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn, WAVEHDR *header, UINT uSize)
2997 {
2998     WINMM_Device *device;
2999
3000     TRACE("(%p, %p, %u)\n", hWaveIn, header, uSize);
3001
3002     if(!header || uSize < sizeof(WAVEHDR))
3003         return MMSYSERR_INVALPARAM;
3004
3005     if(!(header->dwFlags & WHDR_PREPARED))
3006         return WAVERR_UNPREPARED;
3007
3008     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3009
3010     if(!WINMM_ValidateAndLock(device))
3011         return MMSYSERR_INVALHANDLE;
3012
3013     if(!device->first)
3014         device->first = device->last = header;
3015     else{
3016         device->last->lpNext = header;
3017         device->last = header;
3018     }
3019
3020     header->dwBytesRecorded = 0;
3021     header->lpNext = NULL;
3022     header->dwFlags &= ~WHDR_DONE;
3023     header->dwFlags |= WHDR_INQUEUE;
3024
3025     LeaveCriticalSection(&device->lock);
3026
3027     return MMSYSERR_NOERROR;
3028 }
3029
3030 /**************************************************************************
3031  *                              waveInReset             [WINMM.@]
3032  */
3033 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
3034 {
3035     TRACE("(%p)\n", hWaveIn);
3036
3037     return WINMM_Reset((HWAVE)hWaveIn);
3038 }
3039
3040 /**************************************************************************
3041  *                              waveInStart             [WINMM.@]
3042  */
3043 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
3044 {
3045     WINMM_Device *device;
3046     HRESULT hr;
3047
3048     TRACE("(%p)\n", hWaveIn);
3049
3050     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3051
3052     if(!WINMM_ValidateAndLock(device))
3053         return MMSYSERR_INVALHANDLE;
3054
3055     hr = WINMM_BeginPlaying(device);
3056     if(FAILED(hr)){
3057         LeaveCriticalSection(&device->lock);
3058         return MMSYSERR_ERROR;
3059     }
3060
3061     LeaveCriticalSection(&device->lock);
3062
3063     return MMSYSERR_NOERROR;
3064 }
3065
3066 /**************************************************************************
3067  *                              waveInStop              [WINMM.@]
3068  */
3069 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
3070 {
3071     WINMM_CBInfo cb_info;
3072     WINMM_Device *device;
3073     WAVEHDR *buf;
3074     HRESULT hr;
3075
3076     TRACE("(%p)\n", hWaveIn);
3077
3078     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3079
3080     if(!WINMM_ValidateAndLock(device))
3081         return MMSYSERR_INVALHANDLE;
3082
3083     hr = WINMM_Pause((HWAVE)hWaveIn);
3084     if(FAILED(hr)){
3085         LeaveCriticalSection(&device->lock);
3086         return MMSYSERR_ERROR;
3087     }
3088     device->stopped = TRUE;
3089
3090     buf = device->first;
3091     if(buf && buf->dwBytesRecorded > 0){
3092         device->first = buf->lpNext;
3093     }else
3094         buf = NULL;
3095
3096     cb_info = device->cb_info;
3097
3098     LeaveCriticalSection(&device->lock);
3099
3100     if(buf){
3101         buf->dwFlags &= ~WHDR_INQUEUE;
3102         buf->dwFlags |= WHDR_DONE;
3103         WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
3104     }
3105
3106     return MMSYSERR_NOERROR;
3107 }
3108
3109 /**************************************************************************
3110  *                              waveInGetPosition       [WINMM.@]
3111  */
3112 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
3113                               UINT uSize)
3114 {
3115     TRACE("(%p, %p, %u)\n", hWaveIn, lpTime, uSize);
3116
3117     if(!uSize || !lpTime || uSize != sizeof(MMTIME))
3118         return MMSYSERR_INVALPARAM;
3119
3120     return WINMM_GetPosition((HWAVE)hWaveIn, lpTime);
3121 }
3122
3123 /**************************************************************************
3124  *                              waveInGetID                     [WINMM.@]
3125  */
3126 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
3127 {
3128     UINT dev, junk;
3129     BOOL is_out;
3130     WINMM_Device *device;
3131
3132     TRACE("(%p, %p)\n", hWaveIn, lpuDeviceID);
3133
3134     if(!lpuDeviceID)
3135         return MMSYSERR_INVALPARAM;
3136
3137     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3138     if(!WINMM_ValidateAndLock(device))
3139         return MMSYSERR_INVALHANDLE;
3140
3141     LeaveCriticalSection(&device->lock);
3142
3143     WINMM_DecomposeHWAVE((HWAVE)hWaveIn, lpuDeviceID, &is_out, &dev, &junk);
3144
3145     return MMSYSERR_NOERROR;
3146 }
3147
3148 /**************************************************************************
3149  *                              waveInMessage           [WINMM.@]
3150  */
3151 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
3152                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3153 {
3154     TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
3155
3156     switch(uMessage){
3157     case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
3158         return WINMM_QueryInstanceIDSize(HandleToULong(hWaveIn),
3159                 (DWORD_PTR*)dwParam1, FALSE);
3160     case DRV_QUERYFUNCTIONINSTANCEID:
3161         return WINMM_QueryInstanceID(HandleToULong(hWaveIn), (WCHAR*)dwParam1, dwParam2, FALSE);
3162     case DRV_QUERYMAPPABLE:
3163         return MMSYSERR_NOERROR;
3164     }
3165
3166     return MMSYSERR_NOTSUPPORTED;
3167 }
3168
3169 UINT WINAPI mixerGetNumDevs(void)
3170 {
3171     TRACE("\n");
3172
3173     if(!WINMM_StartDevicesThread())
3174         return 0;
3175
3176     return g_outmmdevices_count + g_inmmdevices_count;
3177 }
3178
3179 /**************************************************************************
3180  *                              mixerGetDevCapsA                [WINMM.@]
3181  */
3182 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
3183 {
3184     MIXERCAPSW micW;
3185     UINT       ret;
3186
3187     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3188
3189     if(!lpCaps)
3190         return MMSYSERR_INVALPARAM;
3191
3192     ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
3193
3194     if (ret == MMSYSERR_NOERROR) {
3195         MIXERCAPSA micA;
3196         micA.wMid           = micW.wMid;
3197         micA.wPid           = micW.wPid;
3198         micA.vDriverVersion = micW.vDriverVersion;
3199         WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
3200                              sizeof(micA.szPname), NULL, NULL );
3201         micA.fdwSupport     = micW.fdwSupport;
3202         micA.cDestinations  = micW.cDestinations;
3203         memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
3204     }
3205     return ret;
3206 }
3207
3208 /**************************************************************************
3209  *                              mixerGetDevCapsW                [WINMM.@]
3210  */
3211 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
3212 {
3213     WINMM_MMDevice *mmdevice;
3214     MIXERCAPSW caps;
3215
3216     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3217
3218     if(!WINMM_StartDevicesThread())
3219         return MMSYSERR_ERROR;
3220
3221     if(!lpCaps)
3222         return MMSYSERR_INVALPARAM;
3223
3224     if(!uSize)
3225         return MMSYSERR_NOERROR;
3226
3227     if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3228         return MMSYSERR_BADDEVICEID;
3229
3230     if(uDeviceID < g_outmmdevices_count){
3231         mmdevice = &g_out_mmdevices[uDeviceID];
3232         memcpy(caps.szPname, mmdevice->out_caps.szPname, sizeof(caps.szPname));
3233     }else{
3234         mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3235         memcpy(caps.szPname, mmdevice->in_caps.szPname, sizeof(caps.szPname));
3236     }
3237
3238     caps.wMid = 0xFF;
3239     caps.wPid = 0xFF;
3240     caps.vDriverVersion = 0x00010001;
3241     caps.fdwSupport = 0;
3242     caps.cDestinations = 1;
3243
3244     memcpy(lpCaps, &caps, uSize);
3245
3246     return MMSYSERR_NOERROR;
3247 }
3248
3249 /**************************************************************************
3250  *                              mixerOpen                       [WINMM.@]
3251  */
3252 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
3253                       DWORD_PTR dwInstance, DWORD fdwOpen)
3254 {
3255     WINMM_MMDevice *mmdevice;
3256     MMRESULT mr;
3257
3258     TRACE("(%p, %d, %lx, %lx, %x)\n", lphMix, uDeviceID, dwCallback,
3259             dwInstance, fdwOpen);
3260
3261     if(!WINMM_StartDevicesThread())
3262         return MMSYSERR_ERROR;
3263
3264     if(!lphMix)
3265         return MMSYSERR_INVALPARAM;
3266
3267     mr = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
3268     if(mr != MMSYSERR_NOERROR)
3269         return mr;
3270
3271     if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3272         return MMSYSERR_BADDEVICEID;
3273
3274     if(uDeviceID < g_outmmdevices_count){
3275         mmdevice = &g_out_mmdevices[uDeviceID];
3276         *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID, TRUE,
3277                 mmdevice->mixer_count);
3278     }else{
3279         mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3280         *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID - g_outmmdevices_count,
3281                 FALSE, mmdevice->mixer_count);
3282     }
3283
3284     ++mmdevice->mixer_count;
3285
3286     return MMSYSERR_NOERROR;
3287 }
3288
3289 /**************************************************************************
3290  *                              mixerClose                      [WINMM.@]
3291  */
3292 UINT WINAPI mixerClose(HMIXER hMix)
3293 {
3294     TRACE("(%p)\n", hMix);
3295
3296     return MMSYSERR_NOERROR;
3297 }
3298
3299 /**************************************************************************
3300  *                              mixerGetID                      [WINMM.@]
3301  */
3302 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
3303 {
3304     WINMM_MMDevice *mmdevice;
3305
3306     TRACE("(%p, %p, %x)\n", hmix, lpid, fdwID);
3307
3308     if(!WINMM_StartDevicesThread())
3309         return MMSYSERR_ERROR;
3310
3311     if(!lpid)
3312         return MMSYSERR_INVALPARAM;
3313
3314     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwID, lpid);
3315     if(!mmdevice)
3316         return MMSYSERR_INVALHANDLE;
3317
3318     if(mmdevice->in_caps.szPname[0] != '\0')
3319         *lpid += g_outmmdevices_count;
3320
3321     return MMSYSERR_NOERROR;
3322 }
3323
3324 /**************************************************************************
3325  *                              mixerGetControlDetailsW         [WINMM.@]
3326  */
3327 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
3328                                     DWORD fdwDetails)
3329 {
3330     WINMM_ControlDetails details;
3331
3332     TRACE("(%p, %p, %x)\n", hmix, lpmcdW, fdwDetails);
3333
3334     if(!WINMM_StartDevicesThread())
3335         return MMSYSERR_ERROR;
3336
3337     if(!lpmcdW)
3338         return MMSYSERR_INVALPARAM;
3339
3340     TRACE("dwControlID: %u\n", lpmcdW->dwControlID);
3341
3342     details.hmix = hmix;
3343     details.details = lpmcdW;
3344     details.flags = fdwDetails;
3345
3346     return SendMessageW(g_devices_hwnd, MXDM_GETCONTROLDETAILS,
3347             (DWORD_PTR)&details, 0);
3348 }
3349
3350 /**************************************************************************
3351  *                              mixerGetControlDetailsA [WINMM.@]
3352  */
3353 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
3354                                     DWORD fdwDetails)
3355 {
3356     UINT ret = MMSYSERR_NOTSUPPORTED;
3357
3358     TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
3359
3360     if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
3361         return MMSYSERR_INVALPARAM;
3362
3363     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
3364     case MIXER_GETCONTROLDETAILSF_VALUE:
3365         /* can safely use A structure as it is, no string inside */
3366         ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3367         break;
3368     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
3369         {
3370             MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = lpmcdA->paDetails;
3371             MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
3372             int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3373             unsigned int i;
3374
3375             if (lpmcdA->u.cMultipleItems != 0) {
3376                 size *= lpmcdA->u.cMultipleItems;
3377             }
3378             pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
3379             lpmcdA->paDetails = pDetailsW;
3380             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3381             /* set up lpmcd->paDetails */
3382             ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3383             /* copy from lpmcd->paDetails back to paDetailsW; */
3384             if (ret == MMSYSERR_NOERROR) {
3385                 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
3386                     pDetailsA->dwParam1 = pDetailsW->dwParam1;
3387                     pDetailsA->dwParam2 = pDetailsW->dwParam2;
3388                     WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
3389                                          pDetailsA->szName,
3390                                          sizeof(pDetailsA->szName), NULL, NULL );
3391                     pDetailsA++;
3392                     pDetailsW++;
3393                 }
3394                 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3395                 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3396             }
3397             HeapFree(GetProcessHeap(), 0, pDetailsW);
3398             lpmcdA->paDetails = pDetailsA;
3399             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
3400         }
3401         break;
3402     default:
3403         ERR("Unsupported fdwDetails=0x%08x\n", fdwDetails);
3404     }
3405
3406     return ret;
3407 }
3408
3409 /**************************************************************************
3410  *                              mixerGetLineControlsA   [WINMM.@]
3411  */
3412 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
3413                                   DWORD fdwControls)
3414 {
3415     MIXERLINECONTROLSW  mlcW;
3416     DWORD               ret;
3417     unsigned int        i;
3418
3419     TRACE("(%p, %p, %x)\n", hmix, lpmlcA, fdwControls);
3420
3421     if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
3422         lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
3423         return MMSYSERR_INVALPARAM;
3424
3425     mlcW.cbStruct = sizeof(mlcW);
3426     mlcW.dwLineID = lpmlcA->dwLineID;
3427     mlcW.u.dwControlID = lpmlcA->u.dwControlID;
3428     mlcW.u.dwControlType = lpmlcA->u.dwControlType;
3429
3430     /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
3431        the control count is assumed to be 1 - This is relied upon by a game,
3432        "Dynomite Deluze"                                                    */
3433     if (MIXER_GETLINECONTROLSF_ONEBYTYPE == (fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK)) {
3434         mlcW.cControls = 1;
3435     } else {
3436         mlcW.cControls = lpmlcA->cControls;
3437     }
3438     mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
3439     mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
3440                               mlcW.cControls * mlcW.cbmxctrl);
3441
3442     ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
3443
3444     if (ret == MMSYSERR_NOERROR) {
3445         lpmlcA->dwLineID = mlcW.dwLineID;
3446         lpmlcA->u.dwControlID = mlcW.u.dwControlID;
3447         lpmlcA->u.dwControlType = mlcW.u.dwControlType;
3448
3449         for (i = 0; i < mlcW.cControls; i++) {
3450             lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
3451             lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
3452             lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
3453             lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
3454             lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
3455             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
3456                                  lpmlcA->pamxctrl[i].szShortName,
3457                                  sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
3458             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
3459                                  lpmlcA->pamxctrl[i].szName,
3460                                  sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
3461             /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
3462              * sizeof(mlcW.pamxctrl[i].Bounds) */
3463             memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
3464                    sizeof(mlcW.pamxctrl[i].Bounds));
3465             /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
3466              * sizeof(mlcW.pamxctrl[i].Metrics) */
3467             memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
3468                    sizeof(mlcW.pamxctrl[i].Metrics));
3469         }
3470     }
3471
3472     HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
3473
3474     return ret;
3475 }
3476
3477 static UINT WINMM_GetVolumeLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3478         MIXERCONTROLW *ctl, DWORD flags)
3479 {
3480     ctl->dwControlID = (line == 0xFFFF0000) ? 0 : 2;
3481     ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
3482     ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3483     ctl->cMultipleItems = 0;
3484     lstrcpyW(ctl->szShortName, volumeW);
3485     lstrcpyW(ctl->szName, volumeW);
3486     ctl->Bounds.s1.dwMinimum = 0;
3487     ctl->Bounds.s1.dwMaximum = 0xFFFF;
3488     ctl->Metrics.cSteps = 192;
3489
3490     return MMSYSERR_NOERROR;
3491 }
3492
3493 static UINT WINMM_GetMuteLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3494         MIXERCONTROLW *ctl, DWORD flags)
3495 {
3496     ctl->dwControlID = (line == 0xFFFF0000) ? 1 : 3;
3497     ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
3498     ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3499     ctl->cMultipleItems = 0;
3500     lstrcpyW(ctl->szShortName, muteW);
3501     lstrcpyW(ctl->szName, muteW);
3502     ctl->Bounds.s1.dwMinimum = 0;
3503     ctl->Bounds.s1.dwMaximum = 1;
3504     ctl->Metrics.cSteps = 0;
3505
3506     return MMSYSERR_NOERROR;
3507 }
3508
3509 /**************************************************************************
3510  *                              mixerGetLineControlsW           [WINMM.@]
3511  */
3512 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
3513                                   DWORD fdwControls)
3514 {
3515     WINMM_MMDevice *mmdevice;
3516
3517     TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
3518
3519     if(!WINMM_StartDevicesThread())
3520         return MMSYSERR_ERROR;
3521
3522     if(fdwControls & ~(MIXER_GETLINECONTROLSF_ALL |
3523                 MIXER_GETLINECONTROLSF_ONEBYID |
3524                 MIXER_GETLINECONTROLSF_ONEBYTYPE |
3525                 MIXER_OBJECTF_HMIXER |
3526                 MIXER_OBJECTF_MIXER)){
3527         WARN("Unknown GetLineControls flag: %x\n", fdwControls);
3528         return MMSYSERR_INVALFLAG;
3529     }
3530
3531     if(!lpmlcW || lpmlcW->cbStruct < sizeof(*lpmlcW) || !lpmlcW->pamxctrl)
3532         return MMSYSERR_INVALPARAM;
3533
3534     TRACE("dwLineID: %u\n", lpmlcW->dwLineID);
3535     TRACE("dwControl: %x\n", lpmlcW->u.dwControlID);
3536     TRACE("cControls: %u\n", lpmlcW->cControls);
3537
3538     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwControls, NULL);
3539     if(!mmdevice)
3540         return MMSYSERR_INVALHANDLE;
3541
3542     switch(fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK){
3543     case MIXER_GETLINECONTROLSF_ALL:
3544         if(lpmlcW->cControls != 2)
3545             return MMSYSERR_INVALPARAM;
3546         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3547             return MMSYSERR_INVALPARAM;
3548         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3549             return MIXERR_INVALLINE;
3550         WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3551                 &lpmlcW->pamxctrl[0], fdwControls);
3552         WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3553                 &lpmlcW->pamxctrl[1], fdwControls);
3554         return MMSYSERR_NOERROR;
3555     case MIXER_GETLINECONTROLSF_ONEBYID:
3556         if(lpmlcW->cControls != 1)
3557             return MMSYSERR_INVALPARAM;
3558         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3559             return MMSYSERR_INVALPARAM;
3560         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3561             return MIXERR_INVALLINE;
3562         if(lpmlcW->u.dwControlID == 0)
3563             return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3564                     lpmlcW->pamxctrl, fdwControls);
3565         if(lpmlcW->u.dwControlID == 1)
3566             return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3567                     lpmlcW->pamxctrl, fdwControls);
3568         return MMSYSERR_NOTSUPPORTED;
3569     case MIXER_GETLINECONTROLSF_ONEBYTYPE:
3570         if(lpmlcW->cControls != 1)
3571             return MMSYSERR_INVALPARAM;
3572         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3573             return MMSYSERR_INVALPARAM;
3574         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3575             return MIXERR_INVALLINE;
3576         if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
3577             return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3578                     lpmlcW->pamxctrl, fdwControls);
3579         if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
3580             return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3581                     lpmlcW->pamxctrl, fdwControls);
3582         return MMSYSERR_NOTSUPPORTED;
3583     }
3584
3585     return MMSYSERR_NOTSUPPORTED;
3586 }
3587
3588 static UINT WINMM_GetSourceLineInfo(WINMM_MMDevice *mmdevice, UINT mmdev_index,
3589         MIXERLINEW *info, DWORD flags)
3590 {
3591     BOOL is_out = TRUE;
3592     if(mmdevice->in_caps.szPname[0] != '\0')
3593         is_out = FALSE;
3594
3595     if(info->dwSource != 0)
3596         return MIXERR_INVALLINE;
3597
3598     info->dwDestination = 0;
3599     info->dwLineID = 0;
3600     info->fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
3601     info->cConnections = 0;
3602     info->cControls = 2;
3603     /* volume & mute always affect all channels, so claim 1 channel */
3604     info->cChannels = 1;
3605     info->Target.dwDeviceID = mmdev_index;
3606     info->Target.wMid = ~0;
3607     info->Target.wPid = ~0;
3608     info->Target.vDriverVersion = 0;
3609
3610     lstrcpyW(info->szShortName, volumeW);
3611     lstrcpyW(info->szName, mastervolumeW);
3612
3613     if(is_out){
3614         info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
3615         info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
3616         memcpy(info->Target.szPname, mmdevice->out_caps.szPname,
3617                 sizeof(info->Target.szPname));
3618     }else{
3619         info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
3620         info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3621         info->Target.szPname[0] = '\0';
3622     }
3623
3624     return MMSYSERR_NOERROR;
3625 }
3626
3627 static UINT WINMM_GetDestinationLineInfo(WINMM_MMDevice *mmdevice,
3628         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3629 {
3630     BOOL is_out = TRUE;
3631     if(mmdevice->in_caps.szPname[0] != '\0')
3632         is_out = FALSE;
3633
3634     if(info->dwDestination != 0)
3635         return MIXERR_INVALLINE;
3636
3637     info->dwSource = 0xFFFFFFFF;
3638     info->dwLineID = 0xFFFF0000;
3639     info->fdwLine = MIXERLINE_LINEF_ACTIVE;
3640     info->cConnections = 1;
3641     info->cControls = 2;
3642
3643     lstrcpyW(info->szShortName, volumeW);
3644     lstrcpyW(info->szName, mastervolumeW);
3645
3646     info->Target.dwDeviceID = mmdev_index;
3647     info->Target.wMid = ~0;
3648     info->Target.wPid = ~0;
3649     info->Target.vDriverVersion = 0;
3650
3651     if(is_out){
3652         info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
3653         info->cChannels = mmdevice->out_caps.wChannels;
3654         info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3655         info->Target.szPname[0] = '\0';
3656     }else{
3657         info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
3658         info->cChannels = mmdevice->in_caps.wChannels;
3659         info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
3660         memcpy(info->Target.szPname, mmdevice->in_caps.szPname,
3661                 sizeof(info->Target.szPname));
3662     }
3663
3664     return MMSYSERR_NOERROR;
3665 }
3666
3667 static UINT WINMM_GetComponentTypeLineInfo(WINMM_MMDevice *mmdevice,
3668         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3669 {
3670     BOOL is_out = TRUE;
3671     if(mmdevice->in_caps.szPname[0] != '\0')
3672         is_out = FALSE;
3673
3674     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN){
3675         if(is_out)
3676             return MIXERR_INVALLINE;
3677         info->dwDestination = 0;
3678         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3679     }
3680
3681     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS){
3682         if(!is_out)
3683             return MIXERR_INVALLINE;
3684         info->dwDestination = 0;
3685         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3686     }
3687
3688     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE){
3689         if(is_out)
3690             return MIXERR_INVALLINE;
3691         info->dwSource = 0;
3692         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3693     }
3694
3695     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
3696         if(!is_out)
3697             return MIXERR_INVALLINE;
3698         info->dwSource = 0;
3699         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3700     }
3701
3702     TRACE("Returning INVALLINE on this component type: %u\n",
3703             info->dwComponentType);
3704
3705     return MIXERR_INVALLINE;
3706 }
3707
3708 static UINT WINMM_GetLineIDLineInfo(WINMM_MMDevice *mmdevice,
3709         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3710 {
3711     if(info->dwLineID == 0xFFFF0000){
3712         info->dwDestination = 0;
3713         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3714     }
3715
3716     if(info->dwLineID == 0){
3717         info->dwSource = 0;
3718         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3719     }
3720
3721     TRACE("Returning INVALLINE on this dwLineID: %u\n", info->dwLineID);
3722     return MIXERR_INVALLINE;
3723 }
3724
3725 /**************************************************************************
3726  *                              mixerGetLineInfoW               [WINMM.@]
3727  */
3728 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
3729 {
3730     UINT mmdev_index;
3731     WINMM_MMDevice *mmdevice;
3732
3733     TRACE("(%p, %p, %x)\n", hmix, lpmliW, fdwInfo);
3734
3735     if(!WINMM_StartDevicesThread())
3736         return MMSYSERR_ERROR;
3737
3738     if(!lpmliW || lpmliW->cbStruct < sizeof(MIXERLINEW))
3739         return MMSYSERR_INVALPARAM;
3740
3741     TRACE("dwDestination: %u\n", lpmliW->dwDestination);
3742     TRACE("dwSource: %u\n", lpmliW->dwSource);
3743     TRACE("dwLineID: %u\n", lpmliW->dwLineID);
3744     TRACE("fdwLine: 0x%x\n", lpmliW->fdwLine);
3745     TRACE("dwComponentType: 0x%x\n", lpmliW->dwComponentType);
3746
3747     if(fdwInfo & ~(MIXER_GETLINEINFOF_COMPONENTTYPE |
3748                 MIXER_GETLINEINFOF_DESTINATION |
3749                 MIXER_GETLINEINFOF_LINEID |
3750                 MIXER_GETLINEINFOF_SOURCE |
3751                 MIXER_GETLINEINFOF_TARGETTYPE |
3752                 MIXER_OBJECTF_HMIXER |
3753                 MIXER_OBJECTF_MIXER)){
3754         WARN("Unknown GetLineInfo flag: %x\n", fdwInfo);
3755         return MMSYSERR_INVALFLAG;
3756     }
3757
3758     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwInfo, &mmdev_index);
3759     if(!mmdevice)
3760         return MMSYSERR_INVALHANDLE;
3761
3762     switch(fdwInfo & MIXER_GETLINEINFOF_QUERYMASK){
3763     case MIXER_GETLINEINFOF_DESTINATION:
3764         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, lpmliW,
3765                 fdwInfo);
3766     case MIXER_GETLINEINFOF_SOURCE:
3767         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3768     case MIXER_GETLINEINFOF_COMPONENTTYPE:
3769         return WINMM_GetComponentTypeLineInfo(mmdevice, mmdev_index, lpmliW,
3770                 fdwInfo);
3771     case MIXER_GETLINEINFOF_LINEID:
3772         return WINMM_GetLineIDLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3773     case MIXER_GETLINEINFOF_TARGETTYPE:
3774         FIXME("TARGETTYPE flag not implemented!\n");
3775         return MIXERR_INVALLINE;
3776     }
3777
3778     TRACE("Returning INVALFLAG on these flags: %lx\n",
3779             fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
3780     return MMSYSERR_INVALFLAG;
3781 }
3782
3783 /**************************************************************************
3784  *                              mixerGetLineInfoA               [WINMM.@]
3785  */
3786 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
3787                               DWORD fdwInfo)
3788 {
3789     MIXERLINEW          mliW;
3790     UINT                ret;
3791
3792     TRACE("(%p, %p, %x)\n", hmix, lpmliA, fdwInfo);
3793
3794     if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
3795         return MMSYSERR_INVALPARAM;
3796
3797     mliW.cbStruct = sizeof(mliW);
3798     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
3799     case MIXER_GETLINEINFOF_COMPONENTTYPE:
3800         mliW.dwComponentType = lpmliA->dwComponentType;
3801         break;
3802     case MIXER_GETLINEINFOF_DESTINATION:
3803         mliW.dwDestination = lpmliA->dwDestination;
3804         break;
3805     case MIXER_GETLINEINFOF_LINEID:
3806         mliW.dwLineID = lpmliA->dwLineID;
3807         break;
3808     case MIXER_GETLINEINFOF_SOURCE:
3809         mliW.dwDestination = lpmliA->dwDestination;
3810         mliW.dwSource = lpmliA->dwSource;
3811         break;
3812     case MIXER_GETLINEINFOF_TARGETTYPE:
3813         mliW.Target.dwType = lpmliA->Target.dwType;
3814         mliW.Target.wMid = lpmliA->Target.wMid;
3815         mliW.Target.wPid = lpmliA->Target.wPid;
3816         mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
3817         MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname)/sizeof(WCHAR));
3818         break;
3819     default:
3820         WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
3821         return MMSYSERR_INVALFLAG;
3822     }
3823
3824     ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
3825
3826     if(ret == MMSYSERR_NOERROR)
3827     {
3828         lpmliA->dwDestination = mliW.dwDestination;
3829         lpmliA->dwSource = mliW.dwSource;
3830         lpmliA->dwLineID = mliW.dwLineID;
3831         lpmliA->fdwLine = mliW.fdwLine;
3832         lpmliA->dwUser = mliW.dwUser;
3833         lpmliA->dwComponentType = mliW.dwComponentType;
3834         lpmliA->cChannels = mliW.cChannels;
3835         lpmliA->cConnections = mliW.cConnections;
3836         lpmliA->cControls = mliW.cControls;
3837         WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
3838                              sizeof(lpmliA->szShortName), NULL, NULL);
3839         WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
3840                              sizeof(lpmliA->szName), NULL, NULL );
3841         lpmliA->Target.dwType = mliW.Target.dwType;
3842         lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
3843         lpmliA->Target.wMid = mliW.Target.wMid;
3844         lpmliA->Target.wPid = mliW.Target.wPid;
3845         lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
3846         WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
3847                              sizeof(lpmliA->Target.szPname), NULL, NULL );
3848     }
3849     return ret;
3850 }
3851
3852 /**************************************************************************
3853  *                              mixerSetControlDetails  [WINMM.@]
3854  */
3855 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
3856                                    DWORD fdwDetails)
3857 {
3858     WINMM_ControlDetails details;
3859
3860     TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
3861
3862     if(!WINMM_StartDevicesThread())
3863         return MMSYSERR_ERROR;
3864
3865     if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
3866             MIXER_SETCONTROLDETAILSF_CUSTOM)
3867         return MMSYSERR_NOTSUPPORTED;
3868
3869     if(!lpmcd)
3870         return MMSYSERR_INVALPARAM;
3871
3872     TRACE("dwControlID: %u\n", lpmcd->dwControlID);
3873
3874     details.hmix = hmix;
3875     details.details = lpmcd;
3876     details.flags = fdwDetails;
3877
3878     return SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
3879             (DWORD_PTR)&details, 0);
3880 }
3881
3882 /**************************************************************************
3883  *                              mixerMessage            [WINMM.@]
3884  */
3885 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3886 {
3887     TRACE("(%p, %d, %lx, %lx)\n", hmix, uMsg, dwParam1, dwParam2);
3888
3889     return MMSYSERR_NOTSUPPORTED;
3890 }