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