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