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