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