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