winmm: Separate WINMM_Pause and WINMM_Reset.
[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         ofs = 0;
1334
1335         if(queue->dwFlags & WHDR_ENDLOOP && nloops < device->loop_counter){
1336             queue = device->loop_start;
1337             ++nloops;
1338         }else
1339             queue = queue->lpNext;
1340     }
1341
1342     if(avail_frames != 0 && queue_frames == 0){
1343         hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
1344         if(FAILED(hr)){
1345             ERR("GetBuffer failed: %08x\n", hr);
1346             goto exit;
1347         }
1348
1349         hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames,
1350                 AUDCLNT_BUFFERFLAGS_SILENT);
1351         if(FAILED(hr)){
1352             ERR("ReleaseBuffer failed: %08x\n", hr);
1353             goto exit;
1354         }
1355
1356         goto exit;
1357     }
1358
1359     if(queue_frames < avail_frames)
1360         avail_frames = queue_frames;
1361     if(avail_frames == 0)
1362         goto exit;
1363
1364     hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
1365     if(FAILED(hr)){
1366         ERR("GetBuffer failed: %08x\n", hr);
1367         goto exit;
1368     }
1369
1370     written = 0;
1371     while(device->playing && written < avail_frames){
1372         UINT32 copy_frames, copy_bytes;
1373         BYTE *queue_data;
1374
1375         queue = device->playing;
1376
1377         if(device->acm_handle){
1378             ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)queue->reserved;
1379             queue_bytes = ash->cbDstLengthUsed;
1380             queue_data = ash->pbDst;
1381         }else{
1382             queue_bytes = queue->dwBufferLength;
1383             queue_data = (BYTE*)queue->lpData;
1384         }
1385
1386         queue_frames = (queue_bytes - device->ofs_bytes) /
1387             device->bytes_per_frame;
1388
1389         copy_frames = queue_frames < (avail_frames - written) ?
1390             queue_frames : avail_frames - written;
1391         copy_bytes = copy_frames * device->bytes_per_frame;
1392
1393         memcpy(data, queue_data + device->ofs_bytes, copy_bytes);
1394
1395         data += copy_bytes;
1396         written += copy_frames;
1397         device->ofs_bytes += copy_bytes;
1398
1399         if(device->ofs_bytes >= queue_bytes){
1400             device->ofs_bytes = 0;
1401
1402             if(!(queue->dwFlags & (WHDR_BEGINLOOP | WHDR_ENDLOOP)))
1403                 device->playing = queue->lpNext;
1404             else{
1405                 if(queue->dwFlags & WHDR_BEGINLOOP){
1406                     device->loop_start = device->playing;
1407                     device->playing = queue->lpNext;
1408                     device->loop_counter = queue->dwLoops;
1409                 }
1410                 if(queue->dwFlags & WHDR_ENDLOOP){
1411                     --device->loop_counter;
1412                     if(device->loop_counter)
1413                         device->playing = device->loop_start;
1414                     else
1415                         device->loop_start = device->playing = queue->lpNext;
1416                 }
1417             }
1418         }
1419     }
1420
1421     hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames, 0);
1422     if(FAILED(hr)){
1423         ERR("ReleaseBuffer failed: %08x\n", hr);
1424         goto exit;
1425     }
1426
1427     device->played_frames += avail_frames;
1428
1429 exit:
1430     cb_info = device->cb_info;
1431
1432     LeaveCriticalSection(&device->lock);
1433
1434     while(first && (first->dwFlags & WHDR_DONE)){
1435         WAVEHDR *next = first->lpNext;
1436         WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1437         first = next;
1438     }
1439 }
1440
1441 static void WID_PullACMData(WINMM_Device *device)
1442 {
1443     UINT32 packet, packet_bytes;
1444     DWORD flags;
1445     BYTE *data;
1446     WAVEHDR *queue;
1447     HRESULT hr;
1448     MMRESULT mr;
1449
1450     if(device->acm_hdr.cbDstLength == 0){
1451         hr = IAudioClient_GetCurrentPadding(device->client, &packet);
1452         if(FAILED(hr)){
1453             ERR("GetCurrentPadding failed: %08x\n", hr);
1454             return;
1455         }
1456
1457         if(packet == 0)
1458             return;
1459
1460         hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
1461                 &flags, NULL, NULL);
1462         if(FAILED(hr)){
1463             ERR("GetBuffer failed: %08x\n", hr);
1464             return;
1465         }
1466
1467         acmStreamSize(device->acm_handle, packet * device->bytes_per_frame,
1468                 &packet_bytes, ACM_STREAMSIZEF_SOURCE);
1469
1470         device->acm_offs = 0;
1471
1472         device->acm_hdr.cbStruct = sizeof(device->acm_hdr);
1473         device->acm_hdr.fdwStatus = 0;
1474         device->acm_hdr.dwUser = 0;
1475         device->acm_hdr.pbSrc = data;
1476         device->acm_hdr.cbSrcLength = packet * device->bytes_per_frame;
1477         device->acm_hdr.cbSrcLengthUsed = 0;
1478         device->acm_hdr.dwSrcUser = 0;
1479         device->acm_hdr.pbDst = HeapAlloc(GetProcessHeap(), 0, packet_bytes);
1480         device->acm_hdr.cbDstLength = packet_bytes;
1481         device->acm_hdr.cbDstLengthUsed = 0;
1482         device->acm_hdr.dwDstUser = 0;
1483
1484         mr = acmStreamPrepareHeader(device->acm_handle, &device->acm_hdr, 0);
1485         if(mr != MMSYSERR_NOERROR){
1486             ERR("acmStreamPrepareHeader failed: %d\n", mr);
1487             return;
1488         }
1489
1490         mr = acmStreamConvert(device->acm_handle, &device->acm_hdr, 0);
1491         if(mr != MMSYSERR_NOERROR){
1492             ERR("acmStreamConvert failed: %d\n", mr);
1493             return;
1494         }
1495
1496         hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet);
1497         if(FAILED(hr))
1498             ERR("ReleaseBuffer failed: %08x\n", hr);
1499     }
1500
1501     queue = device->first;
1502     while(queue){
1503         UINT32 to_copy_bytes;
1504
1505         to_copy_bytes = min(queue->dwBufferLength - queue->dwBytesRecorded,
1506                 device->acm_hdr.cbDstLengthUsed - device->acm_offs);
1507
1508         memcpy(queue->lpData + queue->dwBytesRecorded,
1509                 device->acm_hdr.pbDst + device->acm_offs, to_copy_bytes);
1510
1511         queue->dwBytesRecorded += to_copy_bytes;
1512         device->acm_offs += to_copy_bytes;
1513
1514         if(queue->dwBufferLength - queue->dwBytesRecorded <
1515                 device->bytes_per_frame){
1516             queue->dwFlags &= ~WHDR_INQUEUE;
1517             queue->dwFlags |= WHDR_DONE;
1518             device->first = queue = queue->lpNext;
1519         }
1520
1521         if(device->acm_offs >= device->acm_hdr.cbDstLengthUsed){
1522             acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1523             HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1524             device->acm_hdr.cbDstLength = 0;
1525             device->acm_hdr.cbDstLengthUsed = 0;
1526
1527             /* done with this ACM Header, so try to pull more data */
1528             WID_PullACMData(device);
1529             return;
1530         }
1531     }
1532
1533     /* out of WAVEHDRs to write into, so toss the rest of this packet */
1534     acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1535     HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1536     device->acm_hdr.cbDstLength = 0;
1537     device->acm_hdr.cbDstLengthUsed = 0;
1538 }
1539
1540 static void WID_PullData(WINMM_Device *device)
1541 {
1542     WINMM_CBInfo cb_info;
1543     WAVEHDR *queue, *first = NULL;
1544     HRESULT hr;
1545
1546     TRACE("(%p)\n", device->handle);
1547
1548     EnterCriticalSection(&device->lock);
1549
1550     if(!device->device || !device->first)
1551         goto exit;
1552
1553     first = device->first;
1554
1555     if(device->acm_handle){
1556         WID_PullACMData(device);
1557         goto exit;
1558     }
1559
1560     while(device->first){
1561         BYTE *data;
1562         UINT32 pad, packet_len, packet;
1563         DWORD flags;
1564
1565         hr = IAudioClient_GetCurrentPadding(device->client, &pad);
1566         if(FAILED(hr)){
1567             ERR("GetCurrentPadding failed: %08x\n", hr);
1568             goto exit;
1569         }
1570
1571         if(pad == 0)
1572             goto exit;
1573
1574         hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
1575                 &flags, NULL, NULL);
1576         if(FAILED(hr)){
1577             ERR("GetBuffer failed: %08x\n", hr);
1578             goto exit;
1579         }
1580
1581         packet_len = packet;
1582         queue = device->first;
1583         while(queue && packet > 0){
1584             UINT32 to_copy_bytes;
1585
1586             to_copy_bytes = min(packet * device->bytes_per_frame,
1587                     queue->dwBufferLength - queue->dwBytesRecorded);
1588
1589             memcpy(queue->lpData + queue->dwBytesRecorded, data,
1590                     to_copy_bytes);
1591
1592             queue->dwBytesRecorded += to_copy_bytes;
1593
1594             if(queue->dwBufferLength - queue->dwBytesRecorded <
1595                     device->bytes_per_frame){
1596                 queue->dwFlags &= ~WHDR_INQUEUE;
1597                 queue->dwFlags |= WHDR_DONE;
1598                 device->first = queue = queue->lpNext;
1599             }
1600
1601             packet -= to_copy_bytes / device->bytes_per_frame;
1602         }
1603
1604         hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet_len);
1605         if(FAILED(hr))
1606             ERR("ReleaseBuffer failed: %08x\n", hr);
1607     }
1608
1609 exit:
1610     cb_info = device->cb_info;
1611
1612     LeaveCriticalSection(&device->lock);
1613
1614     while(first && (first->dwFlags & WHDR_DONE)){
1615         WAVEHDR *next = first->lpNext;
1616         WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1617         first = next;
1618     }
1619 }
1620
1621 static HRESULT WINMM_BeginPlaying(WINMM_Device *device)
1622 {
1623     HRESULT hr;
1624
1625     TRACE("(%p)\n", device->handle);
1626
1627     if(device->render)
1628         /* prebuffer data before starting */
1629         WOD_PushData(device);
1630
1631     if(device->stopped){
1632         device->stopped = FALSE;
1633
1634         hr = IAudioClient_Start(device->client);
1635         if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){
1636             device->stopped = TRUE;
1637             ERR("Start failed: %08x\n", hr);
1638             return hr;
1639         }
1640     }
1641
1642     return S_OK;
1643 }
1644
1645 static LRESULT WINMM_Pause(HWAVE hwave)
1646 {
1647     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1648     HRESULT hr;
1649
1650     TRACE("(%p)\n", hwave);
1651
1652     if(!WINMM_ValidateAndLock(device))
1653         return MMSYSERR_INVALHANDLE;
1654
1655     hr = IAudioClient_Stop(device->client);
1656     if(FAILED(hr)){
1657         LeaveCriticalSection(&device->lock);
1658         ERR("Stop failed: %08x\n", hr);
1659         return MMSYSERR_ERROR;
1660     }
1661
1662     device->stopped = FALSE;
1663
1664     LeaveCriticalSection(&device->lock);
1665
1666     return MMSYSERR_NOERROR;
1667 }
1668
1669 static LRESULT WINMM_Reset(HWAVE hwave)
1670 {
1671     WINMM_CBInfo cb_info;
1672     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1673     WAVEHDR *first;
1674     HRESULT hr;
1675
1676     TRACE("(%p)\n", hwave);
1677
1678     if(!WINMM_ValidateAndLock(device))
1679         return MMSYSERR_INVALHANDLE;
1680
1681     hr = IAudioClient_Stop(device->client);
1682     if(FAILED(hr)){
1683         LeaveCriticalSection(&device->lock);
1684         ERR("Stop failed: %08x\n", hr);
1685         return MMSYSERR_ERROR;
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     IAudioClient_Reset(device->client);
1699
1700     cb_info = device->cb_info;
1701
1702     LeaveCriticalSection(&device->lock);
1703
1704     while(first){
1705         WAVEHDR *next = first->lpNext;
1706         first->dwFlags &= ~WHDR_INQUEUE;
1707         first->dwFlags |= WHDR_DONE;
1708         if(device->render)
1709             WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1710         else
1711             WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1712         first = next;
1713     }
1714
1715     return MMSYSERR_NOERROR;
1716 }
1717
1718 static MMRESULT WINMM_FramesToMMTime(MMTIME *time, UINT32 played_frames,
1719         UINT32 sample_rate, UINT32 bytes_per_frame)
1720 {
1721     switch(time->wType){
1722     case TIME_SAMPLES:
1723         time->u.sample = played_frames;
1724         return MMSYSERR_NOERROR;
1725     case TIME_MS:
1726         time->u.ms = (DWORD)((played_frames / (double)sample_rate) * 1000);
1727         return MMSYSERR_NOERROR;
1728     case TIME_SMPTE:
1729         time->u.smpte.fps = 30;
1730         if(played_frames >= sample_rate){
1731             time->u.smpte.sec = played_frames / (double)sample_rate;
1732             time->u.smpte.min = time->u.smpte.sec / 60;
1733             time->u.smpte.hour = time->u.smpte.min / 60;
1734             time->u.smpte.sec %= 60;
1735             time->u.smpte.min %= 60;
1736             played_frames %= sample_rate;
1737         }else{
1738             time->u.smpte.sec = 0;
1739             time->u.smpte.min = 0;
1740             time->u.smpte.hour = 0;
1741         }
1742         time->u.smpte.frame = (played_frames / (double)sample_rate) * 30;
1743         return MMSYSERR_NOERROR;
1744     case TIME_BYTES:
1745     default:
1746         time->wType = TIME_BYTES;
1747         time->u.cb = played_frames * bytes_per_frame;
1748         return MMSYSERR_NOERROR;
1749     }
1750
1751     return MMSYSERR_ERROR;
1752 }
1753
1754 static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time)
1755 {
1756     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1757     UINT32 played_frames, sample_rate, bytes_per_frame;
1758
1759     TRACE("(%p, %p)\n", hwave, time);
1760
1761     if(!WINMM_ValidateAndLock(device))
1762         return MMSYSERR_INVALHANDLE;
1763
1764     played_frames = device->played_frames;
1765     sample_rate = device->samples_per_sec;
1766     bytes_per_frame = device->bytes_per_frame;
1767
1768     LeaveCriticalSection(&device->lock);
1769
1770     return WINMM_FramesToMMTime(time, played_frames, sample_rate,
1771             bytes_per_frame);
1772 }
1773
1774 static WINMM_MMDevice *WINMM_GetMixerMMDevice(HMIXEROBJ hmix, DWORD flags,
1775         UINT *mmdev_index)
1776 {
1777     UINT mmdev, dev, junk, *out;
1778     BOOL is_out;
1779
1780     if(!mmdev_index)
1781         out = &mmdev;
1782     else
1783         out = mmdev_index;
1784
1785     switch(flags & 0xF0000000){
1786     case MIXER_OBJECTF_MIXER: /* == 0 */
1787         *out = HandleToULong(hmix);
1788         if(*out < g_outmmdevices_count)
1789             return &g_out_mmdevices[*out];
1790         if(*out - g_outmmdevices_count < g_inmmdevices_count){
1791             *out -= g_outmmdevices_count;
1792             return &g_in_mmdevices[*out];
1793         }
1794         /* fall through -- if it's not a valid mixer device, then
1795          * it could be a valid mixer handle. windows seems to do
1796          * this as well. */
1797     case MIXER_OBJECTF_HMIXER:
1798     case MIXER_OBJECTF_HWAVEOUT:
1799     case MIXER_OBJECTF_HWAVEIN:
1800         WINMM_DecomposeHWAVE((HWAVE)hmix, out, &is_out, &dev, &junk);
1801         if(junk != 0x1 || (is_out && *out >= g_outmmdevices_count) ||
1802                (!is_out && *out >= g_inmmdevices_count))
1803             return NULL;
1804         if(is_out)
1805             return &g_out_mmdevices[*out];
1806         return &g_in_mmdevices[*out];
1807     case MIXER_OBJECTF_WAVEOUT:
1808         *out = HandleToULong(hmix);
1809         if(*out < g_outmmdevices_count)
1810             return &g_out_mmdevices[*out];
1811         return NULL;
1812     case MIXER_OBJECTF_WAVEIN:
1813         *out = HandleToULong(hmix);
1814         if(*out < g_inmmdevices_count)
1815             return &g_in_mmdevices[*out];
1816         return NULL;
1817     }
1818
1819     return NULL;
1820 }
1821
1822 static MMRESULT WINMM_SetupMMDeviceVolume(WINMM_MMDevice *mmdevice)
1823 {
1824     IAudioSessionManager *sesman;
1825     IMMDevice *device;
1826     HRESULT hr;
1827
1828     hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id, &device);
1829     if(FAILED(hr)){
1830         ERR("Device %s (%s) unavailable: %08x\n",
1831                 wine_dbgstr_w(mmdevice->dev_id),
1832                 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
1833         return MMSYSERR_ERROR;
1834     }
1835
1836     hr = IMMDevice_Activate(device, &IID_IAudioSessionManager,
1837             CLSCTX_INPROC_SERVER, NULL, (void**)&sesman);
1838     if(FAILED(hr)){
1839         ERR("Activate failed: %08x\n", hr);
1840         IMMDevice_Release(device);
1841         return MMSYSERR_ERROR;
1842     }
1843
1844     IMMDevice_Release(device);
1845
1846     hr = IAudioSessionManager_GetSimpleAudioVolume(sesman, &mmdevice->session,
1847             FALSE, &mmdevice->volume);
1848     IAudioSessionManager_Release(sesman);
1849     if(FAILED(hr)){
1850         ERR("GetSimpleAudioVolume failed: %08x\n", hr);
1851         return MMSYSERR_ERROR;
1852     }
1853
1854     return MMSYSERR_NOERROR;
1855 }
1856
1857 static LRESULT MXD_GetControlDetails(WINMM_ControlDetails *details)
1858 {
1859     WINMM_MMDevice *mmdevice;
1860     MIXERCONTROLDETAILS *control = details->details;
1861     HRESULT hr;
1862
1863     TRACE("(%p)\n", details->hmix);
1864
1865     mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
1866     if(!mmdevice)
1867         return MMSYSERR_INVALHANDLE;
1868
1869     EnterCriticalSection(&mmdevice->lock);
1870
1871     if(!mmdevice->volume){
1872         MMRESULT mr;
1873
1874         mr = WINMM_SetupMMDeviceVolume(mmdevice);
1875         if(mr != MMSYSERR_NOERROR){
1876             LeaveCriticalSection(&mmdevice->lock);
1877             return mr;
1878         }
1879     }
1880
1881     if(control->dwControlID == 0){
1882         float vol;
1883         MIXERCONTROLDETAILS_UNSIGNED *udet;
1884
1885         if(!control->paDetails ||
1886                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
1887             LeaveCriticalSection(&mmdevice->lock);
1888             return MMSYSERR_INVALPARAM;
1889         }
1890
1891         hr = ISimpleAudioVolume_GetMasterVolume(mmdevice->volume, &vol);
1892         if(FAILED(hr)){
1893             ERR("GetMasterVolume failed: %08x\n", hr);
1894             LeaveCriticalSection(&mmdevice->lock);
1895             return MMSYSERR_ERROR;
1896         }
1897
1898         udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
1899         udet->dwValue = vol * ((unsigned int)0xFFFF);
1900     }else if(control->dwControlID == 1){
1901         BOOL mute;
1902         MIXERCONTROLDETAILS_BOOLEAN *bdet;
1903
1904         if(!control->paDetails ||
1905                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
1906             LeaveCriticalSection(&mmdevice->lock);
1907             return MMSYSERR_INVALPARAM;
1908         }
1909
1910         hr = ISimpleAudioVolume_GetMute(mmdevice->volume, &mute);
1911         if(FAILED(hr)){
1912             ERR("GetMute failed: %08x\n", hr);
1913             LeaveCriticalSection(&mmdevice->lock);
1914             return MMSYSERR_ERROR;
1915         }
1916
1917         bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
1918         bdet->fValue = mute;
1919     }else if(control->dwControlID == 2 || control->dwControlID == 3){
1920         FIXME("What should the sw-side mixer controls map to?\n");
1921     }else{
1922         LeaveCriticalSection(&mmdevice->lock);
1923         return MIXERR_INVALCONTROL;
1924     }
1925
1926     LeaveCriticalSection(&mmdevice->lock);
1927
1928     return MMSYSERR_NOERROR;
1929 }
1930
1931 static LRESULT MXD_SetControlDetails(WINMM_ControlDetails *details)
1932 {
1933     WINMM_MMDevice *mmdevice;
1934     MIXERCONTROLDETAILS *control = details->details;
1935     HRESULT hr;
1936
1937     TRACE("(%p)\n", details->hmix);
1938
1939     mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
1940     if(!mmdevice)
1941         return MMSYSERR_INVALHANDLE;
1942
1943     EnterCriticalSection(&mmdevice->lock);
1944
1945     if(!mmdevice->volume){
1946         MMRESULT mr;
1947
1948         mr = WINMM_SetupMMDeviceVolume(mmdevice);
1949         if(mr != MMSYSERR_NOERROR){
1950             LeaveCriticalSection(&mmdevice->lock);
1951             return mr;
1952         }
1953     }
1954
1955     if(control->dwControlID == 0){
1956         float vol;
1957         MIXERCONTROLDETAILS_UNSIGNED *udet;
1958
1959         if(!control->paDetails ||
1960                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
1961             LeaveCriticalSection(&mmdevice->lock);
1962             return MMSYSERR_INVALPARAM;
1963         }
1964
1965         udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
1966
1967         if(udet->dwValue > 65535){
1968             LeaveCriticalSection(&mmdevice->lock);
1969             return MMSYSERR_INVALPARAM;
1970         }
1971
1972         vol = udet->dwValue / 65535.f;
1973
1974         hr = ISimpleAudioVolume_SetMasterVolume(mmdevice->volume, vol, NULL);
1975         if(FAILED(hr)){
1976             ERR("SetMasterVolume failed: %08x\n", hr);
1977             LeaveCriticalSection(&mmdevice->lock);
1978             return MMSYSERR_ERROR;
1979         }
1980     }else if(control->dwControlID == 1){
1981         BOOL mute;
1982         MIXERCONTROLDETAILS_BOOLEAN *bdet;
1983
1984         if(!control->paDetails ||
1985                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
1986             LeaveCriticalSection(&mmdevice->lock);
1987             return MMSYSERR_INVALPARAM;
1988         }
1989
1990         bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
1991         mute = bdet->fValue;
1992
1993         hr = ISimpleAudioVolume_SetMute(mmdevice->volume, mute, NULL);
1994         if(FAILED(hr)){
1995             ERR("SetMute failed: %08x\n", hr);
1996             LeaveCriticalSection(&mmdevice->lock);
1997             return MMSYSERR_ERROR;
1998         }
1999     }else if(control->dwControlID == 2 || control->dwControlID == 3){
2000         FIXME("What should the sw-side mixer controls map to?\n");
2001     }else{
2002         LeaveCriticalSection(&mmdevice->lock);
2003         return MIXERR_INVALCONTROL;
2004     }
2005
2006     LeaveCriticalSection(&mmdevice->lock);
2007
2008     return MMSYSERR_NOERROR;
2009 }
2010
2011 static LRESULT CALLBACK WINMM_DevicesMsgProc(HWND hwnd, UINT msg, WPARAM wparam,
2012         LPARAM lparam)
2013 {
2014     switch(msg){
2015     case WODM_OPEN:
2016         return WOD_Open((WINMM_OpenInfo*)wparam);
2017     case WODM_CLOSE:
2018         return WOD_Close((HWAVEOUT)wparam);
2019     case WIDM_OPEN:
2020         return WID_Open((WINMM_OpenInfo*)wparam);
2021     case WIDM_CLOSE:
2022         return WID_Close((HWAVEIN)wparam);
2023     case MXDM_GETCONTROLDETAILS:
2024         return MXD_GetControlDetails((WINMM_ControlDetails*)wparam);
2025     case MXDM_SETCONTROLDETAILS:
2026         return MXD_SetControlDetails((WINMM_ControlDetails*)wparam);
2027     }
2028     return DefWindowProcW(hwnd, msg, wparam, lparam);
2029 }
2030
2031 static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
2032 {
2033     HANDLE evt = arg;
2034     HRESULT hr;
2035     static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0};
2036
2037     hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
2038     if(FAILED(hr)){
2039         ERR("CoInitializeEx failed: %08x\n", hr);
2040         return 1;
2041     }
2042
2043     hr = WINMM_InitMMDevices();
2044     if(FAILED(hr)){
2045         CoUninitialize();
2046         return 1;
2047     }
2048
2049     g_devices_hwnd = CreateWindowW(messageW, NULL, 0, 0, 0, 0, 0,
2050             HWND_MESSAGE, NULL, NULL, NULL);
2051     if(!g_devices_hwnd){
2052         ERR("CreateWindow failed: %d\n", GetLastError());
2053         CoUninitialize();
2054         return 1;
2055     }
2056
2057     SetWindowLongPtrW(g_devices_hwnd, GWLP_WNDPROC,
2058             (LONG_PTR)WINMM_DevicesMsgProc);
2059
2060     /* inform caller that the thread is ready to process messages */
2061     SetEvent(evt);
2062     evt = NULL; /* do not use after this point */
2063
2064     while(1){
2065         DWORD wait;
2066         wait = MsgWaitForMultipleObjects(g_devhandle_count, g_device_handles,
2067                 FALSE, INFINITE, QS_ALLINPUT);
2068         if(wait == g_devhandle_count + WAIT_OBJECT_0){
2069             MSG msg;
2070             if(PeekMessageW(&msg, g_devices_hwnd, 0, 0, PM_REMOVE))
2071                 ERR("Unexpected message: 0x%x\n", msg.message);
2072         }else if(wait < g_devhandle_count + WAIT_OBJECT_0){
2073             WINMM_Device *device = g_handle_devices[wait - WAIT_OBJECT_0];
2074             if(device->render)
2075                 WOD_PushData(device);
2076             else
2077                 WID_PullData(device);
2078         }else
2079             ERR("Unexpected MsgWait result 0x%x, GLE: %d\n", wait,
2080                     GetLastError());
2081     }
2082
2083     DestroyWindow(g_devices_hwnd);
2084
2085     CoUninitialize();
2086
2087     return 0;
2088 }
2089
2090 static BOOL WINMM_StartDevicesThread(void)
2091 {
2092     HANDLE events[2];
2093     DWORD wait;
2094
2095     EnterCriticalSection(&g_devthread_lock);
2096
2097     if(g_devices_thread){
2098         DWORD wait;
2099
2100         wait = WaitForSingleObject(g_devices_thread, 0);
2101         if(wait == WAIT_TIMEOUT){
2102             LeaveCriticalSection(&g_devthread_lock);
2103             return TRUE;
2104         }
2105         if(wait != WAIT_OBJECT_0){
2106             LeaveCriticalSection(&g_devthread_lock);
2107             return FALSE;
2108         }
2109
2110         g_devices_thread = NULL;
2111         g_devices_hwnd = NULL;
2112     }
2113
2114     TRACE("Starting up devices thread\n");
2115
2116     events[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
2117
2118     g_devices_thread = CreateThread(NULL, 0, WINMM_DevicesThreadProc,
2119             events[0], 0, NULL);
2120     if(!g_devices_thread){
2121         LeaveCriticalSection(&g_devthread_lock);
2122         CloseHandle(events[0]);
2123         return FALSE;
2124     }
2125
2126     events[1] = g_devices_thread;
2127     wait = WaitForMultipleObjects(2, events, FALSE, INFINITE);
2128     CloseHandle(events[0]);
2129     if(wait != WAIT_OBJECT_0){
2130         if(wait == 1 + WAIT_OBJECT_0){
2131             CloseHandle(g_devices_thread);
2132             g_devices_thread = NULL;
2133             g_devices_hwnd = NULL;
2134         }
2135         LeaveCriticalSection(&g_devthread_lock);
2136         return FALSE;
2137     }
2138
2139     LeaveCriticalSection(&g_devthread_lock);
2140
2141     return TRUE;
2142 }
2143
2144 /**************************************************************************
2145  *                              waveOutGetNumDevs               [WINMM.@]
2146  */
2147 UINT WINAPI waveOutGetNumDevs(void)
2148 {
2149     if(!WINMM_StartDevicesThread())
2150         return 0;
2151
2152     TRACE("count: %u\n", g_outmmdevices_count);
2153
2154     return g_outmmdevices_count;
2155 }
2156
2157 /**************************************************************************
2158  *                              waveOutGetDevCapsA              [WINMM.@]
2159  */
2160 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
2161                                UINT uSize)
2162 {
2163     WAVEOUTCAPSW        wocW;
2164     UINT                ret;
2165
2166     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2167
2168     if(!WINMM_StartDevicesThread())
2169         return MMSYSERR_ERROR;
2170
2171     if(!lpCaps)
2172         return MMSYSERR_INVALPARAM;
2173
2174     ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
2175
2176     if (ret == MMSYSERR_NOERROR) {
2177         WAVEOUTCAPSA wocA;
2178         wocA.wMid           = wocW.wMid;
2179         wocA.wPid           = wocW.wPid;
2180         wocA.vDriverVersion = wocW.vDriverVersion;
2181         WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,
2182                              sizeof(wocA.szPname), NULL, NULL );
2183         wocA.dwFormats      = wocW.dwFormats;
2184         wocA.wChannels      = wocW.wChannels;
2185         wocA.dwSupport      = wocW.dwSupport;
2186         memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));
2187     }
2188     return ret;
2189 }
2190
2191 /**************************************************************************
2192  *                              waveOutGetDevCapsW              [WINMM.@]
2193  */
2194 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
2195                                UINT uSize)
2196 {
2197     WAVEOUTCAPSW mapper_caps, *caps;
2198
2199     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2200
2201     if(!WINMM_StartDevicesThread())
2202         return MMSYSERR_ERROR;
2203
2204     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2205
2206     if(WINMM_IsMapper(uDeviceID)){
2207         /* FIXME: Should be localized */
2208         static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2209             'n','d',' ','M','a','p','p','e','r',0};
2210
2211         mapper_caps.wMid = 0xFF;
2212         mapper_caps.wPid = 0xFF;
2213         mapper_caps.vDriverVersion = 0x00010001;
2214         mapper_caps.dwFormats = 0xFFFFFFFF;
2215         mapper_caps.wReserved1 = 0;
2216         mapper_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
2217             WAVECAPS_SAMPLEACCURATE;
2218         mapper_caps.wChannels = 2;
2219         lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2220
2221         caps = &mapper_caps;
2222     }else{
2223         if(uDeviceID >= g_outmmdevices_count)
2224             return MMSYSERR_BADDEVICEID;
2225
2226         caps = &g_out_mmdevices[uDeviceID].out_caps;
2227     }
2228
2229     memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2230
2231     return MMSYSERR_NOERROR;
2232 }
2233
2234 /**************************************************************************
2235  *                              waveOutGetErrorTextA    [WINMM.@]
2236  *                              waveInGetErrorTextA     [WINMM.@]
2237  */
2238 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2239 {
2240     UINT        ret;
2241
2242     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2243     else if (uSize == 0) ret = MMSYSERR_NOERROR;
2244     else
2245     {
2246         LPWSTR  xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
2247         if (!xstr) ret = MMSYSERR_NOMEM;
2248         else
2249         {
2250             ret = waveOutGetErrorTextW(uError, xstr, uSize);
2251             if (ret == MMSYSERR_NOERROR)
2252                 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
2253             HeapFree(GetProcessHeap(), 0, xstr);
2254         }
2255     }
2256     return ret;
2257 }
2258
2259 /**************************************************************************
2260  *                              waveOutGetErrorTextW    [WINMM.@]
2261  *                              waveInGetErrorTextW     [WINMM.@]
2262  */
2263 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2264 {
2265     UINT        ret = MMSYSERR_BADERRNUM;
2266
2267     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2268     else if (uSize == 0) ret = MMSYSERR_NOERROR;
2269     else if (
2270                /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
2271                 * a warning for the test was always true */
2272                (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
2273                (uError >= WAVERR_BASE  && uError <= WAVERR_LASTERROR)) {
2274         if (LoadStringW(hWinMM32Instance,
2275                         uError, lpText, uSize) > 0) {
2276             ret = MMSYSERR_NOERROR;
2277         }
2278     }
2279     return ret;
2280 }
2281
2282 /**************************************************************************
2283  *                      waveOutOpen                     [WINMM.@]
2284  * All the args/structs have the same layout as the win16 equivalents
2285  */
2286 MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
2287                        LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2288                        DWORD_PTR dwInstance, DWORD dwFlags)
2289 {
2290     LRESULT res;
2291     WINMM_OpenInfo info;
2292     WINMM_CBInfo cb_info;
2293
2294     TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut, uDeviceID, lpFormat,
2295             dwCallback, dwInstance, dwFlags);
2296
2297     if(!WINMM_StartDevicesThread())
2298         return MMSYSERR_ERROR;
2299
2300     if(!lphWaveOut && !(dwFlags & WAVE_FORMAT_QUERY))
2301         return MMSYSERR_INVALPARAM;
2302
2303     res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2304     if(res != MMSYSERR_NOERROR)
2305         return res;
2306
2307     info.format = (WAVEFORMATEX*)lpFormat;
2308     info.callback = dwCallback;
2309     info.cb_user = dwInstance;
2310     info.req_device = uDeviceID;
2311     info.flags = dwFlags;
2312
2313     res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0);
2314     if(res != MMSYSERR_NOERROR)
2315         return res;
2316
2317     if(lphWaveOut)
2318         *lphWaveOut = (HWAVEOUT)info.handle;
2319
2320     cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2321     cb_info.callback = dwCallback;
2322     cb_info.user = dwInstance;
2323     cb_info.hwave = info.handle;
2324
2325     WINMM_NotifyClient(&cb_info, WOM_OPEN, 0, 0);
2326
2327     return res;
2328 }
2329
2330 /**************************************************************************
2331  *                              waveOutClose            [WINMM.@]
2332  */
2333 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
2334 {
2335     UINT res;
2336     WINMM_Device *device;
2337     WINMM_CBInfo cb_info;
2338
2339     TRACE("(%p)\n", hWaveOut);
2340
2341     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2342
2343     if(!WINMM_ValidateAndLock(device))
2344         return MMSYSERR_INVALHANDLE;
2345
2346     cb_info = device->cb_info;
2347
2348     LeaveCriticalSection(&device->lock);
2349
2350     res = SendMessageW(g_devices_hwnd, WODM_CLOSE, (WPARAM)hWaveOut, 0);
2351
2352     if(res == MMSYSERR_NOERROR)
2353         WINMM_NotifyClient(&cb_info, WOM_CLOSE, 0, 0);
2354
2355     return res;
2356 }
2357
2358 /**************************************************************************
2359  *                              waveOutPrepareHeader    [WINMM.@]
2360  */
2361 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
2362                                  WAVEHDR* lpWaveOutHdr, UINT uSize)
2363 {
2364     TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2365
2366     if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2367         return MMSYSERR_INVALPARAM;
2368
2369     if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2370         return WAVERR_STILLPLAYING;
2371
2372     return WINMM_PrepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2373 }
2374
2375 /**************************************************************************
2376  *                              waveOutUnprepareHeader  [WINMM.@]
2377  */
2378 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
2379                                    LPWAVEHDR lpWaveOutHdr, UINT uSize)
2380 {
2381     TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2382
2383     if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2384         return MMSYSERR_INVALPARAM;
2385     
2386     if(!(lpWaveOutHdr->dwFlags & WHDR_PREPARED))
2387         return MMSYSERR_NOERROR;
2388
2389     if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2390         return WAVERR_STILLPLAYING;
2391
2392     return WINMM_UnprepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2393 }
2394
2395 /**************************************************************************
2396  *                              waveOutWrite            [WINMM.@]
2397  */
2398 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, WAVEHDR *header, UINT uSize)
2399 {
2400     WINMM_Device *device;
2401     HRESULT hr;
2402
2403     TRACE("(%p, %p, %u)\n", hWaveOut, header, uSize);
2404
2405     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2406
2407     if(!WINMM_ValidateAndLock(device))
2408         return MMSYSERR_INVALHANDLE;
2409
2410     if(!header->lpData || !(header->dwFlags & WHDR_PREPARED)){
2411         LeaveCriticalSection(&device->lock);
2412         return WAVERR_UNPREPARED;
2413     }
2414
2415     if(header->dwFlags & WHDR_INQUEUE){
2416         LeaveCriticalSection(&device->lock);
2417         return WAVERR_STILLPLAYING;
2418     }
2419
2420     if(device->acm_handle){
2421         ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
2422         MMRESULT mr;
2423
2424         ash->cbSrcLength = header->dwBufferLength;
2425         mr = acmStreamConvert(device->acm_handle, ash, 0);
2426         if(mr != MMSYSERR_NOERROR){
2427             LeaveCriticalSection(&device->lock);
2428             return mr;
2429         }
2430     }
2431
2432     if(device->first){
2433         device->last->lpNext = header;
2434         device->last = header;
2435         if(!device->playing)
2436             device->playing = header;
2437     }else{
2438         device->playing = device->first = device->last = header;
2439         if(header->dwFlags & WHDR_BEGINLOOP){
2440             device->loop_counter = header->dwLoops;
2441             device->loop_start = header;
2442         }
2443     }
2444
2445     header->lpNext = NULL;
2446     header->dwFlags &= ~WHDR_DONE;
2447     header->dwFlags |= WHDR_INQUEUE;
2448
2449     hr = WINMM_BeginPlaying(device);
2450     if(FAILED(hr)){
2451         LeaveCriticalSection(&device->lock);
2452         return MMSYSERR_ERROR;
2453     }
2454
2455     LeaveCriticalSection(&device->lock);
2456
2457     return MMSYSERR_NOERROR;
2458 }
2459
2460 /**************************************************************************
2461  *                              waveOutBreakLoop        [WINMM.@]
2462  */
2463 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
2464 {
2465     WINMM_Device *device;
2466
2467     TRACE("(%p)\n", hWaveOut);
2468
2469     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2470
2471     if(!WINMM_ValidateAndLock(device))
2472         return MMSYSERR_INVALHANDLE;
2473
2474     device->loop_counter = 0;
2475
2476     LeaveCriticalSection(&device->lock);
2477
2478     return MMSYSERR_NOERROR;
2479 }
2480
2481 /**************************************************************************
2482  *                              waveOutPause            [WINMM.@]
2483  */
2484 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
2485 {
2486     TRACE("(%p)\n", hWaveOut);
2487
2488     return WINMM_Pause((HWAVE)hWaveOut);
2489 }
2490
2491 /**************************************************************************
2492  *                              waveOutReset            [WINMM.@]
2493  */
2494 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2495 {
2496     TRACE("(%p)\n", hWaveOut);
2497
2498     return WINMM_Reset((HWAVE)hWaveOut);
2499 }
2500
2501 /**************************************************************************
2502  *                              waveOutRestart          [WINMM.@]
2503  */
2504 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2505 {
2506     WINMM_Device *device;
2507     HRESULT hr;
2508
2509     TRACE("(%p)\n", hWaveOut);
2510
2511     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2512
2513     if(!WINMM_ValidateAndLock(device))
2514         return MMSYSERR_INVALHANDLE;
2515
2516     device->stopped = TRUE;
2517
2518     hr = WINMM_BeginPlaying(device);
2519     if(FAILED(hr)){
2520         LeaveCriticalSection(&device->lock);
2521         return MMSYSERR_ERROR;
2522     }
2523
2524     LeaveCriticalSection(&device->lock);
2525
2526     return MMSYSERR_NOERROR;
2527 }
2528
2529 /**************************************************************************
2530  *                              waveOutGetPosition      [WINMM.@]
2531  */
2532 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2533                                UINT uSize)
2534 {
2535     TRACE("(%p, %p, %u)\n", hWaveOut, lpTime, uSize);
2536
2537     if(!uSize || !lpTime || uSize != sizeof(MMTIME))
2538         return MMSYSERR_INVALPARAM;
2539
2540     return WINMM_GetPosition((HWAVE)hWaveOut, lpTime);
2541 }
2542
2543 /**************************************************************************
2544  *                              waveOutGetPitch         [WINMM.@]
2545  */
2546 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2547 {
2548     TRACE("(%p, %p)\n", hWaveOut, lpdw);
2549     return MMSYSERR_NOTSUPPORTED;
2550 }
2551
2552 /**************************************************************************
2553  *                              waveOutSetPitch         [WINMM.@]
2554  */
2555 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
2556 {
2557     TRACE("(%p, %08x)\n", hWaveOut, dw);
2558
2559     return MMSYSERR_NOTSUPPORTED;
2560 }
2561
2562 /**************************************************************************
2563  *                              waveOutGetPlaybackRate  [WINMM.@]
2564  */
2565 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
2566 {
2567     TRACE("(%p, %p)\n", hWaveOut, lpdw);
2568
2569     return MMSYSERR_NOTSUPPORTED;
2570 }
2571
2572 /**************************************************************************
2573  *                              waveOutSetPlaybackRate  [WINMM.@]
2574  */
2575 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
2576 {
2577     TRACE("(%p, %08x)\n", hWaveOut, dw);
2578
2579     return MMSYSERR_NOTSUPPORTED;
2580 }
2581
2582 /**************************************************************************
2583  *                              waveOutGetVolume        [WINMM.@]
2584  */
2585 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, DWORD *out)
2586 {
2587     WINMM_Device *device;
2588     UINT32 channels;
2589     float *vols;
2590     HRESULT hr;
2591
2592     TRACE("(%p, %p)\n", hWaveOut, out);
2593
2594     if(!out)
2595         return MMSYSERR_INVALPARAM;
2596
2597     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2598
2599     if(!WINMM_ValidateAndLock(device))
2600         return MMSYSERR_INVALHANDLE;
2601
2602     hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2603     if(FAILED(hr)){
2604         LeaveCriticalSection(&device->lock);
2605         ERR("GetChannelCount failed: %08x\n", hr);
2606         return MMSYSERR_ERROR;
2607     }
2608
2609     vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2610     if(!vols){
2611         LeaveCriticalSection(&device->lock);
2612         return MMSYSERR_NOMEM;
2613     }
2614
2615     hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2616     if(FAILED(hr)){
2617         LeaveCriticalSection(&device->lock);
2618         HeapFree(GetProcessHeap(), 0, vols);
2619         ERR("GetAllVolumes failed: %08x\n", hr);
2620         return MMSYSERR_ERROR;
2621     }
2622
2623     LeaveCriticalSection(&device->lock);
2624
2625     *out = ((UINT16)(vols[0] * (DWORD)0xFFFF));
2626     if(channels > 1)
2627         *out |= ((UINT16)(vols[1] * (DWORD)0xFFFF)) << 16;
2628
2629     HeapFree(GetProcessHeap(), 0, vols);
2630
2631     return MMSYSERR_NOERROR;
2632 }
2633
2634 /**************************************************************************
2635  *                              waveOutSetVolume        [WINMM.@]
2636  */
2637 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD in)
2638 {
2639     WINMM_Device *device;
2640     UINT32 channels;
2641     float *vols;
2642     HRESULT hr;
2643
2644     TRACE("(%p, %08x)\n", hWaveOut, in);
2645
2646     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2647
2648     if(!WINMM_ValidateAndLock(device))
2649         return MMSYSERR_INVALHANDLE;
2650
2651     hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2652     if(FAILED(hr)){
2653         LeaveCriticalSection(&device->lock);
2654         ERR("GetChannelCount failed: %08x\n", hr);
2655         return MMSYSERR_ERROR;
2656     }
2657
2658     vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2659     if(!vols){
2660         LeaveCriticalSection(&device->lock);
2661         return MMSYSERR_NOMEM;
2662     }
2663
2664     hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2665     if(FAILED(hr)){
2666         LeaveCriticalSection(&device->lock);
2667         HeapFree(GetProcessHeap(), 0, vols);
2668         ERR("GetAllVolumes failed: %08x\n", hr);
2669         return MMSYSERR_ERROR;
2670     }
2671
2672     vols[0] = (float)((DWORD)(in & 0xFFFF) / (float)0xFFFF);
2673     if(channels > 1)
2674         vols[1] = (float)((DWORD)(in >> 16) / (float)0xFFFF);
2675
2676     hr = IAudioStreamVolume_SetAllVolumes(device->volume, channels, vols);
2677     if(FAILED(hr)){
2678         LeaveCriticalSection(&device->lock);
2679         HeapFree(GetProcessHeap(), 0, vols);
2680         ERR("SetAllVolumes failed: %08x\n", hr);
2681         return MMSYSERR_ERROR;
2682     }
2683
2684     LeaveCriticalSection(&device->lock);
2685
2686     HeapFree(GetProcessHeap(), 0, vols);
2687
2688     return MMSYSERR_NOERROR;
2689 }
2690
2691 /**************************************************************************
2692  *                              waveOutGetID            [WINMM.@]
2693  */
2694 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
2695 {
2696     WINMM_Device *device;
2697     UINT dev, junk;
2698     BOOL is_out;
2699
2700     TRACE("(%p, %p)\n", hWaveOut, lpuDeviceID);
2701
2702     if(!lpuDeviceID)
2703         return MMSYSERR_INVALPARAM;
2704
2705     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2706     if(!WINMM_ValidateAndLock(device))
2707         return MMSYSERR_INVALHANDLE;
2708
2709     LeaveCriticalSection(&device->lock);
2710
2711     WINMM_DecomposeHWAVE((HWAVE)hWaveOut, lpuDeviceID, &is_out, &dev, &junk);
2712
2713     return MMSYSERR_NOERROR;
2714 }
2715
2716 static UINT WINMM_QueryInstanceIDSize(UINT device, DWORD_PTR *len, BOOL is_out)
2717 {
2718     UINT count;
2719     WINMM_MMDevice *devices;
2720
2721     TRACE("(%u, %p, %d)\n", device, len, is_out);
2722
2723     if(is_out){
2724         count = g_outmmdevices_count;
2725         devices = g_out_mmdevices;
2726     }else{
2727         count = g_inmmdevices_count;
2728         devices = g_in_mmdevices;
2729     }
2730
2731     if(device >= count)
2732         return MMSYSERR_INVALHANDLE;
2733
2734     *len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2735
2736     return MMSYSERR_NOERROR;
2737 }
2738
2739 static UINT WINMM_QueryInstanceID(UINT device, WCHAR *str, DWORD_PTR len,
2740         BOOL is_out)
2741 {
2742     UINT count, id_len;
2743     WINMM_MMDevice *devices;
2744
2745     TRACE("(%u, %p, %d)\n", device, str, is_out);
2746
2747     if(is_out){
2748         count = g_outmmdevices_count;
2749         devices = g_out_mmdevices;
2750     }else{
2751         count = g_inmmdevices_count;
2752         devices = g_in_mmdevices;
2753     }
2754
2755     if(device >= count)
2756         return MMSYSERR_INVALHANDLE;
2757
2758     id_len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2759     if(len < id_len)
2760         return MMSYSERR_ERROR;
2761
2762     memcpy(str, devices[device].dev_id, id_len);
2763
2764     return MMSYSERR_NOERROR;
2765 }
2766
2767 static UINT WINMM_DRVMessage(UINT dev, UINT message, DWORD_PTR param1,
2768         DWORD_PTR param2, BOOL is_out)
2769 {
2770     WINE_MLD *wmld;
2771     UINT type = is_out ? MMDRV_WAVEOUT : MMDRV_WAVEIN;
2772
2773     TRACE("(%u, %u, %ld, %ld, %d)\n", dev, message, param1, param2, is_out);
2774
2775     if((wmld = MMDRV_Get(ULongToHandle(dev), type, FALSE)) == NULL){
2776         if((wmld = MMDRV_Get(ULongToHandle(dev), type, TRUE)) != NULL)
2777             return MMDRV_PhysicalFeatures(wmld, message, param1, param2);
2778         return MMSYSERR_INVALHANDLE;
2779     }
2780
2781     if(message < DRVM_IOCTL ||
2782             (message >= DRVM_IOCTL_LAST && message < DRVM_MAPPER))
2783         return MMSYSERR_INVALPARAM;
2784
2785     return MMDRV_Message(wmld, message, param1, param2);
2786 }
2787
2788 static UINT WINMM_FillDSDriverDesc(UINT dev, DSDRIVERDESC *desc, BOOL is_out)
2789 {
2790     WCHAR *name;
2791
2792     if(is_out){
2793         if(dev >= g_outmmdevices_count)
2794             return MMSYSERR_INVALHANDLE;
2795         name = g_out_mmdevices[dev].out_caps.szPname;
2796     }else{
2797         if(dev >= g_inmmdevices_count)
2798             return MMSYSERR_INVALHANDLE;
2799         name = g_in_mmdevices[dev].in_caps.szPname;
2800     }
2801
2802     memset(desc, 0, sizeof(*desc));
2803
2804     strcpy(desc->szDesc, "WinMM: ");
2805     WideCharToMultiByte(CP_ACP, 0, name, -1,
2806             desc->szDesc + strlen(desc->szDesc),
2807             sizeof(desc->szDesc) - strlen(desc->szDesc), NULL, NULL);
2808
2809     strcpy(desc->szDrvname, "winmm.dll");
2810
2811     return MMSYSERR_NOERROR;
2812 }
2813
2814 /**************************************************************************
2815  *                              waveOutMessage          [WINMM.@]
2816  */
2817 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2818                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2819 {
2820     TRACE("(%p, %u, %lx, %lx)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2821
2822     switch(uMessage){
2823     case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
2824         return WINMM_QueryInstanceIDSize(HandleToULong(hWaveOut),
2825                 (DWORD_PTR*)dwParam1, TRUE);
2826     case DRV_QUERYFUNCTIONINSTANCEID:
2827         return WINMM_QueryInstanceID(HandleToULong(hWaveOut), (WCHAR*)dwParam1, dwParam2, TRUE);
2828     /* TODO: Remove after dsound has been rewritten for mmdevapi */
2829     case DRV_QUERYDSOUNDDESC:
2830     case DRV_QUERYDSOUNDIFACE:
2831         if(dwParam2 == DS_HW_ACCEL_FULL)
2832             return WINMM_DRVMessage(HandleToULong(hWaveOut), uMessage,
2833                     dwParam1, 0, TRUE);
2834         return WINMM_FillDSDriverDesc(HandleToULong(hWaveOut),
2835                 (DSDRIVERDESC*)dwParam1, TRUE);
2836     }
2837
2838     return MMSYSERR_NOTSUPPORTED;
2839 }
2840
2841 /**************************************************************************
2842  *                              waveInGetNumDevs                [WINMM.@]
2843  */
2844 UINT WINAPI waveInGetNumDevs(void)
2845 {
2846     if(!WINMM_StartDevicesThread())
2847         return 0;
2848
2849     TRACE("count: %u\n", g_inmmdevices_count);
2850
2851     return g_inmmdevices_count;
2852 }
2853
2854 /**************************************************************************
2855  *                              waveInGetDevCapsW               [WINMM.@]
2856  */
2857 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2858 {
2859     WAVEINCAPSW mapper_caps, *caps;
2860
2861     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2862
2863     if(!WINMM_StartDevicesThread())
2864         return MMSYSERR_ERROR;
2865
2866     if(!lpCaps)
2867         return MMSYSERR_INVALPARAM;
2868
2869     if(WINMM_IsMapper(uDeviceID)){
2870         /* FIXME: Should be localized */
2871         static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2872             'n','d',' ','M','a','p','p','e','r',0};
2873
2874         mapper_caps.wMid = 0xFF;
2875         mapper_caps.wPid = 0xFF;
2876         mapper_caps.vDriverVersion = 0x00010001;
2877         mapper_caps.dwFormats = 0xFFFFFFFF;
2878         mapper_caps.wReserved1 = 0;
2879         mapper_caps.wChannels = 2;
2880         lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2881
2882         caps = &mapper_caps;
2883     }else{
2884         if(uDeviceID >= g_inmmdevices_count)
2885             return MMSYSERR_BADDEVICEID;
2886
2887         caps = &g_in_mmdevices[uDeviceID].in_caps;
2888     }
2889
2890     memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2891
2892     return MMSYSERR_NOERROR;
2893 }
2894
2895 /**************************************************************************
2896  *                              waveInGetDevCapsA               [WINMM.@]
2897  */
2898 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2899 {
2900     UINT ret;
2901     WAVEINCAPSW wicW;
2902
2903     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2904
2905     if(!WINMM_StartDevicesThread())
2906         return MMSYSERR_ERROR;
2907
2908     if(!lpCaps)
2909         return MMSYSERR_INVALPARAM;
2910
2911     ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2912
2913     if (ret == MMSYSERR_NOERROR) {
2914         WAVEINCAPSA wicA;
2915         wicA.wMid           = wicW.wMid;
2916         wicA.wPid           = wicW.wPid;
2917         wicA.vDriverVersion = wicW.vDriverVersion;
2918         WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2919                              sizeof(wicA.szPname), NULL, NULL );
2920         wicA.dwFormats      = wicW.dwFormats;
2921         wicA.wChannels      = wicW.wChannels;
2922         memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2923     }
2924     return ret;
2925 }
2926
2927 /**************************************************************************
2928  *                              waveInOpen                      [WINMM.@]
2929  */
2930 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2931                            LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2932                            DWORD_PTR dwInstance, DWORD dwFlags)
2933 {
2934     LRESULT res;
2935     WINMM_OpenInfo info;
2936     WINMM_CBInfo cb_info;
2937
2938     TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
2939             dwCallback, dwInstance, dwFlags);
2940
2941     if(!WINMM_StartDevicesThread())
2942         return MMSYSERR_ERROR;
2943
2944     if(!lphWaveIn && !(dwFlags & WAVE_FORMAT_QUERY))
2945         return MMSYSERR_INVALPARAM;
2946
2947     res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2948     if(res != MMSYSERR_NOERROR)
2949         return res;
2950
2951     info.format = (WAVEFORMATEX*)lpFormat;
2952     info.callback = dwCallback;
2953     info.cb_user = dwInstance;
2954     info.req_device = uDeviceID;
2955     info.flags = dwFlags;
2956
2957     res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
2958     if(res != MMSYSERR_NOERROR)
2959         return res;
2960
2961     if(lphWaveIn)
2962         *lphWaveIn = (HWAVEIN)info.handle;
2963
2964     cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2965     cb_info.callback = dwCallback;
2966     cb_info.user = dwInstance;
2967     cb_info.hwave = info.handle;
2968
2969     WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
2970
2971     return res;
2972 }
2973
2974 /**************************************************************************
2975  *                              waveInClose                     [WINMM.@]
2976  */
2977 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2978 {
2979     WINMM_Device *device;
2980     WINMM_CBInfo cb_info;
2981     UINT res;
2982
2983     TRACE("(%p)\n", hWaveIn);
2984
2985     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
2986
2987     if(!WINMM_ValidateAndLock(device))
2988         return MMSYSERR_INVALHANDLE;
2989
2990     cb_info = device->cb_info;
2991
2992     LeaveCriticalSection(&device->lock);
2993
2994     res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
2995
2996     if(res == MMSYSERR_NOERROR)
2997         WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
2998
2999     return res;
3000 }
3001
3002 /**************************************************************************
3003  *                              waveInPrepareHeader             [WINMM.@]
3004  */
3005 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
3006                                 UINT uSize)
3007 {
3008     TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
3009
3010     if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
3011         return MMSYSERR_INVALPARAM;
3012
3013     if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
3014         return WAVERR_STILLPLAYING;
3015
3016     return WINMM_PrepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
3017 }
3018
3019 /**************************************************************************
3020  *                              waveInUnprepareHeader   [WINMM.@]
3021  */
3022 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
3023                                   UINT uSize)
3024 {
3025     TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
3026
3027     if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
3028         return MMSYSERR_INVALPARAM;
3029
3030     if(!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
3031         return MMSYSERR_NOERROR;
3032
3033     if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
3034         return WAVERR_STILLPLAYING;
3035
3036     return WINMM_UnprepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
3037 }
3038
3039 /**************************************************************************
3040  *                              waveInAddBuffer         [WINMM.@]
3041  */
3042 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn, WAVEHDR *header, UINT uSize)
3043 {
3044     WINMM_Device *device;
3045
3046     TRACE("(%p, %p, %u)\n", hWaveIn, header, uSize);
3047
3048     if(!header || uSize < sizeof(WAVEHDR))
3049         return MMSYSERR_INVALPARAM;
3050
3051     if(!(header->dwFlags & WHDR_PREPARED))
3052         return WAVERR_UNPREPARED;
3053
3054     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3055
3056     if(!WINMM_ValidateAndLock(device))
3057         return MMSYSERR_INVALHANDLE;
3058
3059     if(!device->first)
3060         device->first = device->last = header;
3061     else{
3062         device->last->lpNext = header;
3063         device->last = header;
3064     }
3065
3066     header->dwBytesRecorded = 0;
3067     header->lpNext = NULL;
3068     header->dwFlags &= ~WHDR_DONE;
3069     header->dwFlags |= WHDR_INQUEUE;
3070
3071     LeaveCriticalSection(&device->lock);
3072
3073     return MMSYSERR_NOERROR;
3074 }
3075
3076 /**************************************************************************
3077  *                              waveInReset             [WINMM.@]
3078  */
3079 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
3080 {
3081     TRACE("(%p)\n", hWaveIn);
3082
3083     return WINMM_Reset((HWAVE)hWaveIn);
3084 }
3085
3086 /**************************************************************************
3087  *                              waveInStart             [WINMM.@]
3088  */
3089 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
3090 {
3091     WINMM_Device *device;
3092     HRESULT hr;
3093
3094     TRACE("(%p)\n", hWaveIn);
3095
3096     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3097
3098     if(!WINMM_ValidateAndLock(device))
3099         return MMSYSERR_INVALHANDLE;
3100
3101     hr = WINMM_BeginPlaying(device);
3102     if(FAILED(hr)){
3103         LeaveCriticalSection(&device->lock);
3104         return MMSYSERR_ERROR;
3105     }
3106
3107     LeaveCriticalSection(&device->lock);
3108
3109     return MMSYSERR_NOERROR;
3110 }
3111
3112 /**************************************************************************
3113  *                              waveInStop              [WINMM.@]
3114  */
3115 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
3116 {
3117     WINMM_CBInfo cb_info;
3118     WINMM_Device *device;
3119     WAVEHDR *buf;
3120     HRESULT hr;
3121
3122     TRACE("(%p)\n", hWaveIn);
3123
3124     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3125
3126     if(!WINMM_ValidateAndLock(device))
3127         return MMSYSERR_INVALHANDLE;
3128
3129     hr = WINMM_Pause((HWAVE)hWaveIn);
3130     if(FAILED(hr)){
3131         LeaveCriticalSection(&device->lock);
3132         return MMSYSERR_ERROR;
3133     }
3134     device->stopped = TRUE;
3135
3136     buf = device->first;
3137     if(buf && buf->dwBytesRecorded > 0){
3138         device->first = buf->lpNext;
3139         buf->dwFlags &= ~WHDR_INQUEUE;
3140         buf->dwFlags |= WHDR_DONE;
3141     }else
3142         buf = NULL;
3143
3144     cb_info = device->cb_info;
3145
3146     LeaveCriticalSection(&device->lock);
3147
3148     if(buf)
3149         WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
3150
3151     return MMSYSERR_NOERROR;
3152 }
3153
3154 /**************************************************************************
3155  *                              waveInGetPosition       [WINMM.@]
3156  */
3157 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
3158                               UINT uSize)
3159 {
3160     TRACE("(%p, %p, %u)\n", hWaveIn, lpTime, uSize);
3161
3162     if(!uSize || !lpTime || uSize != sizeof(MMTIME))
3163         return MMSYSERR_INVALPARAM;
3164
3165     return WINMM_GetPosition((HWAVE)hWaveIn, lpTime);
3166 }
3167
3168 /**************************************************************************
3169  *                              waveInGetID                     [WINMM.@]
3170  */
3171 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
3172 {
3173     UINT dev, junk;
3174     BOOL is_out;
3175     WINMM_Device *device;
3176
3177     TRACE("(%p, %p)\n", hWaveIn, lpuDeviceID);
3178
3179     if(!lpuDeviceID)
3180         return MMSYSERR_INVALPARAM;
3181
3182     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3183     if(!WINMM_ValidateAndLock(device))
3184         return MMSYSERR_INVALHANDLE;
3185
3186     LeaveCriticalSection(&device->lock);
3187
3188     WINMM_DecomposeHWAVE((HWAVE)hWaveIn, lpuDeviceID, &is_out, &dev, &junk);
3189
3190     return MMSYSERR_NOERROR;
3191 }
3192
3193 /**************************************************************************
3194  *                              waveInMessage           [WINMM.@]
3195  */
3196 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
3197                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3198 {
3199     TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
3200
3201     switch(uMessage){
3202     case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
3203         return WINMM_QueryInstanceIDSize(HandleToULong(hWaveIn),
3204                 (DWORD_PTR*)dwParam1, FALSE);
3205     case DRV_QUERYFUNCTIONINSTANCEID:
3206         return WINMM_QueryInstanceID(HandleToULong(hWaveIn), (WCHAR*)dwParam1, dwParam2, FALSE);
3207     /* TODO: Remove after dsound has been rewritten for mmdevapi */
3208     case DRV_QUERYDSOUNDDESC:
3209     case DRV_QUERYDSOUNDIFACE:
3210         if(dwParam2 == DS_HW_ACCEL_FULL)
3211             return WINMM_DRVMessage(HandleToULong(hWaveIn), uMessage,
3212                     dwParam1, 0, FALSE);
3213         return WINMM_FillDSDriverDesc(HandleToULong(hWaveIn),
3214                 (DSDRIVERDESC*)dwParam1, FALSE);
3215     }
3216
3217     return MMSYSERR_NOTSUPPORTED;
3218 }
3219
3220 UINT WINAPI mixerGetNumDevs(void)
3221 {
3222     TRACE("\n");
3223
3224     if(!WINMM_StartDevicesThread())
3225         return 0;
3226
3227     return g_outmmdevices_count + g_inmmdevices_count;
3228 }
3229
3230 /**************************************************************************
3231  *                              mixerGetDevCapsA                [WINMM.@]
3232  */
3233 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
3234 {
3235     MIXERCAPSW micW;
3236     UINT       ret;
3237
3238     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3239
3240     if(!lpCaps)
3241         return MMSYSERR_INVALPARAM;
3242
3243     ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
3244
3245     if (ret == MMSYSERR_NOERROR) {
3246         MIXERCAPSA micA;
3247         micA.wMid           = micW.wMid;
3248         micA.wPid           = micW.wPid;
3249         micA.vDriverVersion = micW.vDriverVersion;
3250         WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
3251                              sizeof(micA.szPname), NULL, NULL );
3252         micA.fdwSupport     = micW.fdwSupport;
3253         micA.cDestinations  = micW.cDestinations;
3254         memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
3255     }
3256     return ret;
3257 }
3258
3259 /**************************************************************************
3260  *                              mixerGetDevCapsW                [WINMM.@]
3261  */
3262 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
3263 {
3264     WINMM_MMDevice *mmdevice;
3265     MIXERCAPSW caps;
3266
3267     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3268
3269     if(!WINMM_StartDevicesThread())
3270         return MMSYSERR_ERROR;
3271
3272     if(!lpCaps)
3273         return MMSYSERR_INVALPARAM;
3274
3275     if(!uSize)
3276         return MMSYSERR_NOERROR;
3277
3278     if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3279         return MMSYSERR_BADDEVICEID;
3280
3281     if(uDeviceID < g_outmmdevices_count){
3282         mmdevice = &g_out_mmdevices[uDeviceID];
3283         memcpy(caps.szPname, mmdevice->out_caps.szPname, sizeof(caps.szPname));
3284     }else{
3285         mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3286         memcpy(caps.szPname, mmdevice->in_caps.szPname, sizeof(caps.szPname));
3287     }
3288
3289     caps.wMid = 0xFF;
3290     caps.wPid = 0xFF;
3291     caps.vDriverVersion = 0x00010001;
3292     caps.fdwSupport = 0;
3293     caps.cDestinations = 1;
3294
3295     memcpy(lpCaps, &caps, uSize);
3296
3297     return MMSYSERR_NOERROR;
3298 }
3299
3300 /**************************************************************************
3301  *                              mixerOpen                       [WINMM.@]
3302  */
3303 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
3304                       DWORD_PTR dwInstance, DWORD fdwOpen)
3305 {
3306     WINMM_MMDevice *mmdevice;
3307     MMRESULT mr;
3308
3309     TRACE("(%p, %d, %lx, %lx, %x)\n", lphMix, uDeviceID, dwCallback,
3310             dwInstance, fdwOpen);
3311
3312     if(!WINMM_StartDevicesThread())
3313         return MMSYSERR_ERROR;
3314
3315     if(!lphMix)
3316         return MMSYSERR_INVALPARAM;
3317
3318     mr = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
3319     if(mr != MMSYSERR_NOERROR)
3320         return mr;
3321
3322     if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3323         return MMSYSERR_BADDEVICEID;
3324
3325     if(uDeviceID < g_outmmdevices_count){
3326         mmdevice = &g_out_mmdevices[uDeviceID];
3327         *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID, TRUE,
3328                 mmdevice->mixer_count);
3329     }else{
3330         mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3331         *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID - g_outmmdevices_count,
3332                 FALSE, mmdevice->mixer_count);
3333     }
3334
3335     ++mmdevice->mixer_count;
3336
3337     return MMSYSERR_NOERROR;
3338 }
3339
3340 /**************************************************************************
3341  *                              mixerClose                      [WINMM.@]
3342  */
3343 UINT WINAPI mixerClose(HMIXER hMix)
3344 {
3345     TRACE("(%p)\n", hMix);
3346
3347     return MMSYSERR_NOERROR;
3348 }
3349
3350 /**************************************************************************
3351  *                              mixerGetID                      [WINMM.@]
3352  */
3353 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
3354 {
3355     WINMM_MMDevice *mmdevice;
3356
3357     TRACE("(%p, %p, %x)\n", hmix, lpid, fdwID);
3358
3359     if(!WINMM_StartDevicesThread())
3360         return MMSYSERR_ERROR;
3361
3362     if(!lpid)
3363         return MMSYSERR_INVALPARAM;
3364
3365     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwID, lpid);
3366     if(!mmdevice)
3367         return MMSYSERR_INVALHANDLE;
3368
3369     if(mmdevice->in_caps.szPname[0] != '\0')
3370         *lpid += g_outmmdevices_count;
3371
3372     return MMSYSERR_NOERROR;
3373 }
3374
3375 /**************************************************************************
3376  *                              mixerGetControlDetailsW         [WINMM.@]
3377  */
3378 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
3379                                     DWORD fdwDetails)
3380 {
3381     WINMM_ControlDetails details;
3382
3383     TRACE("(%p, %p, %x)\n", hmix, lpmcdW, fdwDetails);
3384
3385     if(!WINMM_StartDevicesThread())
3386         return MMSYSERR_ERROR;
3387
3388     if(!lpmcdW)
3389         return MMSYSERR_INVALPARAM;
3390
3391     TRACE("dwControlID: %u\n", lpmcdW->dwControlID);
3392
3393     details.hmix = hmix;
3394     details.details = lpmcdW;
3395     details.flags = fdwDetails;
3396
3397     return SendMessageW(g_devices_hwnd, MXDM_GETCONTROLDETAILS,
3398             (DWORD_PTR)&details, 0);
3399 }
3400
3401 /**************************************************************************
3402  *                              mixerGetControlDetailsA [WINMM.@]
3403  */
3404 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
3405                                     DWORD fdwDetails)
3406 {
3407     UINT ret = MMSYSERR_NOTSUPPORTED;
3408
3409     TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
3410
3411     if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
3412         return MMSYSERR_INVALPARAM;
3413
3414     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
3415     case MIXER_GETCONTROLDETAILSF_VALUE:
3416         /* can safely use A structure as it is, no string inside */
3417         ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3418         break;
3419     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
3420         {
3421             MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = lpmcdA->paDetails;
3422             MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
3423             int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3424             unsigned int i;
3425
3426             if (lpmcdA->u.cMultipleItems != 0) {
3427                 size *= lpmcdA->u.cMultipleItems;
3428             }
3429             pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
3430             lpmcdA->paDetails = pDetailsW;
3431             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3432             /* set up lpmcd->paDetails */
3433             ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3434             /* copy from lpmcd->paDetails back to paDetailsW; */
3435             if (ret == MMSYSERR_NOERROR) {
3436                 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
3437                     pDetailsA->dwParam1 = pDetailsW->dwParam1;
3438                     pDetailsA->dwParam2 = pDetailsW->dwParam2;
3439                     WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
3440                                          pDetailsA->szName,
3441                                          sizeof(pDetailsA->szName), NULL, NULL );
3442                     pDetailsA++;
3443                     pDetailsW++;
3444                 }
3445                 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3446                 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3447             }
3448             HeapFree(GetProcessHeap(), 0, pDetailsW);
3449             lpmcdA->paDetails = pDetailsA;
3450             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
3451         }
3452         break;
3453     default:
3454         ERR("Unsupported fdwDetails=0x%08x\n", fdwDetails);
3455     }
3456
3457     return ret;
3458 }
3459
3460 /**************************************************************************
3461  *                              mixerGetLineControlsA   [WINMM.@]
3462  */
3463 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
3464                                   DWORD fdwControls)
3465 {
3466     MIXERLINECONTROLSW  mlcW;
3467     DWORD               ret;
3468     unsigned int        i;
3469
3470     TRACE("(%p, %p, %x)\n", hmix, lpmlcA, fdwControls);
3471
3472     if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
3473         lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
3474         return MMSYSERR_INVALPARAM;
3475
3476     mlcW.cbStruct = sizeof(mlcW);
3477     mlcW.dwLineID = lpmlcA->dwLineID;
3478     mlcW.u.dwControlID = lpmlcA->u.dwControlID;
3479     mlcW.u.dwControlType = lpmlcA->u.dwControlType;
3480
3481     /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
3482        the control count is assumed to be 1 - This is relied upon by a game,
3483        "Dynomite Deluze"                                                    */
3484     if (MIXER_GETLINECONTROLSF_ONEBYTYPE == (fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK)) {
3485         mlcW.cControls = 1;
3486     } else {
3487         mlcW.cControls = lpmlcA->cControls;
3488     }
3489     mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
3490     mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
3491                               mlcW.cControls * mlcW.cbmxctrl);
3492
3493     ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
3494
3495     if (ret == MMSYSERR_NOERROR) {
3496         lpmlcA->dwLineID = mlcW.dwLineID;
3497         lpmlcA->u.dwControlID = mlcW.u.dwControlID;
3498         lpmlcA->u.dwControlType = mlcW.u.dwControlType;
3499
3500         for (i = 0; i < mlcW.cControls; i++) {
3501             lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
3502             lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
3503             lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
3504             lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
3505             lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
3506             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
3507                                  lpmlcA->pamxctrl[i].szShortName,
3508                                  sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
3509             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
3510                                  lpmlcA->pamxctrl[i].szName,
3511                                  sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
3512             /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
3513              * sizeof(mlcW.pamxctrl[i].Bounds) */
3514             memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
3515                    sizeof(mlcW.pamxctrl[i].Bounds));
3516             /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
3517              * sizeof(mlcW.pamxctrl[i].Metrics) */
3518             memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
3519                    sizeof(mlcW.pamxctrl[i].Metrics));
3520         }
3521     }
3522
3523     HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
3524
3525     return ret;
3526 }
3527
3528 static UINT WINMM_GetVolumeLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3529         MIXERCONTROLW *ctl, DWORD flags)
3530 {
3531     ctl->dwControlID = (line == 0xFFFF0000) ? 0 : 2;
3532     ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
3533     ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3534     ctl->cMultipleItems = 0;
3535     lstrcpyW(ctl->szShortName, volumeW);
3536     lstrcpyW(ctl->szName, volumeW);
3537     ctl->Bounds.s1.dwMinimum = 0;
3538     ctl->Bounds.s1.dwMaximum = 0xFFFF;
3539     ctl->Metrics.cSteps = 192;
3540
3541     return MMSYSERR_NOERROR;
3542 }
3543
3544 static UINT WINMM_GetMuteLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3545         MIXERCONTROLW *ctl, DWORD flags)
3546 {
3547     ctl->dwControlID = (line == 0xFFFF0000) ? 1 : 3;
3548     ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
3549     ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3550     ctl->cMultipleItems = 0;
3551     lstrcpyW(ctl->szShortName, muteW);
3552     lstrcpyW(ctl->szName, muteW);
3553     ctl->Bounds.s1.dwMinimum = 0;
3554     ctl->Bounds.s1.dwMaximum = 1;
3555     ctl->Metrics.cSteps = 0;
3556
3557     return MMSYSERR_NOERROR;
3558 }
3559
3560 /**************************************************************************
3561  *                              mixerGetLineControlsW           [WINMM.@]
3562  */
3563 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
3564                                   DWORD fdwControls)
3565 {
3566     WINMM_MMDevice *mmdevice;
3567
3568     TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
3569
3570     if(!WINMM_StartDevicesThread())
3571         return MMSYSERR_ERROR;
3572
3573     if(fdwControls & ~(MIXER_GETLINECONTROLSF_ALL |
3574                 MIXER_GETLINECONTROLSF_ONEBYID |
3575                 MIXER_GETLINECONTROLSF_ONEBYTYPE |
3576                 MIXER_OBJECTF_HMIXER |
3577                 MIXER_OBJECTF_MIXER)){
3578         WARN("Unknown GetLineControls flag: %x\n", fdwControls);
3579         return MMSYSERR_INVALFLAG;
3580     }
3581
3582     if(!lpmlcW || lpmlcW->cbStruct < sizeof(*lpmlcW) || !lpmlcW->pamxctrl)
3583         return MMSYSERR_INVALPARAM;
3584
3585     TRACE("dwLineID: %u\n", lpmlcW->dwLineID);
3586     TRACE("dwControl: %x\n", lpmlcW->u.dwControlID);
3587     TRACE("cControls: %u\n", lpmlcW->cControls);
3588
3589     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwControls, NULL);
3590     if(!mmdevice)
3591         return MMSYSERR_INVALHANDLE;
3592
3593     switch(fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK){
3594     case MIXER_GETLINECONTROLSF_ALL:
3595         if(lpmlcW->cControls != 2)
3596             return MMSYSERR_INVALPARAM;
3597         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3598             return MMSYSERR_INVALPARAM;
3599         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3600             return MIXERR_INVALLINE;
3601         WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3602                 &lpmlcW->pamxctrl[0], fdwControls);
3603         WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3604                 &lpmlcW->pamxctrl[1], fdwControls);
3605         return MMSYSERR_NOERROR;
3606     case MIXER_GETLINECONTROLSF_ONEBYID:
3607         if(lpmlcW->cControls != 1)
3608             return MMSYSERR_INVALPARAM;
3609         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3610             return MMSYSERR_INVALPARAM;
3611         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3612             return MIXERR_INVALLINE;
3613         if(lpmlcW->u.dwControlID == 0)
3614             return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3615                     lpmlcW->pamxctrl, fdwControls);
3616         if(lpmlcW->u.dwControlID == 1)
3617             return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3618                     lpmlcW->pamxctrl, fdwControls);
3619         return MMSYSERR_NOTSUPPORTED;
3620     case MIXER_GETLINECONTROLSF_ONEBYTYPE:
3621         if(lpmlcW->cControls != 1)
3622             return MMSYSERR_INVALPARAM;
3623         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3624             return MMSYSERR_INVALPARAM;
3625         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3626             return MIXERR_INVALLINE;
3627         if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
3628             return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3629                     lpmlcW->pamxctrl, fdwControls);
3630         if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
3631             return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3632                     lpmlcW->pamxctrl, fdwControls);
3633         return MMSYSERR_NOTSUPPORTED;
3634     }
3635
3636     return MMSYSERR_NOTSUPPORTED;
3637 }
3638
3639 static UINT WINMM_GetSourceLineInfo(WINMM_MMDevice *mmdevice, UINT mmdev_index,
3640         MIXERLINEW *info, DWORD flags)
3641 {
3642     BOOL is_out = TRUE;
3643     if(mmdevice->in_caps.szPname[0] != '\0')
3644         is_out = FALSE;
3645
3646     if(info->dwSource != 0)
3647         return MIXERR_INVALLINE;
3648
3649     info->dwDestination = 0;
3650     info->dwLineID = 0;
3651     info->fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
3652     info->cConnections = 0;
3653     info->cControls = 2;
3654     /* volume & mute always affect all channels, so claim 1 channel */
3655     info->cChannels = 1;
3656     info->Target.dwDeviceID = mmdev_index;
3657     info->Target.wMid = ~0;
3658     info->Target.wPid = ~0;
3659     info->Target.vDriverVersion = 0;
3660
3661     lstrcpyW(info->szShortName, volumeW);
3662     lstrcpyW(info->szName, mastervolumeW);
3663
3664     if(is_out){
3665         info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
3666         info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
3667         memcpy(info->Target.szPname, mmdevice->out_caps.szPname,
3668                 sizeof(info->Target.szPname));
3669     }else{
3670         info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
3671         info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3672         info->Target.szPname[0] = '\0';
3673     }
3674
3675     return MMSYSERR_NOERROR;
3676 }
3677
3678 static UINT WINMM_GetDestinationLineInfo(WINMM_MMDevice *mmdevice,
3679         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3680 {
3681     BOOL is_out = TRUE;
3682     if(mmdevice->in_caps.szPname[0] != '\0')
3683         is_out = FALSE;
3684
3685     if(info->dwDestination != 0)
3686         return MIXERR_INVALLINE;
3687
3688     info->dwSource = 0xFFFFFFFF;
3689     info->dwLineID = 0xFFFF0000;
3690     info->fdwLine = MIXERLINE_LINEF_ACTIVE;
3691     info->cConnections = 1;
3692     info->cControls = 2;
3693
3694     lstrcpyW(info->szShortName, volumeW);
3695     lstrcpyW(info->szName, mastervolumeW);
3696
3697     info->Target.dwDeviceID = mmdev_index;
3698     info->Target.wMid = ~0;
3699     info->Target.wPid = ~0;
3700     info->Target.vDriverVersion = 0;
3701
3702     if(is_out){
3703         info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
3704         info->cChannels = mmdevice->out_caps.wChannels;
3705         info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3706         info->Target.szPname[0] = '\0';
3707     }else{
3708         info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
3709         info->cChannels = mmdevice->in_caps.wChannels;
3710         info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
3711         memcpy(info->Target.szPname, mmdevice->in_caps.szPname,
3712                 sizeof(info->Target.szPname));
3713     }
3714
3715     return MMSYSERR_NOERROR;
3716 }
3717
3718 static UINT WINMM_GetComponentTypeLineInfo(WINMM_MMDevice *mmdevice,
3719         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3720 {
3721     BOOL is_out = TRUE;
3722     if(mmdevice->in_caps.szPname[0] != '\0')
3723         is_out = FALSE;
3724
3725     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN){
3726         if(is_out)
3727             return MIXERR_INVALLINE;
3728         info->dwDestination = 0;
3729         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3730     }
3731
3732     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS){
3733         if(!is_out)
3734             return MIXERR_INVALLINE;
3735         info->dwDestination = 0;
3736         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3737     }
3738
3739     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE){
3740         if(is_out)
3741             return MIXERR_INVALLINE;
3742         info->dwSource = 0;
3743         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3744     }
3745
3746     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
3747         if(!is_out)
3748             return MIXERR_INVALLINE;
3749         info->dwSource = 0;
3750         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3751     }
3752
3753     TRACE("Returning INVALLINE on this component type: %u\n",
3754             info->dwComponentType);
3755
3756     return MIXERR_INVALLINE;
3757 }
3758
3759 static UINT WINMM_GetLineIDLineInfo(WINMM_MMDevice *mmdevice,
3760         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3761 {
3762     if(info->dwLineID == 0xFFFF0000){
3763         info->dwDestination = 0;
3764         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3765     }
3766
3767     if(info->dwLineID == 0){
3768         info->dwSource = 0;
3769         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3770     }
3771
3772     TRACE("Returning INVALLINE on this dwLineID: %u\n", info->dwLineID);
3773     return MIXERR_INVALLINE;
3774 }
3775
3776 /**************************************************************************
3777  *                              mixerGetLineInfoW               [WINMM.@]
3778  */
3779 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
3780 {
3781     UINT mmdev_index;
3782     WINMM_MMDevice *mmdevice;
3783
3784     TRACE("(%p, %p, %x)\n", hmix, lpmliW, fdwInfo);
3785
3786     if(!WINMM_StartDevicesThread())
3787         return MMSYSERR_ERROR;
3788
3789     if(!lpmliW || lpmliW->cbStruct < sizeof(MIXERLINEW))
3790         return MMSYSERR_INVALPARAM;
3791
3792     TRACE("dwDestination: %u\n", lpmliW->dwDestination);
3793     TRACE("dwSource: %u\n", lpmliW->dwSource);
3794     TRACE("dwLineID: %u\n", lpmliW->dwLineID);
3795     TRACE("fdwLine: 0x%x\n", lpmliW->fdwLine);
3796     TRACE("dwComponentType: 0x%x\n", lpmliW->dwComponentType);
3797
3798     if(fdwInfo & ~(MIXER_GETLINEINFOF_COMPONENTTYPE |
3799                 MIXER_GETLINEINFOF_DESTINATION |
3800                 MIXER_GETLINEINFOF_LINEID |
3801                 MIXER_GETLINEINFOF_SOURCE |
3802                 MIXER_GETLINEINFOF_TARGETTYPE |
3803                 MIXER_OBJECTF_HMIXER |
3804                 MIXER_OBJECTF_MIXER)){
3805         WARN("Unknown GetLineInfo flag: %x\n", fdwInfo);
3806         return MMSYSERR_INVALFLAG;
3807     }
3808
3809     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwInfo, &mmdev_index);
3810     if(!mmdevice)
3811         return MMSYSERR_INVALHANDLE;
3812
3813     switch(fdwInfo & MIXER_GETLINEINFOF_QUERYMASK){
3814     case MIXER_GETLINEINFOF_DESTINATION:
3815         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, lpmliW,
3816                 fdwInfo);
3817     case MIXER_GETLINEINFOF_SOURCE:
3818         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3819     case MIXER_GETLINEINFOF_COMPONENTTYPE:
3820         return WINMM_GetComponentTypeLineInfo(mmdevice, mmdev_index, lpmliW,
3821                 fdwInfo);
3822     case MIXER_GETLINEINFOF_LINEID:
3823         return WINMM_GetLineIDLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3824     case MIXER_GETLINEINFOF_TARGETTYPE:
3825         FIXME("TARGETTYPE flag not implemented!\n");
3826         return MIXERR_INVALLINE;
3827     }
3828
3829     TRACE("Returning INVALFLAG on these flags: %lx\n",
3830             fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
3831     return MMSYSERR_INVALFLAG;
3832 }
3833
3834 /**************************************************************************
3835  *                              mixerGetLineInfoA               [WINMM.@]
3836  */
3837 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
3838                               DWORD fdwInfo)
3839 {
3840     MIXERLINEW          mliW;
3841     UINT                ret;
3842
3843     TRACE("(%p, %p, %x)\n", hmix, lpmliA, fdwInfo);
3844
3845     if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
3846         return MMSYSERR_INVALPARAM;
3847
3848     mliW.cbStruct = sizeof(mliW);
3849     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
3850     case MIXER_GETLINEINFOF_COMPONENTTYPE:
3851         mliW.dwComponentType = lpmliA->dwComponentType;
3852         break;
3853     case MIXER_GETLINEINFOF_DESTINATION:
3854         mliW.dwDestination = lpmliA->dwDestination;
3855         break;
3856     case MIXER_GETLINEINFOF_LINEID:
3857         mliW.dwLineID = lpmliA->dwLineID;
3858         break;
3859     case MIXER_GETLINEINFOF_SOURCE:
3860         mliW.dwDestination = lpmliA->dwDestination;
3861         mliW.dwSource = lpmliA->dwSource;
3862         break;
3863     case MIXER_GETLINEINFOF_TARGETTYPE:
3864         mliW.Target.dwType = lpmliA->Target.dwType;
3865         mliW.Target.wMid = lpmliA->Target.wMid;
3866         mliW.Target.wPid = lpmliA->Target.wPid;
3867         mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
3868         MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname)/sizeof(WCHAR));
3869         break;
3870     default:
3871         WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
3872         return MMSYSERR_INVALFLAG;
3873     }
3874
3875     ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
3876
3877     if(ret == MMSYSERR_NOERROR)
3878     {
3879         lpmliA->dwDestination = mliW.dwDestination;
3880         lpmliA->dwSource = mliW.dwSource;
3881         lpmliA->dwLineID = mliW.dwLineID;
3882         lpmliA->fdwLine = mliW.fdwLine;
3883         lpmliA->dwUser = mliW.dwUser;
3884         lpmliA->dwComponentType = mliW.dwComponentType;
3885         lpmliA->cChannels = mliW.cChannels;
3886         lpmliA->cConnections = mliW.cConnections;
3887         lpmliA->cControls = mliW.cControls;
3888         WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
3889                              sizeof(lpmliA->szShortName), NULL, NULL);
3890         WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
3891                              sizeof(lpmliA->szName), NULL, NULL );
3892         lpmliA->Target.dwType = mliW.Target.dwType;
3893         lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
3894         lpmliA->Target.wMid = mliW.Target.wMid;
3895         lpmliA->Target.wPid = mliW.Target.wPid;
3896         lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
3897         WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
3898                              sizeof(lpmliA->Target.szPname), NULL, NULL );
3899     }
3900     return ret;
3901 }
3902
3903 /**************************************************************************
3904  *                              mixerSetControlDetails  [WINMM.@]
3905  */
3906 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
3907                                    DWORD fdwDetails)
3908 {
3909     WINMM_ControlDetails details;
3910
3911     TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
3912
3913     if(!WINMM_StartDevicesThread())
3914         return MMSYSERR_ERROR;
3915
3916     if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
3917             MIXER_SETCONTROLDETAILSF_CUSTOM)
3918         return MMSYSERR_NOTSUPPORTED;
3919
3920     if(!lpmcd)
3921         return MMSYSERR_INVALPARAM;
3922
3923     TRACE("dwControlID: %u\n", lpmcd->dwControlID);
3924
3925     details.hmix = hmix;
3926     details.details = lpmcd;
3927     details.flags = fdwDetails;
3928
3929     return SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
3930             (DWORD_PTR)&details, 0);
3931 }
3932
3933 /**************************************************************************
3934  *                              mixerMessage            [WINMM.@]
3935  */
3936 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3937 {
3938     TRACE("(%p, %d, %lx, %lx)\n", hmix, uMsg, dwParam1, dwParam2);
3939
3940     return MMSYSERR_NOTSUPPORTED;
3941 }