d3d10core: Fixup HRESULT in a bunch of error cases.
[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(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 = 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);
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);
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);
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), (WCHAR*)dwParam1, dwParam2, TRUE);
2827     /* TODO: Remove after dsound has been rewritten for mmdevapi */
2828     case DRV_QUERYDSOUNDDESC:
2829     case DRV_QUERYDSOUNDIFACE:
2830         if(dwParam2 == DS_HW_ACCEL_FULL)
2831             return WINMM_DRVMessage(HandleToULong(hWaveOut), uMessage,
2832                     dwParam1, 0, TRUE);
2833         return WINMM_FillDSDriverDesc(HandleToULong(hWaveOut),
2834                 (DSDRIVERDESC*)dwParam1, TRUE);
2835     }
2836
2837     return MMSYSERR_NOTSUPPORTED;
2838 }
2839
2840 /**************************************************************************
2841  *                              waveInGetNumDevs                [WINMM.@]
2842  */
2843 UINT WINAPI waveInGetNumDevs(void)
2844 {
2845     if(!WINMM_StartDevicesThread())
2846         return 0;
2847
2848     TRACE("count: %u\n", g_inmmdevices_count);
2849
2850     return g_inmmdevices_count;
2851 }
2852
2853 /**************************************************************************
2854  *                              waveInGetDevCapsW               [WINMM.@]
2855  */
2856 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2857 {
2858     WAVEINCAPSW mapper_caps, *caps;
2859
2860     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2861
2862     if(!WINMM_StartDevicesThread())
2863         return MMSYSERR_ERROR;
2864
2865     if(!lpCaps)
2866         return MMSYSERR_INVALPARAM;
2867
2868     if(WINMM_IsMapper(uDeviceID)){
2869         /* FIXME: Should be localized */
2870         static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2871             'n','d',' ','M','a','p','p','e','r',0};
2872
2873         mapper_caps.wMid = 0xFF;
2874         mapper_caps.wPid = 0xFF;
2875         mapper_caps.vDriverVersion = 0x00010001;
2876         mapper_caps.dwFormats = 0xFFFFFFFF;
2877         mapper_caps.wReserved1 = 0;
2878         mapper_caps.wChannels = 2;
2879         lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2880
2881         caps = &mapper_caps;
2882     }else{
2883         if(uDeviceID >= g_inmmdevices_count)
2884             return MMSYSERR_BADDEVICEID;
2885
2886         caps = &g_in_mmdevices[uDeviceID].in_caps;
2887     }
2888
2889     memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2890
2891     return MMSYSERR_NOERROR;
2892 }
2893
2894 /**************************************************************************
2895  *                              waveInGetDevCapsA               [WINMM.@]
2896  */
2897 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2898 {
2899     UINT ret;
2900     WAVEINCAPSW wicW;
2901
2902     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2903
2904     if(!WINMM_StartDevicesThread())
2905         return MMSYSERR_ERROR;
2906
2907     if(!lpCaps)
2908         return MMSYSERR_INVALPARAM;
2909
2910     ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2911
2912     if (ret == MMSYSERR_NOERROR) {
2913         WAVEINCAPSA wicA;
2914         wicA.wMid           = wicW.wMid;
2915         wicA.wPid           = wicW.wPid;
2916         wicA.vDriverVersion = wicW.vDriverVersion;
2917         WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2918                              sizeof(wicA.szPname), NULL, NULL );
2919         wicA.dwFormats      = wicW.dwFormats;
2920         wicA.wChannels      = wicW.wChannels;
2921         memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2922     }
2923     return ret;
2924 }
2925
2926 /**************************************************************************
2927  *                              waveInOpen                      [WINMM.@]
2928  */
2929 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2930                            LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2931                            DWORD_PTR dwInstance, DWORD dwFlags)
2932 {
2933     LRESULT res;
2934     WINMM_OpenInfo info;
2935     WINMM_CBInfo cb_info;
2936
2937     TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
2938             dwCallback, dwInstance, dwFlags);
2939
2940     if(!WINMM_StartDevicesThread())
2941         return MMSYSERR_ERROR;
2942
2943     if(!lphWaveIn && !(dwFlags & WAVE_FORMAT_QUERY))
2944         return MMSYSERR_INVALPARAM;
2945
2946     res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2947     if(res != MMSYSERR_NOERROR)
2948         return res;
2949
2950     info.format = (WAVEFORMATEX*)lpFormat;
2951     info.callback = dwCallback;
2952     info.cb_user = dwInstance;
2953     info.req_device = uDeviceID;
2954     info.flags = dwFlags;
2955
2956     res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
2957     if(res != MMSYSERR_NOERROR)
2958         return res;
2959
2960     if(lphWaveIn)
2961         *lphWaveIn = (HWAVEIN)info.handle;
2962
2963     cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2964     cb_info.callback = dwCallback;
2965     cb_info.user = dwInstance;
2966     cb_info.hwave = info.handle;
2967
2968     WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
2969
2970     return res;
2971 }
2972
2973 /**************************************************************************
2974  *                              waveInClose                     [WINMM.@]
2975  */
2976 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2977 {
2978     WINMM_Device *device;
2979     WINMM_CBInfo cb_info;
2980     UINT res;
2981
2982     TRACE("(%p)\n", hWaveIn);
2983
2984     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
2985
2986     if(!WINMM_ValidateAndLock(device))
2987         return MMSYSERR_INVALHANDLE;
2988
2989     cb_info = device->cb_info;
2990
2991     LeaveCriticalSection(&device->lock);
2992
2993     res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
2994
2995     if(res == MMSYSERR_NOERROR)
2996         WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
2997
2998     return res;
2999 }
3000
3001 /**************************************************************************
3002  *                              waveInPrepareHeader             [WINMM.@]
3003  */
3004 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
3005                                 UINT uSize)
3006 {
3007     TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
3008
3009     if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
3010         return MMSYSERR_INVALPARAM;
3011
3012     if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
3013         return WAVERR_STILLPLAYING;
3014
3015     return WINMM_PrepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
3016 }
3017
3018 /**************************************************************************
3019  *                              waveInUnprepareHeader   [WINMM.@]
3020  */
3021 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
3022                                   UINT uSize)
3023 {
3024     TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
3025
3026     if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
3027         return MMSYSERR_INVALPARAM;
3028
3029     if(!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
3030         return MMSYSERR_NOERROR;
3031
3032     if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
3033         return WAVERR_STILLPLAYING;
3034
3035     return WINMM_UnprepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
3036 }
3037
3038 /**************************************************************************
3039  *                              waveInAddBuffer         [WINMM.@]
3040  */
3041 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn, WAVEHDR *header, UINT uSize)
3042 {
3043     WINMM_Device *device;
3044
3045     TRACE("(%p, %p, %u)\n", hWaveIn, header, uSize);
3046
3047     if(!header || uSize < sizeof(WAVEHDR))
3048         return MMSYSERR_INVALPARAM;
3049
3050     if(!(header->dwFlags & WHDR_PREPARED))
3051         return WAVERR_UNPREPARED;
3052
3053     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3054
3055     if(!WINMM_ValidateAndLock(device))
3056         return MMSYSERR_INVALHANDLE;
3057
3058     if(!device->first)
3059         device->first = device->last = header;
3060     else{
3061         device->last->lpNext = header;
3062         device->last = header;
3063     }
3064
3065     header->dwBytesRecorded = 0;
3066     header->lpNext = NULL;
3067     header->dwFlags &= ~WHDR_DONE;
3068     header->dwFlags |= WHDR_INQUEUE;
3069
3070     LeaveCriticalSection(&device->lock);
3071
3072     return MMSYSERR_NOERROR;
3073 }
3074
3075 /**************************************************************************
3076  *                              waveInReset             [WINMM.@]
3077  */
3078 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
3079 {
3080     TRACE("(%p)\n", hWaveIn);
3081
3082     return WINMM_Reset((HWAVE)hWaveIn);
3083 }
3084
3085 /**************************************************************************
3086  *                              waveInStart             [WINMM.@]
3087  */
3088 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
3089 {
3090     WINMM_Device *device;
3091     HRESULT hr;
3092
3093     TRACE("(%p)\n", hWaveIn);
3094
3095     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3096
3097     if(!WINMM_ValidateAndLock(device))
3098         return MMSYSERR_INVALHANDLE;
3099
3100     hr = WINMM_BeginPlaying(device);
3101     if(FAILED(hr)){
3102         LeaveCriticalSection(&device->lock);
3103         return MMSYSERR_ERROR;
3104     }
3105
3106     LeaveCriticalSection(&device->lock);
3107
3108     return MMSYSERR_NOERROR;
3109 }
3110
3111 /**************************************************************************
3112  *                              waveInStop              [WINMM.@]
3113  */
3114 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
3115 {
3116     WINMM_CBInfo cb_info;
3117     WINMM_Device *device;
3118     WAVEHDR *buf;
3119     HRESULT hr;
3120
3121     TRACE("(%p)\n", hWaveIn);
3122
3123     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3124
3125     if(!WINMM_ValidateAndLock(device))
3126         return MMSYSERR_INVALHANDLE;
3127
3128     hr = WINMM_Pause((HWAVE)hWaveIn);
3129     if(FAILED(hr)){
3130         LeaveCriticalSection(&device->lock);
3131         return MMSYSERR_ERROR;
3132     }
3133     device->stopped = TRUE;
3134
3135     buf = device->first;
3136     if(buf && buf->dwBytesRecorded > 0){
3137         device->first = buf->lpNext;
3138         buf->dwFlags &= ~WHDR_INQUEUE;
3139         buf->dwFlags |= WHDR_DONE;
3140     }else
3141         buf = NULL;
3142
3143     cb_info = device->cb_info;
3144
3145     LeaveCriticalSection(&device->lock);
3146
3147     if(buf)
3148         WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
3149
3150     return MMSYSERR_NOERROR;
3151 }
3152
3153 /**************************************************************************
3154  *                              waveInGetPosition       [WINMM.@]
3155  */
3156 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
3157                               UINT uSize)
3158 {
3159     TRACE("(%p, %p, %u)\n", hWaveIn, lpTime, uSize);
3160
3161     if(!uSize || !lpTime || uSize != sizeof(MMTIME))
3162         return MMSYSERR_INVALPARAM;
3163
3164     return WINMM_GetPosition((HWAVE)hWaveIn, lpTime);
3165 }
3166
3167 /**************************************************************************
3168  *                              waveInGetID                     [WINMM.@]
3169  */
3170 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
3171 {
3172     UINT dev, junk;
3173     BOOL is_out;
3174     WINMM_Device *device;
3175
3176     TRACE("(%p, %p)\n", hWaveIn, lpuDeviceID);
3177
3178     if(!lpuDeviceID)
3179         return MMSYSERR_INVALPARAM;
3180
3181     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3182     if(!WINMM_ValidateAndLock(device))
3183         return MMSYSERR_INVALHANDLE;
3184
3185     LeaveCriticalSection(&device->lock);
3186
3187     WINMM_DecomposeHWAVE((HWAVE)hWaveIn, lpuDeviceID, &is_out, &dev, &junk);
3188
3189     return MMSYSERR_NOERROR;
3190 }
3191
3192 /**************************************************************************
3193  *                              waveInMessage           [WINMM.@]
3194  */
3195 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
3196                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3197 {
3198     TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
3199
3200     switch(uMessage){
3201     case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
3202         return WINMM_QueryInstanceIDSize(HandleToULong(hWaveIn),
3203                 (DWORD_PTR*)dwParam1, FALSE);
3204     case DRV_QUERYFUNCTIONINSTANCEID:
3205         return WINMM_QueryInstanceID(HandleToULong(hWaveIn), (WCHAR*)dwParam1, dwParam2, FALSE);
3206     /* TODO: Remove after dsound has been rewritten for mmdevapi */
3207     case DRV_QUERYDSOUNDDESC:
3208     case DRV_QUERYDSOUNDIFACE:
3209         if(dwParam2 == DS_HW_ACCEL_FULL)
3210             return WINMM_DRVMessage(HandleToULong(hWaveIn), uMessage,
3211                     dwParam1, 0, FALSE);
3212         return WINMM_FillDSDriverDesc(HandleToULong(hWaveIn),
3213                 (DSDRIVERDESC*)dwParam1, FALSE);
3214     }
3215
3216     return MMSYSERR_NOTSUPPORTED;
3217 }
3218
3219 UINT WINAPI mixerGetNumDevs(void)
3220 {
3221     TRACE("\n");
3222
3223     if(!WINMM_StartDevicesThread())
3224         return 0;
3225
3226     return g_outmmdevices_count + g_inmmdevices_count;
3227 }
3228
3229 /**************************************************************************
3230  *                              mixerGetDevCapsA                [WINMM.@]
3231  */
3232 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
3233 {
3234     MIXERCAPSW micW;
3235     UINT       ret;
3236
3237     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3238
3239     if(!lpCaps)
3240         return MMSYSERR_INVALPARAM;
3241
3242     ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
3243
3244     if (ret == MMSYSERR_NOERROR) {
3245         MIXERCAPSA micA;
3246         micA.wMid           = micW.wMid;
3247         micA.wPid           = micW.wPid;
3248         micA.vDriverVersion = micW.vDriverVersion;
3249         WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
3250                              sizeof(micA.szPname), NULL, NULL );
3251         micA.fdwSupport     = micW.fdwSupport;
3252         micA.cDestinations  = micW.cDestinations;
3253         memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
3254     }
3255     return ret;
3256 }
3257
3258 /**************************************************************************
3259  *                              mixerGetDevCapsW                [WINMM.@]
3260  */
3261 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
3262 {
3263     WINMM_MMDevice *mmdevice;
3264     MIXERCAPSW caps;
3265
3266     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3267
3268     if(!WINMM_StartDevicesThread())
3269         return MMSYSERR_ERROR;
3270
3271     if(!lpCaps)
3272         return MMSYSERR_INVALPARAM;
3273
3274     if(!uSize)
3275         return MMSYSERR_NOERROR;
3276
3277     if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3278         return MMSYSERR_BADDEVICEID;
3279
3280     if(uDeviceID < g_outmmdevices_count){
3281         mmdevice = &g_out_mmdevices[uDeviceID];
3282         memcpy(caps.szPname, mmdevice->out_caps.szPname, sizeof(caps.szPname));
3283     }else{
3284         mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3285         memcpy(caps.szPname, mmdevice->in_caps.szPname, sizeof(caps.szPname));
3286     }
3287
3288     caps.wMid = 0xFF;
3289     caps.wPid = 0xFF;
3290     caps.vDriverVersion = 0x00010001;
3291     caps.fdwSupport = 0;
3292     caps.cDestinations = 1;
3293
3294     memcpy(lpCaps, &caps, uSize);
3295
3296     return MMSYSERR_NOERROR;
3297 }
3298
3299 /**************************************************************************
3300  *                              mixerOpen                       [WINMM.@]
3301  */
3302 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
3303                       DWORD_PTR dwInstance, DWORD fdwOpen)
3304 {
3305     WINMM_MMDevice *mmdevice;
3306     MMRESULT mr;
3307
3308     TRACE("(%p, %d, %lx, %lx, %x)\n", lphMix, uDeviceID, dwCallback,
3309             dwInstance, fdwOpen);
3310
3311     if(!WINMM_StartDevicesThread())
3312         return MMSYSERR_ERROR;
3313
3314     if(!lphMix)
3315         return MMSYSERR_INVALPARAM;
3316
3317     mr = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
3318     if(mr != MMSYSERR_NOERROR)
3319         return mr;
3320
3321     if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3322         return MMSYSERR_BADDEVICEID;
3323
3324     if(uDeviceID < g_outmmdevices_count){
3325         mmdevice = &g_out_mmdevices[uDeviceID];
3326         *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID, TRUE,
3327                 mmdevice->mixer_count);
3328     }else{
3329         mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3330         *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID - g_outmmdevices_count,
3331                 FALSE, mmdevice->mixer_count);
3332     }
3333
3334     ++mmdevice->mixer_count;
3335
3336     return MMSYSERR_NOERROR;
3337 }
3338
3339 /**************************************************************************
3340  *                              mixerClose                      [WINMM.@]
3341  */
3342 UINT WINAPI mixerClose(HMIXER hMix)
3343 {
3344     TRACE("(%p)\n", hMix);
3345
3346     return MMSYSERR_NOERROR;
3347 }
3348
3349 /**************************************************************************
3350  *                              mixerGetID                      [WINMM.@]
3351  */
3352 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
3353 {
3354     WINMM_MMDevice *mmdevice;
3355
3356     TRACE("(%p, %p, %x)\n", hmix, lpid, fdwID);
3357
3358     if(!WINMM_StartDevicesThread())
3359         return MMSYSERR_ERROR;
3360
3361     if(!lpid)
3362         return MMSYSERR_INVALPARAM;
3363
3364     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwID, lpid);
3365     if(!mmdevice)
3366         return MMSYSERR_INVALHANDLE;
3367
3368     if(mmdevice->in_caps.szPname[0] != '\0')
3369         *lpid += g_outmmdevices_count;
3370
3371     return MMSYSERR_NOERROR;
3372 }
3373
3374 /**************************************************************************
3375  *                              mixerGetControlDetailsW         [WINMM.@]
3376  */
3377 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
3378                                     DWORD fdwDetails)
3379 {
3380     WINMM_ControlDetails details;
3381
3382     TRACE("(%p, %p, %x)\n", hmix, lpmcdW, fdwDetails);
3383
3384     if(!WINMM_StartDevicesThread())
3385         return MMSYSERR_ERROR;
3386
3387     if(!lpmcdW)
3388         return MMSYSERR_INVALPARAM;
3389
3390     TRACE("dwControlID: %u\n", lpmcdW->dwControlID);
3391
3392     details.hmix = hmix;
3393     details.details = lpmcdW;
3394     details.flags = fdwDetails;
3395
3396     return SendMessageW(g_devices_hwnd, MXDM_GETCONTROLDETAILS,
3397             (DWORD_PTR)&details, 0);
3398 }
3399
3400 /**************************************************************************
3401  *                              mixerGetControlDetailsA [WINMM.@]
3402  */
3403 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
3404                                     DWORD fdwDetails)
3405 {
3406     UINT ret = MMSYSERR_NOTSUPPORTED;
3407
3408     TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
3409
3410     if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
3411         return MMSYSERR_INVALPARAM;
3412
3413     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
3414     case MIXER_GETCONTROLDETAILSF_VALUE:
3415         /* can safely use A structure as it is, no string inside */
3416         ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3417         break;
3418     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
3419         {
3420             MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = lpmcdA->paDetails;
3421             MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
3422             int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3423             unsigned int i;
3424
3425             if (lpmcdA->u.cMultipleItems != 0) {
3426                 size *= lpmcdA->u.cMultipleItems;
3427             }
3428             pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
3429             lpmcdA->paDetails = pDetailsW;
3430             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3431             /* set up lpmcd->paDetails */
3432             ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3433             /* copy from lpmcd->paDetails back to paDetailsW; */
3434             if (ret == MMSYSERR_NOERROR) {
3435                 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
3436                     pDetailsA->dwParam1 = pDetailsW->dwParam1;
3437                     pDetailsA->dwParam2 = pDetailsW->dwParam2;
3438                     WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
3439                                          pDetailsA->szName,
3440                                          sizeof(pDetailsA->szName), NULL, NULL );
3441                     pDetailsA++;
3442                     pDetailsW++;
3443                 }
3444                 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3445                 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3446             }
3447             HeapFree(GetProcessHeap(), 0, pDetailsW);
3448             lpmcdA->paDetails = pDetailsA;
3449             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
3450         }
3451         break;
3452     default:
3453         ERR("Unsupported fdwDetails=0x%08x\n", fdwDetails);
3454     }
3455
3456     return ret;
3457 }
3458
3459 /**************************************************************************
3460  *                              mixerGetLineControlsA   [WINMM.@]
3461  */
3462 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
3463                                   DWORD fdwControls)
3464 {
3465     MIXERLINECONTROLSW  mlcW;
3466     DWORD               ret;
3467     unsigned int        i;
3468
3469     TRACE("(%p, %p, %x)\n", hmix, lpmlcA, fdwControls);
3470
3471     if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
3472         lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
3473         return MMSYSERR_INVALPARAM;
3474
3475     mlcW.cbStruct = sizeof(mlcW);
3476     mlcW.dwLineID = lpmlcA->dwLineID;
3477     mlcW.u.dwControlID = lpmlcA->u.dwControlID;
3478     mlcW.u.dwControlType = lpmlcA->u.dwControlType;
3479
3480     /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
3481        the control count is assumed to be 1 - This is relied upon by a game,
3482        "Dynomite Deluze"                                                    */
3483     if (MIXER_GETLINECONTROLSF_ONEBYTYPE == (fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK)) {
3484         mlcW.cControls = 1;
3485     } else {
3486         mlcW.cControls = lpmlcA->cControls;
3487     }
3488     mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
3489     mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
3490                               mlcW.cControls * mlcW.cbmxctrl);
3491
3492     ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
3493
3494     if (ret == MMSYSERR_NOERROR) {
3495         lpmlcA->dwLineID = mlcW.dwLineID;
3496         lpmlcA->u.dwControlID = mlcW.u.dwControlID;
3497         lpmlcA->u.dwControlType = mlcW.u.dwControlType;
3498
3499         for (i = 0; i < mlcW.cControls; i++) {
3500             lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
3501             lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
3502             lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
3503             lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
3504             lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
3505             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
3506                                  lpmlcA->pamxctrl[i].szShortName,
3507                                  sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
3508             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
3509                                  lpmlcA->pamxctrl[i].szName,
3510                                  sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
3511             /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
3512              * sizeof(mlcW.pamxctrl[i].Bounds) */
3513             memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
3514                    sizeof(mlcW.pamxctrl[i].Bounds));
3515             /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
3516              * sizeof(mlcW.pamxctrl[i].Metrics) */
3517             memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
3518                    sizeof(mlcW.pamxctrl[i].Metrics));
3519         }
3520     }
3521
3522     HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
3523
3524     return ret;
3525 }
3526
3527 static UINT WINMM_GetVolumeLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3528         MIXERCONTROLW *ctl, DWORD flags)
3529 {
3530     ctl->dwControlID = (line == 0xFFFF0000) ? 0 : 2;
3531     ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
3532     ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3533     ctl->cMultipleItems = 0;
3534     lstrcpyW(ctl->szShortName, volumeW);
3535     lstrcpyW(ctl->szName, volumeW);
3536     ctl->Bounds.s1.dwMinimum = 0;
3537     ctl->Bounds.s1.dwMaximum = 0xFFFF;
3538     ctl->Metrics.cSteps = 192;
3539
3540     return MMSYSERR_NOERROR;
3541 }
3542
3543 static UINT WINMM_GetMuteLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3544         MIXERCONTROLW *ctl, DWORD flags)
3545 {
3546     ctl->dwControlID = (line == 0xFFFF0000) ? 1 : 3;
3547     ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
3548     ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3549     ctl->cMultipleItems = 0;
3550     lstrcpyW(ctl->szShortName, muteW);
3551     lstrcpyW(ctl->szName, muteW);
3552     ctl->Bounds.s1.dwMinimum = 0;
3553     ctl->Bounds.s1.dwMaximum = 1;
3554     ctl->Metrics.cSteps = 0;
3555
3556     return MMSYSERR_NOERROR;
3557 }
3558
3559 /**************************************************************************
3560  *                              mixerGetLineControlsW           [WINMM.@]
3561  */
3562 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
3563                                   DWORD fdwControls)
3564 {
3565     WINMM_MMDevice *mmdevice;
3566
3567     TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
3568
3569     if(!WINMM_StartDevicesThread())
3570         return MMSYSERR_ERROR;
3571
3572     if(fdwControls & ~(MIXER_GETLINECONTROLSF_ALL |
3573                 MIXER_GETLINECONTROLSF_ONEBYID |
3574                 MIXER_GETLINECONTROLSF_ONEBYTYPE |
3575                 MIXER_OBJECTF_HMIXER |
3576                 MIXER_OBJECTF_MIXER)){
3577         WARN("Unknown GetLineControls flag: %x\n", fdwControls);
3578         return MMSYSERR_INVALFLAG;
3579     }
3580
3581     if(!lpmlcW || lpmlcW->cbStruct < sizeof(*lpmlcW) || !lpmlcW->pamxctrl)
3582         return MMSYSERR_INVALPARAM;
3583
3584     TRACE("dwLineID: %u\n", lpmlcW->dwLineID);
3585     TRACE("dwControl: %x\n", lpmlcW->u.dwControlID);
3586     TRACE("cControls: %u\n", lpmlcW->cControls);
3587
3588     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwControls, NULL);
3589     if(!mmdevice)
3590         return MMSYSERR_INVALHANDLE;
3591
3592     switch(fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK){
3593     case MIXER_GETLINECONTROLSF_ALL:
3594         if(lpmlcW->cControls != 2)
3595             return MMSYSERR_INVALPARAM;
3596         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3597             return MMSYSERR_INVALPARAM;
3598         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3599             return MIXERR_INVALLINE;
3600         WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3601                 &lpmlcW->pamxctrl[0], fdwControls);
3602         WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3603                 &lpmlcW->pamxctrl[1], fdwControls);
3604         return MMSYSERR_NOERROR;
3605     case MIXER_GETLINECONTROLSF_ONEBYID:
3606         if(lpmlcW->cControls != 1)
3607             return MMSYSERR_INVALPARAM;
3608         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3609             return MMSYSERR_INVALPARAM;
3610         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3611             return MIXERR_INVALLINE;
3612         if(lpmlcW->u.dwControlID == 0)
3613             return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3614                     lpmlcW->pamxctrl, fdwControls);
3615         if(lpmlcW->u.dwControlID == 1)
3616             return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3617                     lpmlcW->pamxctrl, fdwControls);
3618         return MMSYSERR_NOTSUPPORTED;
3619     case MIXER_GETLINECONTROLSF_ONEBYTYPE:
3620         if(lpmlcW->cControls != 1)
3621             return MMSYSERR_INVALPARAM;
3622         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3623             return MMSYSERR_INVALPARAM;
3624         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3625             return MIXERR_INVALLINE;
3626         if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
3627             return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3628                     lpmlcW->pamxctrl, fdwControls);
3629         if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
3630             return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3631                     lpmlcW->pamxctrl, fdwControls);
3632         return MMSYSERR_NOTSUPPORTED;
3633     }
3634
3635     return MMSYSERR_NOTSUPPORTED;
3636 }
3637
3638 static UINT WINMM_GetSourceLineInfo(WINMM_MMDevice *mmdevice, UINT mmdev_index,
3639         MIXERLINEW *info, DWORD flags)
3640 {
3641     BOOL is_out = TRUE;
3642     if(mmdevice->in_caps.szPname[0] != '\0')
3643         is_out = FALSE;
3644
3645     if(info->dwSource != 0)
3646         return MIXERR_INVALLINE;
3647
3648     info->dwDestination = 0;
3649     info->dwLineID = 0;
3650     info->fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
3651     info->cConnections = 0;
3652     info->cControls = 2;
3653     /* volume & mute always affect all channels, so claim 1 channel */
3654     info->cChannels = 1;
3655     info->Target.dwDeviceID = mmdev_index;
3656     info->Target.wMid = ~0;
3657     info->Target.wPid = ~0;
3658     info->Target.vDriverVersion = 0;
3659
3660     lstrcpyW(info->szShortName, volumeW);
3661     lstrcpyW(info->szName, mastervolumeW);
3662
3663     if(is_out){
3664         info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
3665         info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
3666         memcpy(info->Target.szPname, mmdevice->out_caps.szPname,
3667                 sizeof(info->Target.szPname));
3668     }else{
3669         info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
3670         info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3671         info->Target.szPname[0] = '\0';
3672     }
3673
3674     return MMSYSERR_NOERROR;
3675 }
3676
3677 static UINT WINMM_GetDestinationLineInfo(WINMM_MMDevice *mmdevice,
3678         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3679 {
3680     BOOL is_out = TRUE;
3681     if(mmdevice->in_caps.szPname[0] != '\0')
3682         is_out = FALSE;
3683
3684     if(info->dwDestination != 0)
3685         return MIXERR_INVALLINE;
3686
3687     info->dwSource = 0xFFFFFFFF;
3688     info->dwLineID = 0xFFFF0000;
3689     info->fdwLine = MIXERLINE_LINEF_ACTIVE;
3690     info->cConnections = 1;
3691     info->cControls = 2;
3692
3693     lstrcpyW(info->szShortName, volumeW);
3694     lstrcpyW(info->szName, mastervolumeW);
3695
3696     info->Target.dwDeviceID = mmdev_index;
3697     info->Target.wMid = ~0;
3698     info->Target.wPid = ~0;
3699     info->Target.vDriverVersion = 0;
3700
3701     if(is_out){
3702         info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
3703         info->cChannels = mmdevice->out_caps.wChannels;
3704         info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3705         info->Target.szPname[0] = '\0';
3706     }else{
3707         info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
3708         info->cChannels = mmdevice->in_caps.wChannels;
3709         info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
3710         memcpy(info->Target.szPname, mmdevice->in_caps.szPname,
3711                 sizeof(info->Target.szPname));
3712     }
3713
3714     return MMSYSERR_NOERROR;
3715 }
3716
3717 static UINT WINMM_GetComponentTypeLineInfo(WINMM_MMDevice *mmdevice,
3718         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3719 {
3720     BOOL is_out = TRUE;
3721     if(mmdevice->in_caps.szPname[0] != '\0')
3722         is_out = FALSE;
3723
3724     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN){
3725         if(is_out)
3726             return MIXERR_INVALLINE;
3727         info->dwDestination = 0;
3728         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3729     }
3730
3731     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS){
3732         if(!is_out)
3733             return MIXERR_INVALLINE;
3734         info->dwDestination = 0;
3735         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3736     }
3737
3738     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE){
3739         if(is_out)
3740             return MIXERR_INVALLINE;
3741         info->dwSource = 0;
3742         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3743     }
3744
3745     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
3746         if(!is_out)
3747             return MIXERR_INVALLINE;
3748         info->dwSource = 0;
3749         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3750     }
3751
3752     TRACE("Returning INVALLINE on this component type: %u\n",
3753             info->dwComponentType);
3754
3755     return MIXERR_INVALLINE;
3756 }
3757
3758 static UINT WINMM_GetLineIDLineInfo(WINMM_MMDevice *mmdevice,
3759         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3760 {
3761     if(info->dwLineID == 0xFFFF0000){
3762         info->dwDestination = 0;
3763         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3764     }
3765
3766     if(info->dwLineID == 0){
3767         info->dwSource = 0;
3768         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3769     }
3770
3771     TRACE("Returning INVALLINE on this dwLineID: %u\n", info->dwLineID);
3772     return MIXERR_INVALLINE;
3773 }
3774
3775 /**************************************************************************
3776  *                              mixerGetLineInfoW               [WINMM.@]
3777  */
3778 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
3779 {
3780     UINT mmdev_index;
3781     WINMM_MMDevice *mmdevice;
3782
3783     TRACE("(%p, %p, %x)\n", hmix, lpmliW, fdwInfo);
3784
3785     if(!WINMM_StartDevicesThread())
3786         return MMSYSERR_ERROR;
3787
3788     if(!lpmliW || lpmliW->cbStruct < sizeof(MIXERLINEW))
3789         return MMSYSERR_INVALPARAM;
3790
3791     TRACE("dwDestination: %u\n", lpmliW->dwDestination);
3792     TRACE("dwSource: %u\n", lpmliW->dwSource);
3793     TRACE("dwLineID: %u\n", lpmliW->dwLineID);
3794     TRACE("fdwLine: 0x%x\n", lpmliW->fdwLine);
3795     TRACE("dwComponentType: 0x%x\n", lpmliW->dwComponentType);
3796
3797     if(fdwInfo & ~(MIXER_GETLINEINFOF_COMPONENTTYPE |
3798                 MIXER_GETLINEINFOF_DESTINATION |
3799                 MIXER_GETLINEINFOF_LINEID |
3800                 MIXER_GETLINEINFOF_SOURCE |
3801                 MIXER_GETLINEINFOF_TARGETTYPE |
3802                 MIXER_OBJECTF_HMIXER |
3803                 MIXER_OBJECTF_MIXER)){
3804         WARN("Unknown GetLineInfo flag: %x\n", fdwInfo);
3805         return MMSYSERR_INVALFLAG;
3806     }
3807
3808     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwInfo, &mmdev_index);
3809     if(!mmdevice)
3810         return MMSYSERR_INVALHANDLE;
3811
3812     switch(fdwInfo & MIXER_GETLINEINFOF_QUERYMASK){
3813     case MIXER_GETLINEINFOF_DESTINATION:
3814         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, lpmliW,
3815                 fdwInfo);
3816     case MIXER_GETLINEINFOF_SOURCE:
3817         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3818     case MIXER_GETLINEINFOF_COMPONENTTYPE:
3819         return WINMM_GetComponentTypeLineInfo(mmdevice, mmdev_index, lpmliW,
3820                 fdwInfo);
3821     case MIXER_GETLINEINFOF_LINEID:
3822         return WINMM_GetLineIDLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3823     case MIXER_GETLINEINFOF_TARGETTYPE:
3824         FIXME("TARGETTYPE flag not implemented!\n");
3825         return MIXERR_INVALLINE;
3826     }
3827
3828     TRACE("Returning INVALFLAG on these flags: %lx\n",
3829             fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
3830     return MMSYSERR_INVALFLAG;
3831 }
3832
3833 /**************************************************************************
3834  *                              mixerGetLineInfoA               [WINMM.@]
3835  */
3836 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
3837                               DWORD fdwInfo)
3838 {
3839     MIXERLINEW          mliW;
3840     UINT                ret;
3841
3842     TRACE("(%p, %p, %x)\n", hmix, lpmliA, fdwInfo);
3843
3844     if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
3845         return MMSYSERR_INVALPARAM;
3846
3847     mliW.cbStruct = sizeof(mliW);
3848     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
3849     case MIXER_GETLINEINFOF_COMPONENTTYPE:
3850         mliW.dwComponentType = lpmliA->dwComponentType;
3851         break;
3852     case MIXER_GETLINEINFOF_DESTINATION:
3853         mliW.dwDestination = lpmliA->dwDestination;
3854         break;
3855     case MIXER_GETLINEINFOF_LINEID:
3856         mliW.dwLineID = lpmliA->dwLineID;
3857         break;
3858     case MIXER_GETLINEINFOF_SOURCE:
3859         mliW.dwDestination = lpmliA->dwDestination;
3860         mliW.dwSource = lpmliA->dwSource;
3861         break;
3862     case MIXER_GETLINEINFOF_TARGETTYPE:
3863         mliW.Target.dwType = lpmliA->Target.dwType;
3864         mliW.Target.wMid = lpmliA->Target.wMid;
3865         mliW.Target.wPid = lpmliA->Target.wPid;
3866         mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
3867         MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname)/sizeof(WCHAR));
3868         break;
3869     default:
3870         WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
3871         return MMSYSERR_INVALFLAG;
3872     }
3873
3874     ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
3875
3876     if(ret == MMSYSERR_NOERROR)
3877     {
3878         lpmliA->dwDestination = mliW.dwDestination;
3879         lpmliA->dwSource = mliW.dwSource;
3880         lpmliA->dwLineID = mliW.dwLineID;
3881         lpmliA->fdwLine = mliW.fdwLine;
3882         lpmliA->dwUser = mliW.dwUser;
3883         lpmliA->dwComponentType = mliW.dwComponentType;
3884         lpmliA->cChannels = mliW.cChannels;
3885         lpmliA->cConnections = mliW.cConnections;
3886         lpmliA->cControls = mliW.cControls;
3887         WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
3888                              sizeof(lpmliA->szShortName), NULL, NULL);
3889         WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
3890                              sizeof(lpmliA->szName), NULL, NULL );
3891         lpmliA->Target.dwType = mliW.Target.dwType;
3892         lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
3893         lpmliA->Target.wMid = mliW.Target.wMid;
3894         lpmliA->Target.wPid = mliW.Target.wPid;
3895         lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
3896         WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
3897                              sizeof(lpmliA->Target.szPname), NULL, NULL );
3898     }
3899     return ret;
3900 }
3901
3902 /**************************************************************************
3903  *                              mixerSetControlDetails  [WINMM.@]
3904  */
3905 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
3906                                    DWORD fdwDetails)
3907 {
3908     WINMM_ControlDetails details;
3909
3910     TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
3911
3912     if(!WINMM_StartDevicesThread())
3913         return MMSYSERR_ERROR;
3914
3915     if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
3916             MIXER_SETCONTROLDETAILSF_CUSTOM)
3917         return MMSYSERR_NOTSUPPORTED;
3918
3919     if(!lpmcd)
3920         return MMSYSERR_INVALPARAM;
3921
3922     TRACE("dwControlID: %u\n", lpmcd->dwControlID);
3923
3924     details.hmix = hmix;
3925     details.details = lpmcd;
3926     details.flags = fdwDetails;
3927
3928     return SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
3929             (DWORD_PTR)&details, 0);
3930 }
3931
3932 /**************************************************************************
3933  *                              mixerMessage            [WINMM.@]
3934  */
3935 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3936 {
3937     TRACE("(%p, %d, %lx, %lx)\n", hmix, uMsg, dwParam1, dwParam2);
3938
3939     return MMSYSERR_NOTSUPPORTED;
3940 }