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