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