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