dinput: Stub IDirectInputJoyConfig8 interface.
[wine] / dlls / winmm / waveform.c
1 /*
2  * Copyright 1993      Martin Ayotte
3  *           1998-2002 Eric Pouech
4  *           2011 Andrew Eikum for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
24
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
27 #define COBJMACROS
28 #include "windef.h"
29 #include "winbase.h"
30 #include "wingdi.h"
31 #include "mmsystem.h"
32 #include "mmreg.h"
33 #include "msacm.h"
34 #include "winuser.h"
35 #include "winnls.h"
36 #include "winternl.h"
37
38 #include "winemm.h"
39
40 #include "ole2.h"
41 #include "initguid.h"
42 #include "devpkey.h"
43 #include "mmdeviceapi.h"
44 #include "audioclient.h"
45 #include "audiopolicy.h"
46
47 #include "wine/debug.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
50
51 /* FIXME: Should be localized */
52 static const WCHAR volumeW[] = {'V','o','l','u','m','e',0};
53 static const WCHAR mastervolumeW[] = {'M','a','s','t','e','r',' ','V','o','l',
54     'u','m','e',0};
55 static const WCHAR muteW[] = {'M','u','t','e',0};
56
57 /* HWAVE (and HMIXER) format:
58  *
59  * XXXX... 1FDD DDDD IIII IIII
60  * X = unused (must be 0)
61  * 1 = the bit is set to 1, to avoid all-zero HWAVEs
62  * F = flow direction (0 = IN, 1 = OUT)
63  * D = index into g_out_mmdevices
64  * I = index in the mmdevice's devices array
65  *
66  * Two reasons that we don't just use pointers:
67  *   - HWAVEs must fit into 16 bits for compatibility with old applications.
68  *   - We must be able to identify bad devices without crashing.
69  */
70
71 #define MAX_DEVICES 256
72
73 typedef struct _WINMM_CBInfo {
74     DWORD_PTR callback;
75     DWORD_PTR user;
76     DWORD flags;
77     HWAVE hwave;
78 } WINMM_CBInfo;
79
80 struct _WINMM_MMDevice;
81 typedef struct _WINMM_MMDevice WINMM_MMDevice;
82
83 typedef struct _WINMM_Device {
84     WINMM_CBInfo cb_info;
85
86     HWAVE handle;
87
88     BOOL open;
89
90     IMMDevice *device;
91     IAudioClient *client;
92     IAudioRenderClient *render;
93     IAudioCaptureClient *capture;
94     IAudioClock *clock;
95     IAudioStreamVolume *volume;
96
97     HACMSTREAM acm_handle;
98     ACMSTREAMHEADER acm_hdr;
99     UINT32 acm_offs;
100
101     WAVEHDR *first, *last, *playing, *loop_start;
102
103     BOOL stopped;
104     DWORD loop_counter;
105     UINT32 bytes_per_frame, samples_per_sec, ofs_bytes, played_frames;
106
107     /* stored in frames of sample rate, *not* AC::GetFrequency */
108     UINT64 last_clock_pos;
109
110     HANDLE event;
111     CRITICAL_SECTION lock;
112
113     WINMM_MMDevice *parent;
114 } WINMM_Device;
115
116 struct _WINMM_MMDevice {
117     WAVEOUTCAPSW out_caps; /* must not be modified outside of WINMM_InitMMDevices*/
118     WAVEINCAPSW in_caps; /* must not be modified outside of WINMM_InitMMDevices*/
119     WCHAR *dev_id;
120
121     ISimpleAudioVolume *volume;
122
123     GUID session;
124
125     /* HMIXER format is the same as the HWAVE format, but the I bits are
126      * replaced by the value of this counter, to keep each HMIXER unique */
127     UINT mixer_count;
128
129     CRITICAL_SECTION lock;
130
131     WINMM_Device *devices[MAX_DEVICES];
132 };
133
134 static WINMM_MMDevice *g_out_mmdevices;
135 static UINT g_outmmdevices_count;
136
137 static WINMM_MMDevice *g_in_mmdevices;
138 static UINT g_inmmdevices_count;
139
140 static IMMDeviceEnumerator *g_devenum;
141
142 static CRITICAL_SECTION g_devthread_lock;
143 static CRITICAL_SECTION_DEBUG g_devthread_lock_debug =
144 {
145     0, 0, &g_devthread_lock,
146     { &g_devthread_lock_debug.ProcessLocksList, &g_devthread_lock_debug.ProcessLocksList },
147       0, 0, { (DWORD_PTR)(__FILE__ ": g_devthread_lock") }
148 };
149 static CRITICAL_SECTION g_devthread_lock = { &g_devthread_lock_debug, -1, 0, 0, 0, 0 };
150 static HANDLE g_devices_thread;
151 static HWND g_devices_hwnd;
152
153 static UINT g_devhandle_count;
154 static HANDLE *g_device_handles;
155 static WINMM_Device **g_handle_devices;
156
157 typedef struct _WINMM_OpenInfo {
158     HWAVE handle;
159     UINT req_device;
160     WAVEFORMATEX *format;
161     DWORD_PTR callback;
162     DWORD_PTR cb_user;
163     DWORD flags;
164 } WINMM_OpenInfo;
165
166 typedef struct _WINMM_ControlDetails {
167     HMIXEROBJ hmix;
168     MIXERCONTROLDETAILS *details;
169     DWORD flags;
170 } WINMM_ControlDetails;
171
172 static LRESULT WOD_Open(WINMM_OpenInfo *info);
173 static LRESULT WOD_Close(HWAVEOUT hwave);
174 static LRESULT WID_Open(WINMM_OpenInfo *info);
175 static LRESULT WID_Close(HWAVEIN hwave);
176
177 void WINMM_DeleteWaveform(void)
178 {
179     /* FIXME: Free g_(in,out)_mmdevices? */
180     DeleteCriticalSection(&g_devthread_lock);
181 }
182
183 static inline HWAVE WINMM_MakeHWAVE(UINT mmdevice, BOOL is_out, UINT device)
184 {
185     return ULongToHandle((1 << 15) | ((!!is_out) << 14) |
186             (mmdevice << 8) | device);
187 }
188
189 static inline void WINMM_DecomposeHWAVE(HWAVE hwave, UINT *mmdevice_index,
190         BOOL *is_out, UINT *device_index, UINT *junk)
191 {
192     ULONG32 l = HandleToULong(hwave);
193     *device_index = l & 0xFF;
194     *mmdevice_index = (l >> 8) & 0x3F;
195     *is_out = (l >> 14) & 0x1;
196     *junk = l >> 15;
197 }
198
199 static void WINMM_InitDevice(WINMM_Device *device,
200         WINMM_MMDevice *parent, HWAVE hwave)
201 {
202     InitializeCriticalSection(&device->lock);
203     device->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM_Device.lock");
204
205     device->handle = hwave;
206     device->parent = parent;
207 }
208
209 /* finds the first unused Device, marks it as "open", and returns
210  * a pointer to the device
211  *
212  * IMPORTANT: it is the caller's responsibility to release the device's lock
213  * on success
214  */
215 static WINMM_Device *WINMM_FindUnusedDevice(BOOL is_out, UINT mmdevice_index)
216 {
217     WINMM_MMDevice *mmdevice;
218     UINT i;
219
220     if(is_out)
221         mmdevice = &g_out_mmdevices[mmdevice_index];
222     else
223         mmdevice = &g_in_mmdevices[mmdevice_index];
224
225     EnterCriticalSection(&mmdevice->lock);
226     for(i = 0; i < MAX_DEVICES; ++i){
227         WINMM_Device *device = mmdevice->devices[i];
228
229         if(!device){
230             device = mmdevice->devices[i] = HeapAlloc(GetProcessHeap(),
231                     HEAP_ZERO_MEMORY, sizeof(WINMM_Device));
232             if(!device){
233                 LeaveCriticalSection(&mmdevice->lock);
234                 return NULL;
235             }
236
237             WINMM_InitDevice(device, mmdevice,
238                     WINMM_MakeHWAVE(mmdevice_index, is_out, i));
239             EnterCriticalSection(&device->lock);
240         }else
241             EnterCriticalSection(&device->lock);
242
243         if(!device->open){
244             LeaveCriticalSection(&mmdevice->lock);
245             device->open = TRUE;
246             TRACE("Found free device: mmdevice: %u, device id: %u\n",
247                     mmdevice_index, i);
248             return device;
249         }
250
251         LeaveCriticalSection(&device->lock);
252     }
253
254     LeaveCriticalSection(&mmdevice->lock);
255
256     TRACE("All devices in use: mmdevice: %u\n", mmdevice_index);
257
258     return NULL;
259 }
260
261 static inline BOOL WINMM_ValidateAndLock(WINMM_Device *device)
262 {
263     if(!device)
264         return FALSE;
265
266     EnterCriticalSection(&device->lock);
267
268     if(!device->open){
269         LeaveCriticalSection(&device->lock);
270         return FALSE;
271     }
272
273     return TRUE;
274 }
275
276 static WINMM_Device *WINMM_GetDeviceFromHWAVE(HWAVE hwave)
277 {
278     WINMM_MMDevice *mmdevice;
279     WINMM_Device *device;
280     UINT mmdevice_index, device_index, junk;
281     BOOL is_out;
282
283     WINMM_DecomposeHWAVE(hwave, &mmdevice_index, &is_out, &device_index, &junk);
284
285     if(junk != 0x1)
286         return NULL;
287
288     if(mmdevice_index >= (is_out ? g_outmmdevices_count : g_inmmdevices_count))
289         return NULL;
290
291     if(is_out)
292         mmdevice = &g_out_mmdevices[mmdevice_index];
293     else
294         mmdevice = &g_in_mmdevices[mmdevice_index];
295
296     EnterCriticalSection(&mmdevice->lock);
297
298     device = mmdevice->devices[device_index];
299
300     LeaveCriticalSection(&mmdevice->lock);
301
302     return device;
303 }
304
305 /* Note: NotifyClient should never be called while holding the device lock
306  * since the client may call wave* functions from within the callback. */
307 static inline void WINMM_NotifyClient(WINMM_CBInfo *info, WORD msg, DWORD_PTR param1,
308         DWORD_PTR param2)
309 {
310     DriverCallback(info->callback, info->flags, (HDRVR)info->hwave,
311         msg, info->user, param1, param2);
312 }
313
314 static HRESULT WINMM_GetFriendlyName(IMMDevice *device, WCHAR *out,
315         UINT outlen)
316 {
317     IPropertyStore *ps;
318     PROPVARIANT var;
319     HRESULT hr;
320
321     hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
322     if(FAILED(hr))
323         return hr;
324
325     PropVariantInit(&var);
326
327     hr = IPropertyStore_GetValue(ps,
328             (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &var);
329     if(FAILED(hr)){
330         IPropertyStore_Release(ps);
331         return hr;
332     }
333
334     lstrcpynW(out, var.u.pwszVal, outlen);
335
336     PropVariantClear(&var);
337
338     IPropertyStore_Release(ps);
339
340     return S_OK;
341 }
342
343 static HRESULT WINMM_TestFormat(IAudioClient *client, DWORD rate, DWORD depth,
344         WORD channels)
345 {
346     WAVEFORMATEX fmt, *junk;
347     HRESULT hr;
348
349     fmt.wFormatTag = WAVE_FORMAT_PCM;
350     fmt.nChannels = channels;
351     fmt.nSamplesPerSec = rate;
352     fmt.wBitsPerSample = depth;
353     fmt.nBlockAlign = (channels * depth) / 8;
354     fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
355     fmt.cbSize = 0;
356
357     hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED,
358             &fmt, &junk);
359     if(SUCCEEDED(hr))
360         CoTaskMemFree(junk);
361
362     return hr;
363 }
364
365 static struct _TestFormat {
366     DWORD flag;
367     DWORD rate;
368     DWORD depth;
369     WORD channels;
370 } formats_to_test[] = {
371     { WAVE_FORMAT_1M08, 11025, 8, 1 },
372     { WAVE_FORMAT_1M16, 11025, 16, 1 },
373     { WAVE_FORMAT_1S08, 11025, 8, 2 },
374     { WAVE_FORMAT_1S16, 11025, 16, 2 },
375     { WAVE_FORMAT_2M08, 22050, 8, 1 },
376     { WAVE_FORMAT_2M16, 22050, 16, 1 },
377     { WAVE_FORMAT_2S08, 22050, 8, 2 },
378     { WAVE_FORMAT_2S16, 22050, 16, 2 },
379     { WAVE_FORMAT_4M08, 44100, 8, 1 },
380     { WAVE_FORMAT_4M16, 44100, 16, 1 },
381     { WAVE_FORMAT_4S08, 44100, 8, 2 },
382     { WAVE_FORMAT_4S16, 44100, 16, 2 },
383     { WAVE_FORMAT_48M08, 48000, 8, 1 },
384     { WAVE_FORMAT_48M16, 48000, 16, 1 },
385     { WAVE_FORMAT_48S08, 48000, 8, 2 },
386     { WAVE_FORMAT_48S16, 48000, 16, 2 },
387     { WAVE_FORMAT_96M08, 96000, 8, 1 },
388     { WAVE_FORMAT_96M16, 96000, 16, 1 },
389     { WAVE_FORMAT_96S08, 96000, 8, 2 },
390     { WAVE_FORMAT_96S16, 96000, 16, 2 },
391     {0}
392 };
393
394 static DWORD WINMM_GetSupportedFormats(IMMDevice *device)
395 {
396     DWORD flags = 0;
397     HRESULT hr;
398     struct _TestFormat *fmt;
399     IAudioClient *client;
400
401     hr = IMMDevice_Activate(device, &IID_IAudioClient,
402             CLSCTX_INPROC_SERVER, NULL, (void**)&client);
403     if(FAILED(hr))
404         return 0;
405
406     for(fmt = formats_to_test; fmt->flag; ++fmt){
407         hr = WINMM_TestFormat(client, fmt->rate, fmt->depth, fmt->channels);
408         if(hr == S_OK)
409             flags |= fmt->flag;
410     }
411
412     IAudioClient_Release(client);
413
414     return flags;
415 }
416
417 static HRESULT WINMM_InitMMDevice(EDataFlow flow, IMMDevice *device,
418         WINMM_MMDevice *dev, UINT index)
419 {
420     HRESULT hr;
421
422     if(flow == eRender){
423         dev->out_caps.wMid = 0xFF;
424         dev->out_caps.wPid = 0xFF;
425         dev->out_caps.vDriverVersion = 0x00010001;
426         dev->out_caps.dwFormats = WINMM_GetSupportedFormats(device);
427         dev->out_caps.wReserved1 = 0;
428         dev->out_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
429             WAVECAPS_SAMPLEACCURATE;
430         dev->out_caps.wChannels = 2;
431         dev->out_caps.szPname[0] = '\0';
432
433         hr = WINMM_GetFriendlyName(device, dev->out_caps.szPname,
434                 sizeof(dev->out_caps.szPname) /
435                 sizeof(*dev->out_caps.szPname));
436         if(FAILED(hr))
437             return hr;
438     }else{
439         dev->in_caps.wMid = 0xFF;
440         dev->in_caps.wPid = 0xFF;
441         dev->in_caps.vDriverVersion = 0x00010001;
442         dev->in_caps.dwFormats = WINMM_GetSupportedFormats(device);
443         dev->in_caps.wReserved1 = 0;
444         dev->in_caps.wChannels = 2;
445         dev->in_caps.szPname[0] = '\0';
446
447         hr = WINMM_GetFriendlyName(device, dev->in_caps.szPname,
448                 sizeof(dev->in_caps.szPname) /
449                 sizeof(*dev->in_caps.szPname));
450         if(FAILED(hr))
451             return hr;
452     }
453
454     hr = IMMDevice_GetId(device, &dev->dev_id);
455     if(FAILED(hr))
456         return hr;
457
458     CoCreateGuid(&dev->session);
459
460     InitializeCriticalSection(&dev->lock);
461     dev->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM_Device.lock");
462
463     return S_OK;
464 }
465
466 static HRESULT WINMM_EnumDevices(WINMM_MMDevice **devices, UINT *devcount,
467         EDataFlow flow)
468 {
469     IMMDeviceCollection *devcoll;
470     HRESULT hr;
471
472     hr = IMMDeviceEnumerator_EnumAudioEndpoints(g_devenum, flow,
473             DEVICE_STATE_ACTIVE, &devcoll);
474     if(FAILED(hr))
475         return hr;
476
477     hr = IMMDeviceCollection_GetCount(devcoll, devcount);
478     if(FAILED(hr)){
479         IMMDeviceCollection_Release(devcoll);
480         return hr;
481     }
482
483     if(*devcount > 0){
484         UINT n, count;
485         IMMDevice *def_dev = NULL;
486
487         *devices = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
488                 sizeof(WINMM_MMDevice) * (*devcount));
489         if(!*devices){
490             IMMDeviceCollection_Release(devcoll);
491             return E_OUTOFMEMORY;
492         }
493
494         count = 0;
495
496         /* make sure that device 0 is the default device */
497         hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(g_devenum,
498                 flow, eConsole, &def_dev);
499         if(SUCCEEDED(hr)){
500             WINMM_InitMMDevice(flow, def_dev, &(*devices)[0], 0);
501             count = 1;
502         }
503
504         for(n = 0; n < *devcount; ++n){
505             IMMDevice *device;
506
507             hr = IMMDeviceCollection_Item(devcoll, n, &device);
508             if(SUCCEEDED(hr)){
509                 if(device != def_dev){
510                     WINMM_InitMMDevice(flow, device, &(*devices)[count], count);
511                     ++count;
512                 }
513
514                 IMMDevice_Release(device);
515             }
516         }
517
518         if(def_dev)
519             IMMDevice_Release(def_dev);
520
521         *devcount = count;
522     }
523
524     IMMDeviceCollection_Release(devcoll);
525
526     return S_OK;
527 }
528
529 static HRESULT WINMM_InitMMDevices(void)
530 {
531     HRESULT hr;
532
533     hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
534             CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&g_devenum);
535     if(FAILED(hr))
536         return hr;
537
538     hr = WINMM_EnumDevices(&g_out_mmdevices, &g_outmmdevices_count, eRender);
539     if(FAILED(hr)){
540         g_outmmdevices_count = 0;
541         g_inmmdevices_count = 0;
542         return hr;
543     }
544
545     hr = WINMM_EnumDevices(&g_in_mmdevices, &g_inmmdevices_count, eCapture);
546     if(FAILED(hr)){
547         g_inmmdevices_count = 0;
548         return hr;
549     }
550
551     return S_OK;
552 }
553
554 static inline BOOL WINMM_IsMapper(UINT device)
555 {
556     return (device == WAVE_MAPPER || device == (UINT16)WAVE_MAPPER);
557 }
558
559 static MMRESULT WINMM_TryDeviceMapping(WINMM_OpenInfo *info, WORD channels,
560         DWORD freq, DWORD bits_per_samp, BOOL is_out)
561 {
562     WINMM_Device *device;
563     WAVEFORMATEX target;
564     MMRESULT mr;
565     UINT i;
566
567     TRACE("format: %u, channels: %u, sample rate: %u, bit depth: %u\n",
568             WAVE_FORMAT_PCM, channels, freq, bits_per_samp);
569
570     target.wFormatTag = WAVE_FORMAT_PCM;
571     target.nChannels = channels;
572     target.nSamplesPerSec = freq;
573     target.wBitsPerSample = bits_per_samp;
574     target.nBlockAlign = (target.nChannels * target.wBitsPerSample) / 8;
575     target.nAvgBytesPerSec = target.nSamplesPerSec * target.nBlockAlign;
576     target.cbSize = 0;
577
578     if(is_out)
579         mr = acmStreamOpen(NULL, NULL, info->format, &target, NULL, 0,
580                 0, ACM_STREAMOPENF_QUERY);
581     else
582         mr = acmStreamOpen(NULL, NULL, &target, info->format, NULL, 0,
583                 0, ACM_STREAMOPENF_QUERY);
584     if(mr != MMSYSERR_NOERROR)
585         return mr;
586
587     /* ACM can convert from src->dst, so try to find a device
588      * that supports dst */
589     if(is_out){
590         if(WINMM_IsMapper(info->req_device)){
591             for(i = 0; i < g_outmmdevices_count; ++i){
592                 WINMM_OpenInfo l_info = *info;
593                 l_info.req_device = i;
594                 l_info.format = &target;
595                 mr = WOD_Open(&l_info);
596                 if(mr == MMSYSERR_NOERROR){
597                     info->handle = l_info.handle;
598                     break;
599                 }
600             }
601         }else{
602             WINMM_OpenInfo l_info = *info;
603             l_info.flags &= ~WAVE_MAPPED;
604             l_info.format = &target;
605             mr = WOD_Open(&l_info);
606             if(mr == MMSYSERR_NOERROR)
607                 info->handle = l_info.handle;
608         }
609     }else{
610         if(WINMM_IsMapper(info->req_device)){
611             for(i = 0; i < g_inmmdevices_count; ++i){
612                 WINMM_OpenInfo l_info = *info;
613                 l_info.req_device = i;
614                 l_info.format = &target;
615                 mr = WID_Open(&l_info);
616                 if(mr == MMSYSERR_NOERROR){
617                     info->handle = l_info.handle;
618                     break;
619                 }
620             }
621         }else{
622             WINMM_OpenInfo l_info = *info;
623             l_info.flags &= ~WAVE_MAPPED;
624             l_info.format = &target;
625             mr = WID_Open(&l_info);
626             if(mr == MMSYSERR_NOERROR)
627                 info->handle = l_info.handle;
628         }
629     }
630     if(mr != MMSYSERR_NOERROR)
631         return WAVERR_BADFORMAT;
632
633     device = WINMM_GetDeviceFromHWAVE(info->handle);
634     if(!device)
635         return MMSYSERR_INVALHANDLE;
636
637     /* set up the ACM stream */
638     if(is_out)
639         mr = acmStreamOpen(&device->acm_handle, NULL, info->format, &target,
640                 NULL, 0, 0, 0);
641     else
642         mr = acmStreamOpen(&device->acm_handle, NULL, &target, info->format,
643                 NULL, 0, 0, 0);
644     if(mr != MMSYSERR_NOERROR){
645         if(is_out)
646             WOD_Close((HWAVEOUT)info->handle);
647         else
648             WID_Close((HWAVEIN)info->handle);
649         return mr;
650     }
651
652     TRACE("Success\n");
653     return MMSYSERR_NOERROR;
654 }
655
656 static MMRESULT WINMM_MapDevice(WINMM_OpenInfo *info, BOOL is_out)
657 {
658     UINT i;
659     MMRESULT mr;
660     WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)info->format;
661
662     TRACE("(%p, %d)\n", info, is_out);
663
664     /* try to find a direct match */
665     if(is_out){
666         WINMM_OpenInfo l_info = *info;
667         if(WINMM_IsMapper(info->req_device)){
668             for(i = 0; i < g_outmmdevices_count; ++i){
669                 l_info.req_device = i;
670                 mr = WOD_Open(&l_info);
671                 if(mr == MMSYSERR_NOERROR){
672                     info->handle = l_info.handle;
673                     return mr;
674                 }
675             }
676         }else{
677             l_info.flags &= ~WAVE_MAPPED;
678             mr = WOD_Open(&l_info);
679             if(mr == MMSYSERR_NOERROR){
680                 info->handle = l_info.handle;
681                 return mr;
682             }
683         }
684     }else{
685         WINMM_OpenInfo l_info = *info;
686         if(WINMM_IsMapper(info->req_device)){
687             for(i = 0; i < g_inmmdevices_count; ++i){
688                 l_info.req_device = i;
689                 mr = WID_Open(&l_info);
690                 if(mr == MMSYSERR_NOERROR){
691                     info->handle = l_info.handle;
692                     return mr;
693                 }
694             }
695         }else{
696             l_info.flags &= ~WAVE_MAPPED;
697             mr = WID_Open(&l_info);
698             if(mr == MMSYSERR_NOERROR){
699                 info->handle = l_info.handle;
700                 return mr;
701             }
702         }
703     }
704
705     /* no direct match, so set up the ACM stream */
706     if(info->format->wFormatTag != WAVE_FORMAT_PCM &&
707             !(info->format->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
708               IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
709         /* convert to PCM format if it's not already */
710         mr = WINMM_TryDeviceMapping(info, info->format->nChannels,
711                 info->format->nSamplesPerSec, 16, is_out);
712         if(mr == MMSYSERR_NOERROR)
713             return mr;
714
715         mr = WINMM_TryDeviceMapping(info, info->format->nChannels,
716                 info->format->nSamplesPerSec, 8, is_out);
717         if(mr == MMSYSERR_NOERROR)
718             return mr;
719     }else{
720         WORD channels;
721
722         /* first try just changing bit depth and channels */
723         channels = info->format->nChannels;
724         mr = WINMM_TryDeviceMapping(info, channels,
725                 info->format->nSamplesPerSec, 16, is_out);
726         if(mr == MMSYSERR_NOERROR)
727             return mr;
728         mr = WINMM_TryDeviceMapping(info, channels,
729                 info->format->nSamplesPerSec, 8, is_out);
730         if(mr == MMSYSERR_NOERROR)
731             return mr;
732
733         channels = (channels == 2) ? 1 : 2;
734         mr = WINMM_TryDeviceMapping(info, channels,
735                 info->format->nSamplesPerSec, 16, is_out);
736         if(mr == MMSYSERR_NOERROR)
737             return mr;
738         mr = WINMM_TryDeviceMapping(info, channels,
739                 info->format->nSamplesPerSec, 8, is_out);
740         if(mr == MMSYSERR_NOERROR)
741             return mr;
742
743         /* that didn't work, so now try different sample rates */
744         channels = info->format->nChannels;
745         mr = WINMM_TryDeviceMapping(info, channels, 96000, 16, is_out);
746         if(mr == MMSYSERR_NOERROR)
747             return mr;
748         mr = WINMM_TryDeviceMapping(info, channels, 48000, 16, is_out);
749         if(mr == MMSYSERR_NOERROR)
750             return mr;
751         mr = WINMM_TryDeviceMapping(info, channels, 44100, 16, is_out);
752         if(mr == MMSYSERR_NOERROR)
753             return mr;
754         mr = WINMM_TryDeviceMapping(info, channels, 22050, 16, is_out);
755         if(mr == MMSYSERR_NOERROR)
756             return mr;
757         mr = WINMM_TryDeviceMapping(info, channels, 11025, 16, is_out);
758         if(mr == MMSYSERR_NOERROR)
759             return mr;
760
761         channels = (channels == 2) ? 1 : 2;
762         mr = WINMM_TryDeviceMapping(info, channels, 96000, 16, is_out);
763         if(mr == MMSYSERR_NOERROR)
764             return mr;
765         mr = WINMM_TryDeviceMapping(info, channels, 48000, 16, is_out);
766         if(mr == MMSYSERR_NOERROR)
767             return mr;
768         mr = WINMM_TryDeviceMapping(info, channels, 44100, 16, is_out);
769         if(mr == MMSYSERR_NOERROR)
770             return mr;
771         mr = WINMM_TryDeviceMapping(info, channels, 22050, 16, is_out);
772         if(mr == MMSYSERR_NOERROR)
773             return mr;
774         mr = WINMM_TryDeviceMapping(info, channels, 11025, 16, is_out);
775         if(mr == MMSYSERR_NOERROR)
776             return mr;
777
778         channels = info->format->nChannels;
779         mr = WINMM_TryDeviceMapping(info, channels, 96000, 8, is_out);
780         if(mr == MMSYSERR_NOERROR)
781             return mr;
782         mr = WINMM_TryDeviceMapping(info, channels, 48000, 8, is_out);
783         if(mr == MMSYSERR_NOERROR)
784             return mr;
785         mr = WINMM_TryDeviceMapping(info, channels, 44100, 8, is_out);
786         if(mr == MMSYSERR_NOERROR)
787             return mr;
788         mr = WINMM_TryDeviceMapping(info, channels, 22050, 8, is_out);
789         if(mr == MMSYSERR_NOERROR)
790             return mr;
791         mr = WINMM_TryDeviceMapping(info, channels, 11025, 8, is_out);
792         if(mr == MMSYSERR_NOERROR)
793             return mr;
794
795         channels = (channels == 2) ? 1 : 2;
796         mr = WINMM_TryDeviceMapping(info, channels, 96000, 8, is_out);
797         if(mr == MMSYSERR_NOERROR)
798             return mr;
799         mr = WINMM_TryDeviceMapping(info, channels, 48000, 8, is_out);
800         if(mr == MMSYSERR_NOERROR)
801             return mr;
802         mr = WINMM_TryDeviceMapping(info, channels, 44100, 8, is_out);
803         if(mr == MMSYSERR_NOERROR)
804             return mr;
805         mr = WINMM_TryDeviceMapping(info, channels, 22050, 8, is_out);
806         if(mr == MMSYSERR_NOERROR)
807             return mr;
808         mr = WINMM_TryDeviceMapping(info, channels, 11025, 8, is_out);
809         if(mr == MMSYSERR_NOERROR)
810             return mr;
811     }
812
813     WARN("Unable to find compatible device!\n");
814     return WAVERR_BADFORMAT;
815 }
816
817 static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_MMDevice *mmdevice,
818         WINMM_OpenInfo *info)
819 {
820     WAVEFORMATEX *closer_fmt = NULL, fmt, *passed_fmt;
821     LRESULT ret = MMSYSERR_ERROR;
822     HRESULT hr;
823
824     hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id,
825             &device->device);
826     if(FAILED(hr)){
827         ERR("Device %s (%s) unavailable: %08x\n",
828                 wine_dbgstr_w(mmdevice->dev_id),
829                 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
830         goto error;
831     }
832
833     hr = IMMDevice_Activate(device->device, &IID_IAudioClient,
834             CLSCTX_INPROC_SERVER, NULL, (void**)&device->client);
835     if(FAILED(hr)){
836         ERR("Activate failed: %08x\n", hr);
837         goto error;
838     }
839
840     if(info->format->wFormatTag == WAVE_FORMAT_PCM){
841         /* we aren't guaranteed that the struct in lpFormat is a full
842          * WAVEFORMATEX struct, which IAC::IsFormatSupported requires */
843         passed_fmt = &fmt;
844         memcpy(passed_fmt, info->format, sizeof(PCMWAVEFORMAT));
845         passed_fmt->cbSize = 0;
846     }else
847         passed_fmt = info->format;
848
849     hr = IAudioClient_IsFormatSupported(device->client,
850             AUDCLNT_SHAREMODE_SHARED, passed_fmt, &closer_fmt);
851     if(closer_fmt)
852         CoTaskMemFree(closer_fmt);
853     if(FAILED(hr) && hr != AUDCLNT_E_UNSUPPORTED_FORMAT){
854         ERR("IsFormatSupported failed: %08x\n", hr);
855         goto error;
856     }
857     if(hr == S_FALSE || hr == AUDCLNT_E_UNSUPPORTED_FORMAT){
858         ret = WAVERR_BADFORMAT;
859         goto error;
860     }
861     if(info->flags & WAVE_FORMAT_QUERY){
862         ret = MMSYSERR_NOERROR;
863         goto error;
864     }
865
866     /* buffer size = 10 * 100000 (100 ns) = 0.1 seconds */
867     hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED,
868             AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
869             10 * 100000, 50000, passed_fmt, &device->parent->session);
870     if(FAILED(hr)){
871         ERR("Initialize failed: %08x\n", hr);
872         goto error;
873     }
874
875     hr = IAudioClient_GetService(device->client, &IID_IAudioClock,
876             (void**)&device->clock);
877     if(FAILED(hr)){
878         ERR("GetService failed: %08x\n", hr);
879         goto error;
880     }
881
882     if(!device->event){
883         device->event = CreateEventW(NULL, FALSE, FALSE, NULL);
884         if(!device->event){
885             ERR("CreateEvent failed: %08x\n", hr);
886             goto error;
887         }
888
889         /* As the devices thread is waiting on g_device_handles, it can
890          * only be modified from within this same thread. */
891         if(g_device_handles){
892             g_device_handles = HeapReAlloc(GetProcessHeap(), 0, g_device_handles,
893                     sizeof(HANDLE) * (g_devhandle_count + 1));
894             g_handle_devices = HeapReAlloc(GetProcessHeap(), 0, g_handle_devices,
895                     sizeof(WINMM_Device *) * (g_devhandle_count + 1));
896         }else{
897             g_device_handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE));
898             g_handle_devices = HeapAlloc(GetProcessHeap(), 0,
899                     sizeof(WINMM_Device *));
900         }
901         g_device_handles[g_devhandle_count] = device->event;
902         g_handle_devices[g_devhandle_count] = device;
903         ++g_devhandle_count;
904     }
905
906     hr = IAudioClient_SetEventHandle(device->client, device->event);
907     if(FAILED(hr)){
908         ERR("SetEventHandle failed: %08x\n", hr);
909         goto error;
910     }
911
912     device->bytes_per_frame = info->format->nBlockAlign;
913     device->samples_per_sec = info->format->nSamplesPerSec;
914
915     device->played_frames = 0;
916     device->last_clock_pos = 0;
917     device->ofs_bytes = 0;
918     device->loop_counter = 0;
919     device->stopped = TRUE;
920     device->first = device->last = device->playing = device->loop_start = NULL;
921
922     device->cb_info.flags = HIWORD(info->flags & CALLBACK_TYPEMASK);
923     device->cb_info.callback = info->callback;
924     device->cb_info.user = info->cb_user;
925     device->cb_info.hwave = device->handle;
926
927     info->handle = device->handle;
928
929     return MMSYSERR_NOERROR;
930
931 error:
932     if(device->client){
933         IAudioClient_Release(device->client);
934         device->client = NULL;
935     }
936     if(device->device){
937         IMMDevice_Release(device->device);
938         device->device = NULL;
939     }
940
941     return ret;
942 }
943
944 static LRESULT WOD_Open(WINMM_OpenInfo *info)
945 {
946     WINMM_MMDevice *mmdevice;
947     WINMM_Device *device = NULL;
948     LRESULT ret = MMSYSERR_ERROR;
949     HRESULT hr;
950
951     TRACE("(%u, %p, %08x)\n", info->req_device, info, info->flags);
952
953     if(WINMM_IsMapper(info->req_device) || (info->flags & WAVE_MAPPED))
954         return WINMM_MapDevice(info, TRUE);
955
956     if(info->req_device >= g_outmmdevices_count)
957         return MMSYSERR_BADDEVICEID;
958
959     mmdevice = &g_out_mmdevices[info->req_device];
960
961     if(!mmdevice->out_caps.szPname[0])
962         return MMSYSERR_NOTENABLED;
963
964     device = WINMM_FindUnusedDevice(TRUE, info->req_device);
965     if(!device)
966         return MMSYSERR_ALLOCATED;
967
968     ret = WINMM_OpenDevice(device, mmdevice, info);
969     if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
970         goto error;
971     ret = MMSYSERR_ERROR;
972
973     hr = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
974             (void**)&device->render);
975     if(FAILED(hr)){
976         ERR("GetService failed: %08x\n", hr);
977         goto error;
978     }
979
980     hr = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
981             (void**)&device->volume);
982     if(FAILED(hr)){
983         ERR("GetService failed: %08x\n", hr);
984         goto error;
985     }
986
987     LeaveCriticalSection(&device->lock);
988
989     return MMSYSERR_NOERROR;
990
991 error:
992     if(device->device){
993         IMMDevice_Release(device->device);
994         device->device = NULL;
995     }
996     if(device->client){
997         IAudioClient_Release(device->client);
998         device->client = NULL;
999     }
1000     if(device->render){
1001         IAudioRenderClient_Release(device->render);
1002         device->render = NULL;
1003     }
1004     if(device->volume){
1005         IAudioStreamVolume_Release(device->volume);
1006         device->volume = NULL;
1007     }
1008     if(device->clock){
1009         IAudioClock_Release(device->clock);
1010         device->clock = NULL;
1011     }
1012     device->open = FALSE;
1013     LeaveCriticalSection(&device->lock);
1014     return ret;
1015 }
1016
1017 static LRESULT WID_Open(WINMM_OpenInfo *info)
1018 {
1019     WINMM_MMDevice *mmdevice;
1020     WINMM_Device *device = NULL;
1021     LRESULT ret = MMSYSERR_ERROR;
1022     HRESULT hr;
1023
1024     TRACE("(%u, %p, %08x)\n", info->req_device, info, info->flags);
1025
1026     if(WINMM_IsMapper(info->req_device) || info->flags & WAVE_MAPPED)
1027         return WINMM_MapDevice(info, FALSE);
1028
1029     if(info->req_device >= g_inmmdevices_count)
1030         return MMSYSERR_BADDEVICEID;
1031
1032     mmdevice = &g_in_mmdevices[info->req_device];
1033
1034     if(!mmdevice->in_caps.szPname[0])
1035         return MMSYSERR_NOTENABLED;
1036
1037     device = WINMM_FindUnusedDevice(FALSE, info->req_device);
1038     if(!device)
1039         return MMSYSERR_ALLOCATED;
1040
1041     ret = WINMM_OpenDevice(device, mmdevice, info);
1042     if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
1043         goto error;
1044     ret = MMSYSERR_ERROR;
1045
1046     hr = IAudioClient_GetService(device->client, &IID_IAudioCaptureClient,
1047             (void**)&device->capture);
1048     if(FAILED(hr)){
1049         ERR("GetService failed: %08x\n", hr);
1050         goto error;
1051     }
1052
1053     LeaveCriticalSection(&device->lock);
1054
1055     return MMSYSERR_NOERROR;
1056
1057 error:
1058     if(device->device){
1059         IMMDevice_Release(device->device);
1060         device->device = NULL;
1061     }
1062     if(device->client){
1063         IAudioClient_Release(device->client);
1064         device->client = NULL;
1065     }
1066     if(device->capture){
1067         IAudioCaptureClient_Release(device->capture);
1068         device->capture = NULL;
1069     }
1070     if(device->clock){
1071         IAudioClock_Release(device->clock);
1072         device->clock = NULL;
1073     }
1074     device->open = FALSE;
1075     LeaveCriticalSection(&device->lock);
1076     return ret;
1077 }
1078
1079 static HRESULT WINMM_CloseDevice(WINMM_Device *device)
1080 {
1081     device->open = FALSE;
1082
1083     if(!device->stopped){
1084         IAudioClient_Stop(device->client);
1085         device->stopped = TRUE;
1086     }
1087
1088     if(device->acm_handle){
1089         acmStreamClose(device->acm_handle, 0);
1090         device->acm_handle = NULL;
1091     }
1092
1093     IMMDevice_Release(device->device);
1094     device->device = NULL;
1095
1096     IAudioClient_Release(device->client);
1097     device->client = NULL;
1098
1099     IAudioClock_Release(device->clock);
1100     device->clock = NULL;
1101
1102     return S_OK;
1103 }
1104
1105 static LRESULT WOD_Close(HWAVEOUT hwave)
1106 {
1107     WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1108
1109     TRACE("(%p)\n", hwave);
1110
1111     if(!WINMM_ValidateAndLock(device))
1112         return MMSYSERR_INVALHANDLE;
1113
1114     WINMM_CloseDevice(device);
1115
1116     IAudioRenderClient_Release(device->render);
1117     device->render = NULL;
1118
1119     IAudioStreamVolume_Release(device->volume);
1120     device->volume = NULL;
1121
1122     LeaveCriticalSection(&device->lock);
1123
1124     return MMSYSERR_NOERROR;
1125 }
1126
1127 static LRESULT WID_Close(HWAVEIN hwave)
1128 {
1129     WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1130
1131     TRACE("(%p)\n", hwave);
1132
1133     if(!WINMM_ValidateAndLock(device))
1134         return MMSYSERR_INVALHANDLE;
1135
1136     WINMM_CloseDevice(device);
1137
1138     IAudioCaptureClient_Release(device->capture);
1139     device->capture = NULL;
1140
1141     LeaveCriticalSection(&device->lock);
1142
1143     return MMSYSERR_NOERROR;
1144 }
1145
1146 static LRESULT WINMM_PrepareHeader(HWAVE hwave, WAVEHDR *header)
1147 {
1148     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1149
1150     TRACE("(%p, %p)\n", hwave, header);
1151
1152     if(!WINMM_ValidateAndLock(device))
1153         return MMSYSERR_INVALHANDLE;
1154
1155     if(device->render && device->acm_handle){
1156         ACMSTREAMHEADER *ash;
1157         DWORD size;
1158         MMRESULT mr;
1159
1160         mr = acmStreamSize(device->acm_handle, header->dwBufferLength, &size,
1161                 ACM_STREAMSIZEF_SOURCE);
1162         if(mr != MMSYSERR_NOERROR){
1163             LeaveCriticalSection(&device->lock);
1164             return mr;
1165         }
1166
1167         ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + size);
1168         if(!ash){
1169             LeaveCriticalSection(&device->lock);
1170             return MMSYSERR_NOMEM;
1171         }
1172
1173         ash->cbStruct = sizeof(*ash);
1174         ash->fdwStatus = 0;
1175         ash->dwUser = (DWORD_PTR)header;
1176         ash->pbSrc = (BYTE*)header->lpData;
1177         ash->cbSrcLength = header->dwBufferLength;
1178         ash->dwSrcUser = header->dwUser;
1179         ash->pbDst = (BYTE*)ash + sizeof(ACMSTREAMHEADER);
1180         ash->cbDstLength = size;
1181         ash->dwDstUser = 0;
1182
1183         mr = acmStreamPrepareHeader(device->acm_handle, ash, 0);
1184         if(mr != MMSYSERR_NOERROR){
1185             HeapFree(GetProcessHeap(), 0, ash);
1186             LeaveCriticalSection(&device->lock);
1187             return mr;
1188         }
1189
1190         header->reserved = (DWORD_PTR)ash;
1191     }
1192
1193     LeaveCriticalSection(&device->lock);
1194
1195     header->dwFlags |= WHDR_PREPARED;
1196     header->dwFlags &= ~WHDR_DONE;
1197
1198     return MMSYSERR_NOERROR;
1199 }
1200
1201 static LRESULT WINMM_UnprepareHeader(HWAVE hwave, WAVEHDR *header)
1202 {
1203     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1204
1205     TRACE("(%p, %p)\n", hwave, header);
1206
1207     if(!WINMM_ValidateAndLock(device))
1208         return MMSYSERR_INVALHANDLE;
1209
1210     if(device->render && device->acm_handle){
1211         ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1212
1213         acmStreamUnprepareHeader(device->acm_handle, ash, 0);
1214
1215         HeapFree(GetProcessHeap(), 0, ash);
1216     }
1217
1218     LeaveCriticalSection(&device->lock);
1219
1220     header->dwFlags &= ~WHDR_PREPARED;
1221     header->dwFlags |= WHDR_DONE;
1222
1223     return MMSYSERR_NOERROR;
1224 }
1225
1226 static UINT32 WINMM_HeaderLenBytes(WINMM_Device *device, WAVEHDR *header)
1227 {
1228     if(device->acm_handle){
1229         ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1230         return ash->cbDstLengthUsed;
1231     }
1232
1233     return header->dwBufferLength;
1234 }
1235
1236 static UINT32 WINMM_HeaderLenFrames(WINMM_Device *device, WAVEHDR *header)
1237 {
1238     return WINMM_HeaderLenBytes(device, header) / device->bytes_per_frame;
1239 }
1240
1241 static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device)
1242 {
1243     HRESULT hr;
1244     WAVEHDR *first = device->first, *queue = first, *last = NULL;
1245     UINT64 clock_freq, clock_pos, clock_frames;
1246     UINT32 nloops, queue_frames = 0;
1247
1248     hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
1249     if(FAILED(hr)){
1250         ERR("GetFrequency failed: %08x\n", hr);
1251         return NULL;
1252     }
1253
1254     hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
1255     if(FAILED(hr)){
1256         ERR("GetPosition failed: %08x\n", hr);
1257         return NULL;
1258     }
1259
1260     clock_frames = (clock_pos * device->samples_per_sec) / clock_freq;
1261
1262     nloops = device->loop_counter;
1263     while(queue &&
1264             (queue_frames += WINMM_HeaderLenFrames(device, queue)) <=
1265                 clock_frames - device->last_clock_pos){
1266         if(!nloops){
1267             last = queue;
1268             device->last_clock_pos += queue_frames;
1269             queue_frames = 0;
1270             queue = device->first = queue->lpNext;
1271         }else{
1272             if(queue->dwFlags & WHDR_BEGINLOOP){
1273                 if(queue->dwFlags & WHDR_ENDLOOP)
1274                     --nloops;
1275                 else
1276                     queue = queue->lpNext;
1277             }else if(queue->dwFlags & WHDR_ENDLOOP){
1278                 queue = device->loop_start;
1279                 --nloops;
1280             }
1281         }
1282     }
1283
1284     if(last){
1285         last->lpNext = NULL;
1286         return first;
1287     }else
1288         return NULL;
1289 }
1290
1291 static void WOD_PushData(WINMM_Device *device)
1292 {
1293     WINMM_CBInfo cb_info;
1294     HRESULT hr;
1295     UINT32 pad, bufsize, avail_frames, queue_frames, written, ofs;
1296     UINT32 queue_bytes, nloops;
1297     BYTE *data;
1298     WAVEHDR *queue, *first = NULL;
1299
1300     TRACE("(%p)\n", device->handle);
1301
1302     EnterCriticalSection(&device->lock);
1303
1304     if(!device->device)
1305         goto exit;
1306
1307     if(!device->first){
1308         device->stopped = TRUE;
1309         device->last_clock_pos = 0;
1310         IAudioClient_Stop(device->client);
1311         IAudioClient_Reset(device->client);
1312         goto exit;
1313     }
1314
1315     hr = IAudioClient_GetBufferSize(device->client, &bufsize);
1316     if(FAILED(hr)){
1317         ERR("GetBufferSize failed: %08x\n", hr);
1318         goto exit;
1319     }
1320
1321     hr = IAudioClient_GetCurrentPadding(device->client, &pad);
1322     if(FAILED(hr)){
1323         ERR("GetCurrentPadding failed: %08x\n", hr);
1324         goto exit;
1325     }
1326
1327     first = WOD_MarkDoneHeaders(device);
1328
1329     /* determine which is larger between the available buffer size and
1330      * the amount of data left in the queue */
1331     avail_frames = bufsize - pad;
1332
1333     queue = device->playing;
1334     ofs = device->ofs_bytes;
1335     queue_frames = 0;
1336     nloops = 0;
1337     while(queue && queue_frames < avail_frames){
1338         queue_bytes = WINMM_HeaderLenBytes(device, queue);
1339         queue_frames += (queue_bytes - ofs) / device->bytes_per_frame;
1340         ofs = 0;
1341
1342         if(queue->dwFlags & WHDR_ENDLOOP && nloops < device->loop_counter){
1343             queue = device->loop_start;
1344             ++nloops;
1345         }else
1346             queue = queue->lpNext;
1347     }
1348
1349     if(queue_frames < avail_frames)
1350         avail_frames = queue_frames;
1351     if(avail_frames == 0)
1352         goto exit;
1353
1354     hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
1355     if(FAILED(hr)){
1356         ERR("GetBuffer failed: %08x\n", hr);
1357         goto exit;
1358     }
1359
1360     written = 0;
1361     while(device->playing && written < avail_frames){
1362         UINT32 copy_frames, copy_bytes;
1363         BYTE *queue_data;
1364
1365         queue = device->playing;
1366
1367         if(device->acm_handle){
1368             ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)queue->reserved;
1369             queue_bytes = ash->cbDstLengthUsed;
1370             queue_data = ash->pbDst;
1371         }else{
1372             queue_bytes = queue->dwBufferLength;
1373             queue_data = (BYTE*)queue->lpData;
1374         }
1375
1376         queue_frames = (queue_bytes - device->ofs_bytes) /
1377             device->bytes_per_frame;
1378
1379         copy_frames = queue_frames < (avail_frames - written) ?
1380             queue_frames : avail_frames - written;
1381         copy_bytes = copy_frames * device->bytes_per_frame;
1382
1383         memcpy(data, queue_data + device->ofs_bytes, copy_bytes);
1384
1385         data += copy_bytes;
1386         written += copy_frames;
1387         device->ofs_bytes += copy_bytes;
1388
1389         if(device->ofs_bytes >= queue_bytes){
1390             device->ofs_bytes = 0;
1391
1392             if(!(queue->dwFlags & (WHDR_BEGINLOOP | WHDR_ENDLOOP)))
1393                 device->playing = queue->lpNext;
1394             else{
1395                 if(queue->dwFlags & WHDR_BEGINLOOP){
1396                     device->loop_start = device->playing;
1397                     device->playing = queue->lpNext;
1398                     device->loop_counter = queue->dwLoops;
1399                 }
1400                 if(queue->dwFlags & WHDR_ENDLOOP){
1401                     --device->loop_counter;
1402                     if(device->loop_counter)
1403                         device->playing = device->loop_start;
1404                     else
1405                         device->loop_start = device->playing = queue->lpNext;
1406                 }
1407             }
1408         }
1409     }
1410
1411     hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames, 0);
1412     if(FAILED(hr)){
1413         ERR("ReleaseBuffer failed: %08x\n", hr);
1414         goto exit;
1415     }
1416
1417     device->played_frames += avail_frames;
1418
1419 exit:
1420     cb_info = device->cb_info;
1421
1422     LeaveCriticalSection(&device->lock);
1423
1424     while(first){
1425         WAVEHDR *next = first->lpNext;
1426         first->dwFlags &= ~WHDR_INQUEUE;
1427         first->dwFlags |= WHDR_DONE;
1428         WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1429         first = next;
1430     }
1431 }
1432
1433 static void WID_PullACMData(WINMM_Device *device)
1434 {
1435     UINT32 packet, packet_bytes;
1436     DWORD flags;
1437     BYTE *data;
1438     WAVEHDR *queue;
1439     HRESULT hr;
1440     MMRESULT mr;
1441
1442     if(device->acm_hdr.cbDstLength == 0){
1443         hr = IAudioClient_GetCurrentPadding(device->client, &packet);
1444         if(FAILED(hr)){
1445             ERR("GetCurrentPadding failed: %08x\n", hr);
1446             return;
1447         }
1448
1449         if(packet == 0)
1450             return;
1451
1452         hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
1453                 &flags, NULL, NULL);
1454         if(FAILED(hr)){
1455             ERR("GetBuffer failed: %08x\n", hr);
1456             return;
1457         }
1458
1459         acmStreamSize(device->acm_handle, packet * device->bytes_per_frame,
1460                 &packet_bytes, ACM_STREAMSIZEF_SOURCE);
1461
1462         device->acm_offs = 0;
1463
1464         device->acm_hdr.cbStruct = sizeof(device->acm_hdr);
1465         device->acm_hdr.fdwStatus = 0;
1466         device->acm_hdr.dwUser = 0;
1467         device->acm_hdr.pbSrc = data;
1468         device->acm_hdr.cbSrcLength = packet * device->bytes_per_frame;
1469         device->acm_hdr.cbSrcLengthUsed = 0;
1470         device->acm_hdr.dwSrcUser = 0;
1471         device->acm_hdr.pbDst = HeapAlloc(GetProcessHeap(), 0, packet_bytes);
1472         device->acm_hdr.cbDstLength = packet_bytes;
1473         device->acm_hdr.cbDstLengthUsed = 0;
1474         device->acm_hdr.dwDstUser = 0;
1475
1476         mr = acmStreamPrepareHeader(device->acm_handle, &device->acm_hdr, 0);
1477         if(mr != MMSYSERR_NOERROR){
1478             ERR("acmStreamPrepareHeader failed: %d\n", mr);
1479             return;
1480         }
1481
1482         mr = acmStreamConvert(device->acm_handle, &device->acm_hdr, 0);
1483         if(mr != MMSYSERR_NOERROR){
1484             ERR("acmStreamConvert failed: %d\n", mr);
1485             return;
1486         }
1487
1488         hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet);
1489         if(FAILED(hr))
1490             ERR("ReleaseBuffer failed: %08x\n", hr);
1491
1492         device->played_frames += packet;
1493     }
1494
1495     queue = device->first;
1496     while(queue){
1497         UINT32 to_copy_bytes;
1498
1499         to_copy_bytes = min(queue->dwBufferLength - queue->dwBytesRecorded,
1500                 device->acm_hdr.cbDstLengthUsed - device->acm_offs);
1501
1502         memcpy(queue->lpData + queue->dwBytesRecorded,
1503                 device->acm_hdr.pbDst + device->acm_offs, to_copy_bytes);
1504
1505         queue->dwBytesRecorded += to_copy_bytes;
1506         device->acm_offs += to_copy_bytes;
1507
1508         if(queue->dwBufferLength - queue->dwBytesRecorded <
1509                 device->bytes_per_frame){
1510             queue->dwFlags &= ~WHDR_INQUEUE;
1511             queue->dwFlags |= WHDR_DONE;
1512             device->first = queue = queue->lpNext;
1513         }
1514
1515         if(device->acm_offs >= device->acm_hdr.cbDstLengthUsed){
1516             acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1517             HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1518             device->acm_hdr.cbDstLength = 0;
1519             device->acm_hdr.cbDstLengthUsed = 0;
1520
1521             /* done with this ACM Header, so try to pull more data */
1522             WID_PullACMData(device);
1523             return;
1524         }
1525     }
1526
1527     /* out of WAVEHDRs to write into, so toss the rest of this packet */
1528     acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1529     HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1530     device->acm_hdr.cbDstLength = 0;
1531     device->acm_hdr.cbDstLengthUsed = 0;
1532 }
1533
1534 static void WID_PullData(WINMM_Device *device)
1535 {
1536     WINMM_CBInfo cb_info;
1537     WAVEHDR *queue, *first = NULL, *last = NULL;
1538     HRESULT hr;
1539
1540     TRACE("(%p)\n", device->handle);
1541
1542     EnterCriticalSection(&device->lock);
1543
1544     if(!device->device || !device->first)
1545         goto exit;
1546
1547     first = device->first;
1548
1549     if(device->acm_handle){
1550         WID_PullACMData(device);
1551         goto exit;
1552     }
1553
1554     while(device->first){
1555         BYTE *data;
1556         UINT32 pad, packet_len, packet;
1557         DWORD flags;
1558
1559         hr = IAudioClient_GetCurrentPadding(device->client, &pad);
1560         if(FAILED(hr)){
1561             ERR("GetCurrentPadding failed: %08x\n", hr);
1562             goto exit;
1563         }
1564
1565         if(pad == 0)
1566             goto exit;
1567
1568         hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
1569                 &flags, NULL, NULL);
1570         if(FAILED(hr)){
1571             ERR("GetBuffer failed: %08x\n", hr);
1572             goto exit;
1573         }
1574
1575         packet_len = packet;
1576         queue = device->first;
1577         while(queue && packet > 0){
1578             UINT32 to_copy_bytes;
1579
1580             to_copy_bytes = min(packet * device->bytes_per_frame,
1581                     queue->dwBufferLength - queue->dwBytesRecorded);
1582
1583             memcpy(queue->lpData + queue->dwBytesRecorded,
1584                     data + (packet_len - packet) * device->bytes_per_frame,
1585                     to_copy_bytes);
1586
1587             queue->dwBytesRecorded += to_copy_bytes;
1588
1589             if(queue->dwBufferLength - queue->dwBytesRecorded <
1590                     device->bytes_per_frame){
1591                 last = queue;
1592                 device->first = queue = queue->lpNext;
1593             }
1594
1595             packet -= to_copy_bytes / device->bytes_per_frame;
1596         }
1597
1598         hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet_len);
1599         if(FAILED(hr))
1600             ERR("ReleaseBuffer failed: %08x\n", hr);
1601
1602         device->played_frames += packet_len;
1603     }
1604
1605 exit:
1606     cb_info = device->cb_info;
1607
1608     LeaveCriticalSection(&device->lock);
1609
1610     if(last){
1611         last->lpNext = NULL;
1612         while(first){
1613             WAVEHDR *next = first->lpNext;
1614             first->dwFlags &= ~WHDR_INQUEUE;
1615             first->dwFlags |= WHDR_DONE;
1616             WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1617             first = next;
1618         }
1619     }
1620 }
1621
1622 static HRESULT WINMM_BeginPlaying(WINMM_Device *device)
1623 {
1624     HRESULT hr;
1625
1626     TRACE("(%p)\n", device->handle);
1627
1628     if(device->render)
1629         /* prebuffer data before starting */
1630         WOD_PushData(device);
1631
1632     if(device->stopped){
1633         device->stopped = FALSE;
1634
1635         hr = IAudioClient_Start(device->client);
1636         if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){
1637             device->stopped = TRUE;
1638             ERR("Start failed: %08x\n", hr);
1639             return hr;
1640         }
1641     }
1642
1643     return S_OK;
1644 }
1645
1646 static LRESULT WINMM_Pause(HWAVE hwave)
1647 {
1648     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1649     HRESULT hr;
1650
1651     TRACE("(%p)\n", hwave);
1652
1653     if(!WINMM_ValidateAndLock(device))
1654         return MMSYSERR_INVALHANDLE;
1655
1656     hr = IAudioClient_Stop(device->client);
1657     if(FAILED(hr)){
1658         LeaveCriticalSection(&device->lock);
1659         ERR("Stop failed: %08x\n", hr);
1660         return MMSYSERR_ERROR;
1661     }
1662
1663     device->stopped = FALSE;
1664
1665     LeaveCriticalSection(&device->lock);
1666
1667     return MMSYSERR_NOERROR;
1668 }
1669
1670 static LRESULT WINMM_Reset(HWAVE hwave)
1671 {
1672     WINMM_CBInfo cb_info;
1673     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1674     BOOL is_out;
1675     WAVEHDR *first;
1676     HRESULT hr;
1677
1678     TRACE("(%p)\n", hwave);
1679
1680     if(!WINMM_ValidateAndLock(device))
1681         return MMSYSERR_INVALHANDLE;
1682
1683     hr = IAudioClient_Stop(device->client);
1684     if(FAILED(hr)){
1685         LeaveCriticalSection(&device->lock);
1686         ERR("Stop failed: %08x\n", hr);
1687         return MMSYSERR_ERROR;
1688     }
1689     device->stopped = TRUE;
1690
1691     first = device->first;
1692     device->first = device->last = device->playing = NULL;
1693     device->ofs_bytes = 0;
1694     device->played_frames = 0;
1695     device->loop_counter = 0;
1696     device->last_clock_pos = 0;
1697     IAudioClient_Reset(device->client);
1698
1699     cb_info = device->cb_info;
1700     is_out = device->render ? TRUE : FALSE;
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(is_out)
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 /**************************************************************************
2768  *                              waveOutMessage          [WINMM.@]
2769  */
2770 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2771                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2772 {
2773     TRACE("(%p, %u, %lx, %lx)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2774
2775     switch(uMessage){
2776     case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
2777         return WINMM_QueryInstanceIDSize(HandleToULong(hWaveOut),
2778                 (DWORD_PTR*)dwParam1, TRUE);
2779     case DRV_QUERYFUNCTIONINSTANCEID:
2780         return WINMM_QueryInstanceID(HandleToULong(hWaveOut), (WCHAR*)dwParam1, dwParam2, TRUE);
2781     case DRV_QUERYMAPPABLE:
2782         return MMSYSERR_NOERROR;
2783     }
2784
2785     return MMSYSERR_NOTSUPPORTED;
2786 }
2787
2788 /**************************************************************************
2789  *                              waveInGetNumDevs                [WINMM.@]
2790  */
2791 UINT WINAPI waveInGetNumDevs(void)
2792 {
2793     if(!WINMM_StartDevicesThread())
2794         return 0;
2795
2796     TRACE("count: %u\n", g_inmmdevices_count);
2797
2798     return g_inmmdevices_count;
2799 }
2800
2801 /**************************************************************************
2802  *                              waveInGetDevCapsW               [WINMM.@]
2803  */
2804 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2805 {
2806     WAVEINCAPSW mapper_caps, *caps;
2807
2808     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2809
2810     if(!WINMM_StartDevicesThread())
2811         return MMSYSERR_ERROR;
2812
2813     if(!lpCaps)
2814         return MMSYSERR_INVALPARAM;
2815
2816     if(WINMM_IsMapper(uDeviceID)){
2817         /* FIXME: Should be localized */
2818         static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2819             'n','d',' ','M','a','p','p','e','r',0};
2820
2821         mapper_caps.wMid = 0xFF;
2822         mapper_caps.wPid = 0xFF;
2823         mapper_caps.vDriverVersion = 0x00010001;
2824         mapper_caps.dwFormats = 0xFFFFFFFF;
2825         mapper_caps.wReserved1 = 0;
2826         mapper_caps.wChannels = 2;
2827         lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2828
2829         caps = &mapper_caps;
2830     }else{
2831         if(uDeviceID >= g_inmmdevices_count)
2832             return MMSYSERR_BADDEVICEID;
2833
2834         caps = &g_in_mmdevices[uDeviceID].in_caps;
2835     }
2836
2837     memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2838
2839     return MMSYSERR_NOERROR;
2840 }
2841
2842 /**************************************************************************
2843  *                              waveInGetDevCapsA               [WINMM.@]
2844  */
2845 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2846 {
2847     UINT ret;
2848     WAVEINCAPSW wicW;
2849
2850     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2851
2852     if(!WINMM_StartDevicesThread())
2853         return MMSYSERR_ERROR;
2854
2855     if(!lpCaps)
2856         return MMSYSERR_INVALPARAM;
2857
2858     ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2859
2860     if (ret == MMSYSERR_NOERROR) {
2861         WAVEINCAPSA wicA;
2862         wicA.wMid           = wicW.wMid;
2863         wicA.wPid           = wicW.wPid;
2864         wicA.vDriverVersion = wicW.vDriverVersion;
2865         WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2866                              sizeof(wicA.szPname), NULL, NULL );
2867         wicA.dwFormats      = wicW.dwFormats;
2868         wicA.wChannels      = wicW.wChannels;
2869         memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2870     }
2871     return ret;
2872 }
2873
2874 /**************************************************************************
2875  *                              waveInOpen                      [WINMM.@]
2876  */
2877 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2878                            LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2879                            DWORD_PTR dwInstance, DWORD dwFlags)
2880 {
2881     LRESULT res;
2882     WINMM_OpenInfo info;
2883     WINMM_CBInfo cb_info;
2884
2885     TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
2886             dwCallback, dwInstance, dwFlags);
2887
2888     if(!WINMM_StartDevicesThread())
2889         return MMSYSERR_ERROR;
2890
2891     if(!lphWaveIn && !(dwFlags & WAVE_FORMAT_QUERY))
2892         return MMSYSERR_INVALPARAM;
2893
2894     res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2895     if(res != MMSYSERR_NOERROR)
2896         return res;
2897
2898     info.format = (WAVEFORMATEX*)lpFormat;
2899     info.callback = dwCallback;
2900     info.cb_user = dwInstance;
2901     info.req_device = uDeviceID;
2902     info.flags = dwFlags;
2903
2904     res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
2905     if(res != MMSYSERR_NOERROR)
2906         return res;
2907
2908     if(lphWaveIn)
2909         *lphWaveIn = (HWAVEIN)info.handle;
2910
2911     cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2912     cb_info.callback = dwCallback;
2913     cb_info.user = dwInstance;
2914     cb_info.hwave = info.handle;
2915
2916     WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
2917
2918     return res;
2919 }
2920
2921 /**************************************************************************
2922  *                              waveInClose                     [WINMM.@]
2923  */
2924 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2925 {
2926     WINMM_Device *device;
2927     WINMM_CBInfo cb_info;
2928     UINT res;
2929
2930     TRACE("(%p)\n", hWaveIn);
2931
2932     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
2933
2934     if(!WINMM_ValidateAndLock(device))
2935         return MMSYSERR_INVALHANDLE;
2936
2937     cb_info = device->cb_info;
2938
2939     LeaveCriticalSection(&device->lock);
2940
2941     res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
2942
2943     if(res == MMSYSERR_NOERROR)
2944         WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
2945
2946     return res;
2947 }
2948
2949 /**************************************************************************
2950  *                              waveInPrepareHeader             [WINMM.@]
2951  */
2952 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2953                                 UINT uSize)
2954 {
2955     TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2956
2957     if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2958         return MMSYSERR_INVALPARAM;
2959
2960     if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2961         return WAVERR_STILLPLAYING;
2962
2963     return WINMM_PrepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
2964 }
2965
2966 /**************************************************************************
2967  *                              waveInUnprepareHeader   [WINMM.@]
2968  */
2969 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2970                                   UINT uSize)
2971 {
2972     TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2973
2974     if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2975         return MMSYSERR_INVALPARAM;
2976
2977     if(!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
2978         return MMSYSERR_NOERROR;
2979
2980     if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2981         return WAVERR_STILLPLAYING;
2982
2983     return WINMM_UnprepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
2984 }
2985
2986 /**************************************************************************
2987  *                              waveInAddBuffer         [WINMM.@]
2988  */
2989 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn, WAVEHDR *header, UINT uSize)
2990 {
2991     WINMM_Device *device;
2992
2993     TRACE("(%p, %p, %u)\n", hWaveIn, header, uSize);
2994
2995     if(!header || uSize < sizeof(WAVEHDR))
2996         return MMSYSERR_INVALPARAM;
2997
2998     if(!(header->dwFlags & WHDR_PREPARED))
2999         return WAVERR_UNPREPARED;
3000
3001     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3002
3003     if(!WINMM_ValidateAndLock(device))
3004         return MMSYSERR_INVALHANDLE;
3005
3006     if(!device->first)
3007         device->first = device->last = header;
3008     else{
3009         device->last->lpNext = header;
3010         device->last = header;
3011     }
3012
3013     header->dwBytesRecorded = 0;
3014     header->lpNext = NULL;
3015     header->dwFlags &= ~WHDR_DONE;
3016     header->dwFlags |= WHDR_INQUEUE;
3017
3018     LeaveCriticalSection(&device->lock);
3019
3020     return MMSYSERR_NOERROR;
3021 }
3022
3023 /**************************************************************************
3024  *                              waveInReset             [WINMM.@]
3025  */
3026 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
3027 {
3028     TRACE("(%p)\n", hWaveIn);
3029
3030     return WINMM_Reset((HWAVE)hWaveIn);
3031 }
3032
3033 /**************************************************************************
3034  *                              waveInStart             [WINMM.@]
3035  */
3036 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
3037 {
3038     WINMM_Device *device;
3039     HRESULT hr;
3040
3041     TRACE("(%p)\n", hWaveIn);
3042
3043     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3044
3045     if(!WINMM_ValidateAndLock(device))
3046         return MMSYSERR_INVALHANDLE;
3047
3048     hr = WINMM_BeginPlaying(device);
3049     if(FAILED(hr)){
3050         LeaveCriticalSection(&device->lock);
3051         return MMSYSERR_ERROR;
3052     }
3053
3054     LeaveCriticalSection(&device->lock);
3055
3056     return MMSYSERR_NOERROR;
3057 }
3058
3059 /**************************************************************************
3060  *                              waveInStop              [WINMM.@]
3061  */
3062 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
3063 {
3064     WINMM_CBInfo cb_info;
3065     WINMM_Device *device;
3066     WAVEHDR *buf;
3067     HRESULT hr;
3068
3069     TRACE("(%p)\n", hWaveIn);
3070
3071     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3072
3073     if(!WINMM_ValidateAndLock(device))
3074         return MMSYSERR_INVALHANDLE;
3075
3076     hr = WINMM_Pause((HWAVE)hWaveIn);
3077     if(FAILED(hr)){
3078         LeaveCriticalSection(&device->lock);
3079         return MMSYSERR_ERROR;
3080     }
3081     device->stopped = TRUE;
3082
3083     buf = device->first;
3084     if(buf && buf->dwBytesRecorded > 0){
3085         device->first = buf->lpNext;
3086     }else
3087         buf = NULL;
3088
3089     cb_info = device->cb_info;
3090
3091     LeaveCriticalSection(&device->lock);
3092
3093     if(buf){
3094         buf->dwFlags &= ~WHDR_INQUEUE;
3095         buf->dwFlags |= WHDR_DONE;
3096         WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
3097     }
3098
3099     return MMSYSERR_NOERROR;
3100 }
3101
3102 /**************************************************************************
3103  *                              waveInGetPosition       [WINMM.@]
3104  */
3105 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
3106                               UINT uSize)
3107 {
3108     TRACE("(%p, %p, %u)\n", hWaveIn, lpTime, uSize);
3109
3110     if(!uSize || !lpTime || uSize != sizeof(MMTIME))
3111         return MMSYSERR_INVALPARAM;
3112
3113     return WINMM_GetPosition((HWAVE)hWaveIn, lpTime);
3114 }
3115
3116 /**************************************************************************
3117  *                              waveInGetID                     [WINMM.@]
3118  */
3119 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
3120 {
3121     UINT dev, junk;
3122     BOOL is_out;
3123     WINMM_Device *device;
3124
3125     TRACE("(%p, %p)\n", hWaveIn, lpuDeviceID);
3126
3127     if(!lpuDeviceID)
3128         return MMSYSERR_INVALPARAM;
3129
3130     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3131     if(!WINMM_ValidateAndLock(device))
3132         return MMSYSERR_INVALHANDLE;
3133
3134     LeaveCriticalSection(&device->lock);
3135
3136     WINMM_DecomposeHWAVE((HWAVE)hWaveIn, lpuDeviceID, &is_out, &dev, &junk);
3137
3138     return MMSYSERR_NOERROR;
3139 }
3140
3141 /**************************************************************************
3142  *                              waveInMessage           [WINMM.@]
3143  */
3144 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
3145                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3146 {
3147     TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
3148
3149     switch(uMessage){
3150     case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
3151         return WINMM_QueryInstanceIDSize(HandleToULong(hWaveIn),
3152                 (DWORD_PTR*)dwParam1, FALSE);
3153     case DRV_QUERYFUNCTIONINSTANCEID:
3154         return WINMM_QueryInstanceID(HandleToULong(hWaveIn), (WCHAR*)dwParam1, dwParam2, FALSE);
3155     case DRV_QUERYMAPPABLE:
3156         return MMSYSERR_NOERROR;
3157     }
3158
3159     return MMSYSERR_NOTSUPPORTED;
3160 }
3161
3162 UINT WINAPI mixerGetNumDevs(void)
3163 {
3164     TRACE("\n");
3165
3166     if(!WINMM_StartDevicesThread())
3167         return 0;
3168
3169     return g_outmmdevices_count + g_inmmdevices_count;
3170 }
3171
3172 /**************************************************************************
3173  *                              mixerGetDevCapsA                [WINMM.@]
3174  */
3175 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
3176 {
3177     MIXERCAPSW micW;
3178     UINT       ret;
3179
3180     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3181
3182     if(!lpCaps)
3183         return MMSYSERR_INVALPARAM;
3184
3185     ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
3186
3187     if (ret == MMSYSERR_NOERROR) {
3188         MIXERCAPSA micA;
3189         micA.wMid           = micW.wMid;
3190         micA.wPid           = micW.wPid;
3191         micA.vDriverVersion = micW.vDriverVersion;
3192         WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
3193                              sizeof(micA.szPname), NULL, NULL );
3194         micA.fdwSupport     = micW.fdwSupport;
3195         micA.cDestinations  = micW.cDestinations;
3196         memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
3197     }
3198     return ret;
3199 }
3200
3201 /**************************************************************************
3202  *                              mixerGetDevCapsW                [WINMM.@]
3203  */
3204 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
3205 {
3206     WINMM_MMDevice *mmdevice;
3207     MIXERCAPSW caps;
3208
3209     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3210
3211     if(!WINMM_StartDevicesThread())
3212         return MMSYSERR_ERROR;
3213
3214     if(!lpCaps)
3215         return MMSYSERR_INVALPARAM;
3216
3217     if(!uSize)
3218         return MMSYSERR_NOERROR;
3219
3220     if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3221         return MMSYSERR_BADDEVICEID;
3222
3223     if(uDeviceID < g_outmmdevices_count){
3224         mmdevice = &g_out_mmdevices[uDeviceID];
3225         memcpy(caps.szPname, mmdevice->out_caps.szPname, sizeof(caps.szPname));
3226     }else{
3227         mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3228         memcpy(caps.szPname, mmdevice->in_caps.szPname, sizeof(caps.szPname));
3229     }
3230
3231     caps.wMid = 0xFF;
3232     caps.wPid = 0xFF;
3233     caps.vDriverVersion = 0x00010001;
3234     caps.fdwSupport = 0;
3235     caps.cDestinations = 1;
3236
3237     memcpy(lpCaps, &caps, uSize);
3238
3239     return MMSYSERR_NOERROR;
3240 }
3241
3242 /**************************************************************************
3243  *                              mixerOpen                       [WINMM.@]
3244  */
3245 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
3246                       DWORD_PTR dwInstance, DWORD fdwOpen)
3247 {
3248     WINMM_MMDevice *mmdevice;
3249     MMRESULT mr;
3250
3251     TRACE("(%p, %d, %lx, %lx, %x)\n", lphMix, uDeviceID, dwCallback,
3252             dwInstance, fdwOpen);
3253
3254     if(!WINMM_StartDevicesThread())
3255         return MMSYSERR_ERROR;
3256
3257     if(!lphMix)
3258         return MMSYSERR_INVALPARAM;
3259
3260     mr = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
3261     if(mr != MMSYSERR_NOERROR)
3262         return mr;
3263
3264     if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3265         return MMSYSERR_BADDEVICEID;
3266
3267     if(uDeviceID < g_outmmdevices_count){
3268         mmdevice = &g_out_mmdevices[uDeviceID];
3269         *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID, TRUE,
3270                 mmdevice->mixer_count);
3271     }else{
3272         mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3273         *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID - g_outmmdevices_count,
3274                 FALSE, mmdevice->mixer_count);
3275     }
3276
3277     ++mmdevice->mixer_count;
3278
3279     return MMSYSERR_NOERROR;
3280 }
3281
3282 /**************************************************************************
3283  *                              mixerClose                      [WINMM.@]
3284  */
3285 UINT WINAPI mixerClose(HMIXER hMix)
3286 {
3287     TRACE("(%p)\n", hMix);
3288
3289     return MMSYSERR_NOERROR;
3290 }
3291
3292 /**************************************************************************
3293  *                              mixerGetID                      [WINMM.@]
3294  */
3295 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
3296 {
3297     WINMM_MMDevice *mmdevice;
3298
3299     TRACE("(%p, %p, %x)\n", hmix, lpid, fdwID);
3300
3301     if(!WINMM_StartDevicesThread())
3302         return MMSYSERR_ERROR;
3303
3304     if(!lpid)
3305         return MMSYSERR_INVALPARAM;
3306
3307     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwID, lpid);
3308     if(!mmdevice)
3309         return MMSYSERR_INVALHANDLE;
3310
3311     if(mmdevice->in_caps.szPname[0] != '\0')
3312         *lpid += g_outmmdevices_count;
3313
3314     return MMSYSERR_NOERROR;
3315 }
3316
3317 /**************************************************************************
3318  *                              mixerGetControlDetailsW         [WINMM.@]
3319  */
3320 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
3321                                     DWORD fdwDetails)
3322 {
3323     WINMM_ControlDetails details;
3324
3325     TRACE("(%p, %p, %x)\n", hmix, lpmcdW, fdwDetails);
3326
3327     if(!WINMM_StartDevicesThread())
3328         return MMSYSERR_ERROR;
3329
3330     if(!lpmcdW)
3331         return MMSYSERR_INVALPARAM;
3332
3333     TRACE("dwControlID: %u\n", lpmcdW->dwControlID);
3334
3335     details.hmix = hmix;
3336     details.details = lpmcdW;
3337     details.flags = fdwDetails;
3338
3339     return SendMessageW(g_devices_hwnd, MXDM_GETCONTROLDETAILS,
3340             (DWORD_PTR)&details, 0);
3341 }
3342
3343 /**************************************************************************
3344  *                              mixerGetControlDetailsA [WINMM.@]
3345  */
3346 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
3347                                     DWORD fdwDetails)
3348 {
3349     UINT ret = MMSYSERR_NOTSUPPORTED;
3350
3351     TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
3352
3353     if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
3354         return MMSYSERR_INVALPARAM;
3355
3356     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
3357     case MIXER_GETCONTROLDETAILSF_VALUE:
3358         /* can safely use A structure as it is, no string inside */
3359         ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3360         break;
3361     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
3362         {
3363             MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = lpmcdA->paDetails;
3364             MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
3365             int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3366             unsigned int i;
3367
3368             if (lpmcdA->u.cMultipleItems != 0) {
3369                 size *= lpmcdA->u.cMultipleItems;
3370             }
3371             pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
3372             lpmcdA->paDetails = pDetailsW;
3373             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3374             /* set up lpmcd->paDetails */
3375             ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3376             /* copy from lpmcd->paDetails back to paDetailsW; */
3377             if (ret == MMSYSERR_NOERROR) {
3378                 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
3379                     pDetailsA->dwParam1 = pDetailsW->dwParam1;
3380                     pDetailsA->dwParam2 = pDetailsW->dwParam2;
3381                     WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
3382                                          pDetailsA->szName,
3383                                          sizeof(pDetailsA->szName), NULL, NULL );
3384                     pDetailsA++;
3385                     pDetailsW++;
3386                 }
3387                 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3388                 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3389             }
3390             HeapFree(GetProcessHeap(), 0, pDetailsW);
3391             lpmcdA->paDetails = pDetailsA;
3392             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
3393         }
3394         break;
3395     default:
3396         ERR("Unsupported fdwDetails=0x%08x\n", fdwDetails);
3397     }
3398
3399     return ret;
3400 }
3401
3402 /**************************************************************************
3403  *                              mixerGetLineControlsA   [WINMM.@]
3404  */
3405 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
3406                                   DWORD fdwControls)
3407 {
3408     MIXERLINECONTROLSW  mlcW;
3409     DWORD               ret;
3410     unsigned int        i;
3411
3412     TRACE("(%p, %p, %x)\n", hmix, lpmlcA, fdwControls);
3413
3414     if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
3415         lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
3416         return MMSYSERR_INVALPARAM;
3417
3418     mlcW.cbStruct = sizeof(mlcW);
3419     mlcW.dwLineID = lpmlcA->dwLineID;
3420     mlcW.u.dwControlID = lpmlcA->u.dwControlID;
3421     mlcW.u.dwControlType = lpmlcA->u.dwControlType;
3422
3423     /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
3424        the control count is assumed to be 1 - This is relied upon by a game,
3425        "Dynomite Deluze"                                                    */
3426     if (MIXER_GETLINECONTROLSF_ONEBYTYPE == (fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK)) {
3427         mlcW.cControls = 1;
3428     } else {
3429         mlcW.cControls = lpmlcA->cControls;
3430     }
3431     mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
3432     mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
3433                               mlcW.cControls * mlcW.cbmxctrl);
3434
3435     ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
3436
3437     if (ret == MMSYSERR_NOERROR) {
3438         lpmlcA->dwLineID = mlcW.dwLineID;
3439         lpmlcA->u.dwControlID = mlcW.u.dwControlID;
3440         lpmlcA->u.dwControlType = mlcW.u.dwControlType;
3441
3442         for (i = 0; i < mlcW.cControls; i++) {
3443             lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
3444             lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
3445             lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
3446             lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
3447             lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
3448             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
3449                                  lpmlcA->pamxctrl[i].szShortName,
3450                                  sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
3451             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
3452                                  lpmlcA->pamxctrl[i].szName,
3453                                  sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
3454             /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
3455              * sizeof(mlcW.pamxctrl[i].Bounds) */
3456             memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
3457                    sizeof(mlcW.pamxctrl[i].Bounds));
3458             /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
3459              * sizeof(mlcW.pamxctrl[i].Metrics) */
3460             memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
3461                    sizeof(mlcW.pamxctrl[i].Metrics));
3462         }
3463     }
3464
3465     HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
3466
3467     return ret;
3468 }
3469
3470 static UINT WINMM_GetVolumeLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3471         MIXERCONTROLW *ctl, DWORD flags)
3472 {
3473     ctl->dwControlID = (line == 0xFFFF0000) ? 0 : 2;
3474     ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
3475     ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3476     ctl->cMultipleItems = 0;
3477     lstrcpyW(ctl->szShortName, volumeW);
3478     lstrcpyW(ctl->szName, volumeW);
3479     ctl->Bounds.s1.dwMinimum = 0;
3480     ctl->Bounds.s1.dwMaximum = 0xFFFF;
3481     ctl->Metrics.cSteps = 192;
3482
3483     return MMSYSERR_NOERROR;
3484 }
3485
3486 static UINT WINMM_GetMuteLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3487         MIXERCONTROLW *ctl, DWORD flags)
3488 {
3489     ctl->dwControlID = (line == 0xFFFF0000) ? 1 : 3;
3490     ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
3491     ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3492     ctl->cMultipleItems = 0;
3493     lstrcpyW(ctl->szShortName, muteW);
3494     lstrcpyW(ctl->szName, muteW);
3495     ctl->Bounds.s1.dwMinimum = 0;
3496     ctl->Bounds.s1.dwMaximum = 1;
3497     ctl->Metrics.cSteps = 0;
3498
3499     return MMSYSERR_NOERROR;
3500 }
3501
3502 /**************************************************************************
3503  *                              mixerGetLineControlsW           [WINMM.@]
3504  */
3505 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
3506                                   DWORD fdwControls)
3507 {
3508     WINMM_MMDevice *mmdevice;
3509
3510     TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
3511
3512     if(!WINMM_StartDevicesThread())
3513         return MMSYSERR_ERROR;
3514
3515     if(fdwControls & ~(MIXER_GETLINECONTROLSF_ALL |
3516                 MIXER_GETLINECONTROLSF_ONEBYID |
3517                 MIXER_GETLINECONTROLSF_ONEBYTYPE |
3518                 MIXER_OBJECTF_HMIXER |
3519                 MIXER_OBJECTF_MIXER)){
3520         WARN("Unknown GetLineControls flag: %x\n", fdwControls);
3521         return MMSYSERR_INVALFLAG;
3522     }
3523
3524     if(!lpmlcW || lpmlcW->cbStruct < sizeof(*lpmlcW) || !lpmlcW->pamxctrl)
3525         return MMSYSERR_INVALPARAM;
3526
3527     TRACE("dwLineID: %u\n", lpmlcW->dwLineID);
3528     TRACE("dwControl: %x\n", lpmlcW->u.dwControlID);
3529     TRACE("cControls: %u\n", lpmlcW->cControls);
3530
3531     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwControls, NULL);
3532     if(!mmdevice)
3533         return MMSYSERR_INVALHANDLE;
3534
3535     switch(fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK){
3536     case MIXER_GETLINECONTROLSF_ALL:
3537         if(lpmlcW->cControls != 2)
3538             return MMSYSERR_INVALPARAM;
3539         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3540             return MMSYSERR_INVALPARAM;
3541         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3542             return MIXERR_INVALLINE;
3543         WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3544                 &lpmlcW->pamxctrl[0], fdwControls);
3545         WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3546                 &lpmlcW->pamxctrl[1], fdwControls);
3547         return MMSYSERR_NOERROR;
3548     case MIXER_GETLINECONTROLSF_ONEBYID:
3549         if(lpmlcW->cControls != 1)
3550             return MMSYSERR_INVALPARAM;
3551         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3552             return MMSYSERR_INVALPARAM;
3553         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3554             return MIXERR_INVALLINE;
3555         if(lpmlcW->u.dwControlID == 0)
3556             return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3557                     lpmlcW->pamxctrl, fdwControls);
3558         if(lpmlcW->u.dwControlID == 1)
3559             return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3560                     lpmlcW->pamxctrl, fdwControls);
3561         return MMSYSERR_NOTSUPPORTED;
3562     case MIXER_GETLINECONTROLSF_ONEBYTYPE:
3563         if(lpmlcW->cControls != 1)
3564             return MMSYSERR_INVALPARAM;
3565         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3566             return MMSYSERR_INVALPARAM;
3567         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3568             return MIXERR_INVALLINE;
3569         if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
3570             return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3571                     lpmlcW->pamxctrl, fdwControls);
3572         if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
3573             return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3574                     lpmlcW->pamxctrl, fdwControls);
3575         return MMSYSERR_NOTSUPPORTED;
3576     }
3577
3578     return MMSYSERR_NOTSUPPORTED;
3579 }
3580
3581 static UINT WINMM_GetSourceLineInfo(WINMM_MMDevice *mmdevice, UINT mmdev_index,
3582         MIXERLINEW *info, DWORD flags)
3583 {
3584     BOOL is_out = TRUE;
3585     if(mmdevice->in_caps.szPname[0] != '\0')
3586         is_out = FALSE;
3587
3588     if(info->dwSource != 0)
3589         return MIXERR_INVALLINE;
3590
3591     info->dwDestination = 0;
3592     info->dwLineID = 0;
3593     info->fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
3594     info->cConnections = 0;
3595     info->cControls = 2;
3596     /* volume & mute always affect all channels, so claim 1 channel */
3597     info->cChannels = 1;
3598     info->Target.dwDeviceID = mmdev_index;
3599     info->Target.wMid = ~0;
3600     info->Target.wPid = ~0;
3601     info->Target.vDriverVersion = 0;
3602
3603     lstrcpyW(info->szShortName, volumeW);
3604     lstrcpyW(info->szName, mastervolumeW);
3605
3606     if(is_out){
3607         info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
3608         info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
3609         memcpy(info->Target.szPname, mmdevice->out_caps.szPname,
3610                 sizeof(info->Target.szPname));
3611     }else{
3612         info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
3613         info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3614         info->Target.szPname[0] = '\0';
3615     }
3616
3617     return MMSYSERR_NOERROR;
3618 }
3619
3620 static UINT WINMM_GetDestinationLineInfo(WINMM_MMDevice *mmdevice,
3621         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3622 {
3623     BOOL is_out = TRUE;
3624     if(mmdevice->in_caps.szPname[0] != '\0')
3625         is_out = FALSE;
3626
3627     if(info->dwDestination != 0)
3628         return MIXERR_INVALLINE;
3629
3630     info->dwSource = 0xFFFFFFFF;
3631     info->dwLineID = 0xFFFF0000;
3632     info->fdwLine = MIXERLINE_LINEF_ACTIVE;
3633     info->cConnections = 1;
3634     info->cControls = 2;
3635
3636     lstrcpyW(info->szShortName, volumeW);
3637     lstrcpyW(info->szName, mastervolumeW);
3638
3639     info->Target.dwDeviceID = mmdev_index;
3640     info->Target.wMid = ~0;
3641     info->Target.wPid = ~0;
3642     info->Target.vDriverVersion = 0;
3643
3644     if(is_out){
3645         info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
3646         info->cChannels = mmdevice->out_caps.wChannels;
3647         info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3648         info->Target.szPname[0] = '\0';
3649     }else{
3650         info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
3651         info->cChannels = mmdevice->in_caps.wChannels;
3652         info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
3653         memcpy(info->Target.szPname, mmdevice->in_caps.szPname,
3654                 sizeof(info->Target.szPname));
3655     }
3656
3657     return MMSYSERR_NOERROR;
3658 }
3659
3660 static UINT WINMM_GetComponentTypeLineInfo(WINMM_MMDevice *mmdevice,
3661         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3662 {
3663     BOOL is_out = TRUE;
3664     if(mmdevice->in_caps.szPname[0] != '\0')
3665         is_out = FALSE;
3666
3667     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN){
3668         if(is_out)
3669             return MIXERR_INVALLINE;
3670         info->dwDestination = 0;
3671         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3672     }
3673
3674     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS){
3675         if(!is_out)
3676             return MIXERR_INVALLINE;
3677         info->dwDestination = 0;
3678         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3679     }
3680
3681     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE){
3682         if(is_out)
3683             return MIXERR_INVALLINE;
3684         info->dwSource = 0;
3685         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3686     }
3687
3688     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
3689         if(!is_out)
3690             return MIXERR_INVALLINE;
3691         info->dwSource = 0;
3692         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3693     }
3694
3695     TRACE("Returning INVALLINE on this component type: %u\n",
3696             info->dwComponentType);
3697
3698     return MIXERR_INVALLINE;
3699 }
3700
3701 static UINT WINMM_GetLineIDLineInfo(WINMM_MMDevice *mmdevice,
3702         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3703 {
3704     if(info->dwLineID == 0xFFFF0000){
3705         info->dwDestination = 0;
3706         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3707     }
3708
3709     if(info->dwLineID == 0){
3710         info->dwSource = 0;
3711         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3712     }
3713
3714     TRACE("Returning INVALLINE on this dwLineID: %u\n", info->dwLineID);
3715     return MIXERR_INVALLINE;
3716 }
3717
3718 /**************************************************************************
3719  *                              mixerGetLineInfoW               [WINMM.@]
3720  */
3721 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
3722 {
3723     UINT mmdev_index;
3724     WINMM_MMDevice *mmdevice;
3725
3726     TRACE("(%p, %p, %x)\n", hmix, lpmliW, fdwInfo);
3727
3728     if(!WINMM_StartDevicesThread())
3729         return MMSYSERR_ERROR;
3730
3731     if(!lpmliW || lpmliW->cbStruct < sizeof(MIXERLINEW))
3732         return MMSYSERR_INVALPARAM;
3733
3734     TRACE("dwDestination: %u\n", lpmliW->dwDestination);
3735     TRACE("dwSource: %u\n", lpmliW->dwSource);
3736     TRACE("dwLineID: %u\n", lpmliW->dwLineID);
3737     TRACE("fdwLine: 0x%x\n", lpmliW->fdwLine);
3738     TRACE("dwComponentType: 0x%x\n", lpmliW->dwComponentType);
3739
3740     if(fdwInfo & ~(MIXER_GETLINEINFOF_COMPONENTTYPE |
3741                 MIXER_GETLINEINFOF_DESTINATION |
3742                 MIXER_GETLINEINFOF_LINEID |
3743                 MIXER_GETLINEINFOF_SOURCE |
3744                 MIXER_GETLINEINFOF_TARGETTYPE |
3745                 MIXER_OBJECTF_HMIXER |
3746                 MIXER_OBJECTF_MIXER)){
3747         WARN("Unknown GetLineInfo flag: %x\n", fdwInfo);
3748         return MMSYSERR_INVALFLAG;
3749     }
3750
3751     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwInfo, &mmdev_index);
3752     if(!mmdevice)
3753         return MMSYSERR_INVALHANDLE;
3754
3755     switch(fdwInfo & MIXER_GETLINEINFOF_QUERYMASK){
3756     case MIXER_GETLINEINFOF_DESTINATION:
3757         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, lpmliW,
3758                 fdwInfo);
3759     case MIXER_GETLINEINFOF_SOURCE:
3760         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3761     case MIXER_GETLINEINFOF_COMPONENTTYPE:
3762         return WINMM_GetComponentTypeLineInfo(mmdevice, mmdev_index, lpmliW,
3763                 fdwInfo);
3764     case MIXER_GETLINEINFOF_LINEID:
3765         return WINMM_GetLineIDLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3766     case MIXER_GETLINEINFOF_TARGETTYPE:
3767         FIXME("TARGETTYPE flag not implemented!\n");
3768         return MIXERR_INVALLINE;
3769     }
3770
3771     TRACE("Returning INVALFLAG on these flags: %x\n", fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
3772     return MMSYSERR_INVALFLAG;
3773 }
3774
3775 /**************************************************************************
3776  *                              mixerGetLineInfoA               [WINMM.@]
3777  */
3778 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
3779                               DWORD fdwInfo)
3780 {
3781     MIXERLINEW          mliW;
3782     UINT                ret;
3783
3784     TRACE("(%p, %p, %x)\n", hmix, lpmliA, fdwInfo);
3785
3786     if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
3787         return MMSYSERR_INVALPARAM;
3788
3789     mliW.cbStruct = sizeof(mliW);
3790     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
3791     case MIXER_GETLINEINFOF_COMPONENTTYPE:
3792         mliW.dwComponentType = lpmliA->dwComponentType;
3793         break;
3794     case MIXER_GETLINEINFOF_DESTINATION:
3795         mliW.dwDestination = lpmliA->dwDestination;
3796         break;
3797     case MIXER_GETLINEINFOF_LINEID:
3798         mliW.dwLineID = lpmliA->dwLineID;
3799         break;
3800     case MIXER_GETLINEINFOF_SOURCE:
3801         mliW.dwDestination = lpmliA->dwDestination;
3802         mliW.dwSource = lpmliA->dwSource;
3803         break;
3804     case MIXER_GETLINEINFOF_TARGETTYPE:
3805         mliW.Target.dwType = lpmliA->Target.dwType;
3806         mliW.Target.wMid = lpmliA->Target.wMid;
3807         mliW.Target.wPid = lpmliA->Target.wPid;
3808         mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
3809         MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname)/sizeof(WCHAR));
3810         break;
3811     default:
3812         WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
3813         return MMSYSERR_INVALFLAG;
3814     }
3815
3816     ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
3817
3818     if(ret == MMSYSERR_NOERROR)
3819     {
3820         lpmliA->dwDestination = mliW.dwDestination;
3821         lpmliA->dwSource = mliW.dwSource;
3822         lpmliA->dwLineID = mliW.dwLineID;
3823         lpmliA->fdwLine = mliW.fdwLine;
3824         lpmliA->dwUser = mliW.dwUser;
3825         lpmliA->dwComponentType = mliW.dwComponentType;
3826         lpmliA->cChannels = mliW.cChannels;
3827         lpmliA->cConnections = mliW.cConnections;
3828         lpmliA->cControls = mliW.cControls;
3829         WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
3830                              sizeof(lpmliA->szShortName), NULL, NULL);
3831         WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
3832                              sizeof(lpmliA->szName), NULL, NULL );
3833         lpmliA->Target.dwType = mliW.Target.dwType;
3834         lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
3835         lpmliA->Target.wMid = mliW.Target.wMid;
3836         lpmliA->Target.wPid = mliW.Target.wPid;
3837         lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
3838         WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
3839                              sizeof(lpmliA->Target.szPname), NULL, NULL );
3840     }
3841     return ret;
3842 }
3843
3844 /**************************************************************************
3845  *                              mixerSetControlDetails  [WINMM.@]
3846  */
3847 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
3848                                    DWORD fdwDetails)
3849 {
3850     WINMM_ControlDetails details;
3851
3852     TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
3853
3854     if(!WINMM_StartDevicesThread())
3855         return MMSYSERR_ERROR;
3856
3857     if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
3858             MIXER_SETCONTROLDETAILSF_CUSTOM)
3859         return MMSYSERR_NOTSUPPORTED;
3860
3861     if(!lpmcd)
3862         return MMSYSERR_INVALPARAM;
3863
3864     TRACE("dwControlID: %u\n", lpmcd->dwControlID);
3865
3866     details.hmix = hmix;
3867     details.details = lpmcd;
3868     details.flags = fdwDetails;
3869
3870     return SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
3871             (DWORD_PTR)&details, 0);
3872 }
3873
3874 /**************************************************************************
3875  *                              mixerMessage            [WINMM.@]
3876  */
3877 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3878 {
3879     TRACE("(%p, %d, %lx, %lx)\n", hmix, uMsg, dwParam1, dwParam2);
3880
3881     return MMSYSERR_NOTSUPPORTED;
3882 }