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