dinput: GetDeviceData always succeeds regardless if buffering is enabled or device...
[wine/multimedia.git] / dlls / winmm / waveform.c
blob7110f252cfa37ae3caa2fafb59f17996926543e8
1 /*
2 * Copyright 1993 Martin Ayotte
3 * 1998-2002 Eric Pouech
4 * 2011 Andrew Eikum for CodeWeavers
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.
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.
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
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <string.h>
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"
38 #include "winemm.h"
40 #include "ole2.h"
41 #include "initguid.h"
42 #include "devpkey.h"
43 #include "mmdeviceapi.h"
44 #include "audioclient.h"
45 #include "audiopolicy.h"
47 #include "wine/debug.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
51 /* FIXME: Should be localized */
52 static const WCHAR volumeW[] = {'V','o','l','u','m','e',0};
53 static const WCHAR mastervolumeW[] = {'M','a','s','t','e','r',' ','V','o','l',
54 'u','m','e',0};
55 static const WCHAR muteW[] = {'M','u','t','e',0};
57 /* HWAVE (and HMIXER) format:
59 * XXXX... 1FDD DDDD IIII IIII
60 * X = unused (must be 0)
61 * 1 = the bit is set to 1, to avoid all-zero HWAVEs
62 * F = flow direction (0 = IN, 1 = OUT)
63 * D = index into g_out_mmdevices
64 * I = index in the mmdevice's devices array
66 * Two reasons that we don't just use pointers:
67 * - HWAVEs must fit into 16 bits for compatibility with old applications.
68 * - We must be able to identify bad devices without crashing.
71 #define MAX_DEVICES 256
73 typedef struct _WINMM_CBInfo {
74 DWORD_PTR callback;
75 DWORD_PTR user;
76 DWORD flags;
77 HWAVE hwave;
78 } WINMM_CBInfo;
80 struct _WINMM_MMDevice;
81 typedef struct _WINMM_MMDevice WINMM_MMDevice;
83 typedef struct _WINMM_Device {
84 WINMM_CBInfo cb_info;
86 HWAVE handle;
88 BOOL open;
90 IMMDevice *device;
91 IAudioClient *client;
92 IAudioRenderClient *render;
93 IAudioCaptureClient *capture;
94 IAudioClock *clock;
95 IAudioStreamVolume *volume;
97 HACMSTREAM acm_handle;
98 ACMSTREAMHEADER acm_hdr;
99 UINT32 acm_offs;
101 WAVEHDR *first, *last, *playing, *loop_start;
103 BOOL stopped;
104 DWORD loop_counter;
105 UINT32 bytes_per_frame, samples_per_sec, ofs_bytes, played_frames;
107 /* stored in frames of sample rate, *not* AC::GetFrequency */
108 UINT64 last_clock_pos;
110 HANDLE event;
111 CRITICAL_SECTION lock;
113 WINMM_MMDevice *parent;
114 } WINMM_Device;
116 struct _WINMM_MMDevice {
117 WAVEOUTCAPSW out_caps; /* must not be modified outside of WINMM_InitMMDevices*/
118 WAVEINCAPSW in_caps; /* must not be modified outside of WINMM_InitMMDevices*/
119 WCHAR *dev_id;
121 ISimpleAudioVolume *volume;
123 GUID session;
125 /* HMIXER format is the same as the HWAVE format, but the I bits are
126 * replaced by the value of this counter, to keep each HMIXER unique */
127 UINT mixer_count;
129 CRITICAL_SECTION lock;
131 WINMM_Device *devices[MAX_DEVICES];
134 static WINMM_MMDevice *g_out_mmdevices;
135 static UINT g_outmmdevices_count;
137 static WINMM_MMDevice *g_in_mmdevices;
138 static UINT g_inmmdevices_count;
140 static IMMDeviceEnumerator *g_devenum;
142 static CRITICAL_SECTION g_devthread_lock;
143 static HANDLE g_devices_thread;
144 static HWND g_devices_hwnd;
146 static UINT g_devhandle_count;
147 static HANDLE *g_device_handles;
148 static WINMM_Device **g_handle_devices;
150 typedef struct _WINMM_OpenInfo {
151 HWAVE handle;
152 UINT req_device;
153 WAVEFORMATEX *format;
154 DWORD_PTR callback;
155 DWORD_PTR cb_user;
156 DWORD flags;
157 } WINMM_OpenInfo;
159 typedef struct _WINMM_ControlDetails {
160 HMIXEROBJ hmix;
161 MIXERCONTROLDETAILS *details;
162 DWORD flags;
163 } WINMM_ControlDetails;
165 static LRESULT WOD_Open(WINMM_OpenInfo *info);
166 static LRESULT WOD_Close(HWAVEOUT hwave);
167 static LRESULT WID_Open(WINMM_OpenInfo *info);
168 static LRESULT WID_Close(HWAVEIN hwave);
170 BOOL WINMM_InitWaveform(void)
172 InitializeCriticalSection(&g_devthread_lock);
173 return TRUE;
176 static inline HWAVE WINMM_MakeHWAVE(UINT mmdevice, BOOL is_out, UINT device)
178 return ULongToHandle((1 << 15) | ((!!is_out) << 14) |
179 (mmdevice << 8) | device);
182 static inline void WINMM_DecomposeHWAVE(HWAVE hwave, UINT *mmdevice_index,
183 BOOL *is_out, UINT *device_index, UINT *junk)
185 ULONG32 l = HandleToULong(hwave);
186 *device_index = l & 0xFF;
187 *mmdevice_index = (l >> 8) & 0x3F;
188 *is_out = (l >> 14) & 0x1;
189 *junk = l >> 15;
192 static void WINMM_InitDevice(WINMM_Device *device,
193 WINMM_MMDevice *parent, HWAVE hwave)
195 InitializeCriticalSection(&device->lock);
196 device->handle = hwave;
197 device->parent = parent;
200 /* finds the first unused Device, marks it as "open", and returns
201 * a pointer to the device
203 * IMPORTANT: it is the caller's responsibility to release the device's lock
204 * on success
206 static WINMM_Device *WINMM_FindUnusedDevice(BOOL is_out, UINT mmdevice_index)
208 WINMM_MMDevice *mmdevice;
209 UINT i;
211 if(is_out)
212 mmdevice = &g_out_mmdevices[mmdevice_index];
213 else
214 mmdevice = &g_in_mmdevices[mmdevice_index];
216 EnterCriticalSection(&mmdevice->lock);
217 for(i = 0; i < MAX_DEVICES; ++i){
218 WINMM_Device *device = mmdevice->devices[i];
220 if(!device){
221 device = mmdevice->devices[i] = HeapAlloc(GetProcessHeap(),
222 HEAP_ZERO_MEMORY, sizeof(WINMM_Device));
223 if(!device){
224 LeaveCriticalSection(&mmdevice->lock);
225 return NULL;
228 WINMM_InitDevice(device, mmdevice,
229 WINMM_MakeHWAVE(mmdevice_index, is_out, i));
230 EnterCriticalSection(&device->lock);
231 }else
232 EnterCriticalSection(&device->lock);
234 if(!device->open){
235 LeaveCriticalSection(&mmdevice->lock);
236 device->open = TRUE;
237 TRACE("Found free device: mmdevice: %u, device id: %u\n",
238 mmdevice_index, i);
239 return device;
242 LeaveCriticalSection(&device->lock);
245 LeaveCriticalSection(&mmdevice->lock);
247 TRACE("All devices in use: mmdevice: %u\n", mmdevice_index);
249 return NULL;
252 static inline BOOL WINMM_ValidateAndLock(WINMM_Device *device)
254 if(!device)
255 return FALSE;
257 EnterCriticalSection(&device->lock);
259 if(!device->open){
260 LeaveCriticalSection(&device->lock);
261 return FALSE;
264 return TRUE;
267 static WINMM_Device *WINMM_GetDeviceFromHWAVE(HWAVE hwave)
269 WINMM_MMDevice *mmdevice;
270 WINMM_Device *device;
271 UINT mmdevice_index, device_index, junk;
272 BOOL is_out;
274 WINMM_DecomposeHWAVE(hwave, &mmdevice_index, &is_out, &device_index, &junk);
276 if(junk != 0x1)
277 return NULL;
279 if(mmdevice_index >= (is_out ? g_outmmdevices_count : g_inmmdevices_count))
280 return NULL;
282 if(is_out)
283 mmdevice = &g_out_mmdevices[mmdevice_index];
284 else
285 mmdevice = &g_in_mmdevices[mmdevice_index];
287 EnterCriticalSection(&mmdevice->lock);
289 device = mmdevice->devices[device_index];
291 LeaveCriticalSection(&mmdevice->lock);
293 return device;
296 /* Note: NotifyClient should never be called while holding the device lock
297 * since the client may call wave* functions from within the callback. */
298 static inline void WINMM_NotifyClient(WINMM_CBInfo *info, WORD msg, DWORD_PTR param1,
299 DWORD_PTR param2)
301 DriverCallback(info->callback, info->flags, (HDRVR)info->hwave,
302 msg, info->user, param1, param2);
305 static HRESULT WINMM_GetFriendlyName(IMMDevice *device, WCHAR *out,
306 UINT outlen)
308 IPropertyStore *ps;
309 PROPVARIANT var;
310 HRESULT hr;
312 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
313 if(FAILED(hr))
314 return hr;
316 PropVariantInit(&var);
318 hr = IPropertyStore_GetValue(ps,
319 (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &var);
320 if(FAILED(hr)){
321 IPropertyStore_Release(ps);
322 return hr;
325 lstrcpynW(out, var.u.pwszVal, outlen);
327 PropVariantClear(&var);
329 IPropertyStore_Release(ps);
331 return S_OK;
334 static HRESULT WINMM_TestFormat(IAudioClient *client, DWORD rate, DWORD depth,
335 WORD channels)
337 WAVEFORMATEX fmt, *junk;
338 HRESULT hr;
340 fmt.wFormatTag = WAVE_FORMAT_PCM;
341 fmt.nChannels = channels;
342 fmt.nSamplesPerSec = rate;
343 fmt.wBitsPerSample = depth;
344 fmt.nBlockAlign = (channels * depth) / 8;
345 fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
346 fmt.cbSize = 0;
348 hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED,
349 &fmt, &junk);
350 if(SUCCEEDED(hr))
351 CoTaskMemFree(junk);
353 return hr;
356 static struct _TestFormat {
357 DWORD flag;
358 DWORD rate;
359 DWORD depth;
360 WORD channels;
361 } formats_to_test[] = {
362 { WAVE_FORMAT_1M08, 11025, 8, 1 },
363 { WAVE_FORMAT_1M16, 11025, 16, 1 },
364 { WAVE_FORMAT_1S08, 11025, 8, 2 },
365 { WAVE_FORMAT_1S16, 11025, 16, 2 },
366 { WAVE_FORMAT_2M08, 22050, 8, 1 },
367 { WAVE_FORMAT_2M16, 22050, 16, 1 },
368 { WAVE_FORMAT_2S08, 22050, 8, 2 },
369 { WAVE_FORMAT_2S16, 22050, 16, 2 },
370 { WAVE_FORMAT_4M08, 44100, 8, 1 },
371 { WAVE_FORMAT_4M16, 44100, 16, 1 },
372 { WAVE_FORMAT_4S08, 44100, 8, 2 },
373 { WAVE_FORMAT_4S16, 44100, 16, 2 },
374 { WAVE_FORMAT_48M08, 48000, 8, 1 },
375 { WAVE_FORMAT_48M16, 48000, 16, 1 },
376 { WAVE_FORMAT_48S08, 48000, 8, 2 },
377 { WAVE_FORMAT_48S16, 48000, 16, 2 },
378 { WAVE_FORMAT_96M08, 96000, 8, 1 },
379 { WAVE_FORMAT_96M16, 96000, 16, 1 },
380 { WAVE_FORMAT_96S08, 96000, 8, 2 },
381 { WAVE_FORMAT_96S16, 96000, 16, 2 },
385 static DWORD WINMM_GetSupportedFormats(IMMDevice *device)
387 DWORD flags = 0;
388 HRESULT hr;
389 struct _TestFormat *fmt;
390 IAudioClient *client;
392 hr = IMMDevice_Activate(device, &IID_IAudioClient,
393 CLSCTX_INPROC_SERVER, NULL, (void**)&client);
394 if(FAILED(hr))
395 return 0;
397 for(fmt = formats_to_test; fmt->flag; ++fmt){
398 hr = WINMM_TestFormat(client, fmt->rate, fmt->depth, fmt->channels);
399 if(hr == S_OK)
400 flags |= fmt->flag;
403 IAudioClient_Release(client);
405 return flags;
408 static HRESULT WINMM_InitMMDevice(EDataFlow flow, IMMDevice *device,
409 WINMM_MMDevice *dev, UINT index)
411 HRESULT hr;
413 if(flow == eRender){
414 dev->out_caps.wMid = 0xFF;
415 dev->out_caps.wPid = 0xFF;
416 dev->out_caps.vDriverVersion = 0x00010001;
417 dev->out_caps.dwFormats = WINMM_GetSupportedFormats(device);
418 dev->out_caps.wReserved1 = 0;
419 dev->out_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
420 WAVECAPS_SAMPLEACCURATE;
421 dev->out_caps.wChannels = 2;
422 dev->out_caps.szPname[0] = '\0';
424 hr = WINMM_GetFriendlyName(device, dev->out_caps.szPname,
425 sizeof(dev->out_caps.szPname) /
426 sizeof(*dev->out_caps.szPname));
427 if(FAILED(hr))
428 return hr;
429 }else{
430 dev->in_caps.wMid = 0xFF;
431 dev->in_caps.wPid = 0xFF;
432 dev->in_caps.vDriverVersion = 0x00010001;
433 dev->in_caps.dwFormats = WINMM_GetSupportedFormats(device);
434 dev->in_caps.wReserved1 = 0;
435 dev->in_caps.wChannels = 2;
436 dev->in_caps.szPname[0] = '\0';
438 hr = WINMM_GetFriendlyName(device, dev->in_caps.szPname,
439 sizeof(dev->in_caps.szPname) /
440 sizeof(*dev->in_caps.szPname));
441 if(FAILED(hr))
442 return hr;
445 hr = IMMDevice_GetId(device, &dev->dev_id);
446 if(FAILED(hr))
447 return hr;
449 CoCreateGuid(&dev->session);
451 InitializeCriticalSection(&dev->lock);
453 return S_OK;
456 static HRESULT WINMM_EnumDevices(WINMM_MMDevice **devices, UINT *devcount,
457 EDataFlow flow)
459 IMMDeviceCollection *devcoll;
460 HRESULT hr;
462 hr = IMMDeviceEnumerator_EnumAudioEndpoints(g_devenum, flow,
463 DEVICE_STATE_ACTIVE, &devcoll);
464 if(FAILED(hr))
465 return hr;
467 hr = IMMDeviceCollection_GetCount(devcoll, devcount);
468 if(FAILED(hr)){
469 IMMDeviceCollection_Release(devcoll);
470 return hr;
473 if(*devcount > 0){
474 UINT n, count;
475 IMMDevice *def_dev = NULL;
477 *devices = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
478 sizeof(WINMM_MMDevice) * (*devcount));
479 if(!*devices){
480 IMMDeviceCollection_Release(devcoll);
481 return E_OUTOFMEMORY;
484 count = 0;
486 /* make sure that device 0 is the default device */
487 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(g_devenum,
488 flow, eConsole, &def_dev);
489 if(SUCCEEDED(hr)){
490 WINMM_InitMMDevice(flow, def_dev, &(*devices)[0], 0);
491 count = 1;
494 for(n = 0; n < *devcount; ++n){
495 IMMDevice *device;
497 hr = IMMDeviceCollection_Item(devcoll, n, &device);
498 if(SUCCEEDED(hr)){
499 if(device != def_dev){
500 WINMM_InitMMDevice(flow, device, &(*devices)[count], count);
501 ++count;
504 IMMDevice_Release(device);
508 if(def_dev)
509 IMMDevice_Release(def_dev);
511 *devcount = count;
514 IMMDeviceCollection_Release(devcoll);
516 return S_OK;
519 static HRESULT WINMM_InitMMDevices(void)
521 HRESULT hr;
523 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
524 CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&g_devenum);
525 if(FAILED(hr))
526 return hr;
528 hr = WINMM_EnumDevices(&g_out_mmdevices, &g_outmmdevices_count, eRender);
529 if(FAILED(hr)){
530 g_outmmdevices_count = 0;
531 g_inmmdevices_count = 0;
532 return hr;
535 hr = WINMM_EnumDevices(&g_in_mmdevices, &g_inmmdevices_count, eCapture);
536 if(FAILED(hr)){
537 g_inmmdevices_count = 0;
538 return hr;
541 return S_OK;
544 static inline BOOL WINMM_IsMapper(UINT device)
546 return (device == WAVE_MAPPER || device == (UINT16)WAVE_MAPPER);
549 static MMRESULT WINMM_TryDeviceMapping(WINMM_OpenInfo *info, WORD channels,
550 DWORD freq, DWORD bits_per_samp, BOOL is_out)
552 WINMM_Device *device;
553 WAVEFORMATEX target;
554 MMRESULT mr;
555 UINT i;
557 TRACE("format: %u, channels: %u, sample rate: %u, bit depth: %u\n",
558 WAVE_FORMAT_PCM, channels, freq, bits_per_samp);
560 target.wFormatTag = WAVE_FORMAT_PCM;
561 target.nChannels = channels;
562 target.nSamplesPerSec = freq;
563 target.wBitsPerSample = bits_per_samp;
564 target.nBlockAlign = (target.nChannels * target.wBitsPerSample) / 8;
565 target.nAvgBytesPerSec = target.nSamplesPerSec * target.nBlockAlign;
566 target.cbSize = 0;
568 if(is_out)
569 mr = acmStreamOpen(NULL, NULL, info->format, &target, NULL, 0,
570 0, ACM_STREAMOPENF_QUERY);
571 else
572 mr = acmStreamOpen(NULL, NULL, &target, info->format, NULL, 0,
573 0, ACM_STREAMOPENF_QUERY);
574 if(mr != MMSYSERR_NOERROR)
575 return mr;
577 /* ACM can convert from src->dst, so try to find a device
578 * that supports dst */
579 if(is_out){
580 if(WINMM_IsMapper(info->req_device)){
581 for(i = 0; i < g_outmmdevices_count; ++i){
582 WINMM_OpenInfo l_info = *info;
583 l_info.req_device = i;
584 l_info.format = &target;
585 mr = WOD_Open(&l_info);
586 if(mr == MMSYSERR_NOERROR){
587 info->handle = l_info.handle;
588 break;
591 }else{
592 WINMM_OpenInfo l_info = *info;
593 l_info.flags &= ~WAVE_MAPPED;
594 l_info.format = &target;
595 mr = WOD_Open(&l_info);
596 if(mr == MMSYSERR_NOERROR)
597 info->handle = l_info.handle;
599 }else{
600 if(WINMM_IsMapper(info->req_device)){
601 for(i = 0; i < g_inmmdevices_count; ++i){
602 WINMM_OpenInfo l_info = *info;
603 l_info.req_device = i;
604 l_info.format = &target;
605 mr = WID_Open(&l_info);
606 if(mr == MMSYSERR_NOERROR){
607 info->handle = l_info.handle;
608 break;
611 }else{
612 WINMM_OpenInfo l_info = *info;
613 l_info.flags &= ~WAVE_MAPPED;
614 l_info.format = &target;
615 mr = WID_Open(&l_info);
616 if(mr == MMSYSERR_NOERROR)
617 info->handle = l_info.handle;
620 if(mr != MMSYSERR_NOERROR)
621 return WAVERR_BADFORMAT;
623 device = WINMM_GetDeviceFromHWAVE(info->handle);
624 if(!device)
625 return MMSYSERR_INVALHANDLE;
627 /* set up the ACM stream */
628 if(is_out)
629 mr = acmStreamOpen(&device->acm_handle, NULL, info->format, &target,
630 NULL, 0, 0, 0);
631 else
632 mr = acmStreamOpen(&device->acm_handle, NULL, &target, info->format,
633 NULL, 0, 0, 0);
634 if(mr != MMSYSERR_NOERROR){
635 if(is_out)
636 WOD_Close((HWAVEOUT)info->handle);
637 else
638 WID_Close((HWAVEIN)info->handle);
639 return mr;
642 TRACE("Success\n");
643 return MMSYSERR_NOERROR;
646 static MMRESULT WINMM_MapDevice(WINMM_OpenInfo *info, BOOL is_out)
648 UINT i;
649 MMRESULT mr;
650 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)info->format;
652 TRACE("(%p, %d)\n", info, is_out);
654 /* try to find a direct match */
655 if(is_out){
656 WINMM_OpenInfo l_info = *info;
657 if(WINMM_IsMapper(info->req_device)){
658 for(i = 0; i < g_outmmdevices_count; ++i){
659 l_info.req_device = i;
660 mr = WOD_Open(&l_info);
661 if(mr == MMSYSERR_NOERROR){
662 info->handle = l_info.handle;
663 return mr;
666 }else{
667 l_info.flags &= ~WAVE_MAPPED;
668 mr = WOD_Open(&l_info);
669 if(mr == MMSYSERR_NOERROR){
670 info->handle = l_info.handle;
671 return mr;
674 }else{
675 WINMM_OpenInfo l_info = *info;
676 if(WINMM_IsMapper(info->req_device)){
677 for(i = 0; i < g_inmmdevices_count; ++i){
678 l_info.req_device = i;
679 mr = WID_Open(&l_info);
680 if(mr == MMSYSERR_NOERROR){
681 info->handle = l_info.handle;
682 return mr;
685 }else{
686 l_info.flags &= ~WAVE_MAPPED;
687 mr = WID_Open(&l_info);
688 if(mr == MMSYSERR_NOERROR){
689 info->handle = l_info.handle;
690 return mr;
695 /* no direct match, so set up the ACM stream */
696 if(info->format->wFormatTag != WAVE_FORMAT_PCM &&
697 !(info->format->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
698 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
699 /* convert to PCM format if it's not already */
700 mr = WINMM_TryDeviceMapping(info, info->format->nChannels,
701 info->format->nSamplesPerSec, 16, is_out);
702 if(mr == MMSYSERR_NOERROR)
703 return mr;
705 mr = WINMM_TryDeviceMapping(info, info->format->nChannels,
706 info->format->nSamplesPerSec, 8, is_out);
707 if(mr == MMSYSERR_NOERROR)
708 return mr;
709 }else{
710 WORD channels;
712 /* first try just changing bit depth and channels */
713 channels = info->format->nChannels;
714 mr = WINMM_TryDeviceMapping(info, channels,
715 info->format->nSamplesPerSec, 16, is_out);
716 if(mr == MMSYSERR_NOERROR)
717 return mr;
718 mr = WINMM_TryDeviceMapping(info, channels,
719 info->format->nSamplesPerSec, 8, is_out);
720 if(mr == MMSYSERR_NOERROR)
721 return mr;
723 channels = (channels == 2) ? 1 : 2;
724 mr = WINMM_TryDeviceMapping(info, channels,
725 info->format->nSamplesPerSec, 16, is_out);
726 if(mr == MMSYSERR_NOERROR)
727 return mr;
728 mr = WINMM_TryDeviceMapping(info, channels,
729 info->format->nSamplesPerSec, 8, is_out);
730 if(mr == MMSYSERR_NOERROR)
731 return mr;
733 /* that didn't work, so now try different sample rates */
734 channels = info->format->nChannels;
735 mr = WINMM_TryDeviceMapping(info, channels, 96000, 16, is_out);
736 if(mr == MMSYSERR_NOERROR)
737 return mr;
738 mr = WINMM_TryDeviceMapping(info, channels, 48000, 16, is_out);
739 if(mr == MMSYSERR_NOERROR)
740 return mr;
741 mr = WINMM_TryDeviceMapping(info, channels, 44100, 16, is_out);
742 if(mr == MMSYSERR_NOERROR)
743 return mr;
744 mr = WINMM_TryDeviceMapping(info, channels, 22050, 16, is_out);
745 if(mr == MMSYSERR_NOERROR)
746 return mr;
747 mr = WINMM_TryDeviceMapping(info, channels, 11025, 16, is_out);
748 if(mr == MMSYSERR_NOERROR)
749 return mr;
751 channels = (channels == 2) ? 1 : 2;
752 mr = WINMM_TryDeviceMapping(info, channels, 96000, 16, is_out);
753 if(mr == MMSYSERR_NOERROR)
754 return mr;
755 mr = WINMM_TryDeviceMapping(info, channels, 48000, 16, is_out);
756 if(mr == MMSYSERR_NOERROR)
757 return mr;
758 mr = WINMM_TryDeviceMapping(info, channels, 44100, 16, is_out);
759 if(mr == MMSYSERR_NOERROR)
760 return mr;
761 mr = WINMM_TryDeviceMapping(info, channels, 22050, 16, is_out);
762 if(mr == MMSYSERR_NOERROR)
763 return mr;
764 mr = WINMM_TryDeviceMapping(info, channels, 11025, 16, is_out);
765 if(mr == MMSYSERR_NOERROR)
766 return mr;
768 channels = info->format->nChannels;
769 mr = WINMM_TryDeviceMapping(info, channels, 96000, 8, is_out);
770 if(mr == MMSYSERR_NOERROR)
771 return mr;
772 mr = WINMM_TryDeviceMapping(info, channels, 48000, 8, is_out);
773 if(mr == MMSYSERR_NOERROR)
774 return mr;
775 mr = WINMM_TryDeviceMapping(info, channels, 44100, 8, is_out);
776 if(mr == MMSYSERR_NOERROR)
777 return mr;
778 mr = WINMM_TryDeviceMapping(info, channels, 22050, 8, is_out);
779 if(mr == MMSYSERR_NOERROR)
780 return mr;
781 mr = WINMM_TryDeviceMapping(info, channels, 11025, 8, is_out);
782 if(mr == MMSYSERR_NOERROR)
783 return mr;
785 channels = (channels == 2) ? 1 : 2;
786 mr = WINMM_TryDeviceMapping(info, channels, 96000, 8, is_out);
787 if(mr == MMSYSERR_NOERROR)
788 return mr;
789 mr = WINMM_TryDeviceMapping(info, channels, 48000, 8, is_out);
790 if(mr == MMSYSERR_NOERROR)
791 return mr;
792 mr = WINMM_TryDeviceMapping(info, channels, 44100, 8, is_out);
793 if(mr == MMSYSERR_NOERROR)
794 return mr;
795 mr = WINMM_TryDeviceMapping(info, channels, 22050, 8, is_out);
796 if(mr == MMSYSERR_NOERROR)
797 return mr;
798 mr = WINMM_TryDeviceMapping(info, channels, 11025, 8, is_out);
799 if(mr == MMSYSERR_NOERROR)
800 return mr;
803 WARN("Unable to find compatible device!\n");
804 return WAVERR_BADFORMAT;
807 static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_MMDevice *mmdevice,
808 WINMM_OpenInfo *info)
810 WAVEFORMATEX *closer_fmt = NULL, fmt, *passed_fmt;
811 LRESULT ret = MMSYSERR_ERROR;
812 HRESULT hr;
814 hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id,
815 &device->device);
816 if(FAILED(hr)){
817 ERR("Device %s (%s) unavailable: %08x\n",
818 wine_dbgstr_w(mmdevice->dev_id),
819 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
820 goto error;
823 hr = IMMDevice_Activate(device->device, &IID_IAudioClient,
824 CLSCTX_INPROC_SERVER, NULL, (void**)&device->client);
825 if(FAILED(hr)){
826 ERR("Activate failed: %08x\n", hr);
827 goto error;
830 if(info->format->wFormatTag == WAVE_FORMAT_PCM){
831 /* we aren't guaranteed that the struct in lpFormat is a full
832 * WAVEFORMATEX struct, which IAC::IsFormatSupported requires */
833 passed_fmt = &fmt;
834 memcpy(passed_fmt, info->format, sizeof(PCMWAVEFORMAT));
835 passed_fmt->cbSize = 0;
836 }else
837 passed_fmt = info->format;
839 hr = IAudioClient_IsFormatSupported(device->client,
840 AUDCLNT_SHAREMODE_SHARED, passed_fmt, &closer_fmt);
841 if(closer_fmt)
842 CoTaskMemFree(closer_fmt);
843 if(FAILED(hr) && hr != AUDCLNT_E_UNSUPPORTED_FORMAT){
844 ERR("IsFormatSupported failed: %08x\n", hr);
845 goto error;
847 if(hr == S_FALSE || hr == AUDCLNT_E_UNSUPPORTED_FORMAT){
848 ret = WAVERR_BADFORMAT;
849 goto error;
851 if(info->flags & WAVE_FORMAT_QUERY){
852 ret = MMSYSERR_NOERROR;
853 goto error;
856 /* buffer size = 10 * 100000 (100 ns) = 0.1 seconds */
857 hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED,
858 AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
859 10 * 100000, 50000, passed_fmt, &device->parent->session);
860 if(FAILED(hr)){
861 ERR("Initialize failed: %08x\n", hr);
862 goto error;
865 hr = IAudioClient_GetService(device->client, &IID_IAudioClock,
866 (void**)&device->clock);
867 if(FAILED(hr)){
868 ERR("GetService failed: %08x\n", hr);
869 goto error;
872 if(!device->event){
873 device->event = CreateEventW(NULL, FALSE, FALSE, NULL);
874 if(!device->event){
875 ERR("CreateEvent failed: %08x\n", hr);
876 goto error;
879 /* As the devices thread is waiting on g_device_handles, it can
880 * only be modified from within this same thread. */
881 if(g_device_handles){
882 g_device_handles = HeapReAlloc(GetProcessHeap(), 0, g_device_handles,
883 sizeof(HANDLE) * (g_devhandle_count + 1));
884 g_handle_devices = HeapReAlloc(GetProcessHeap(), 0, g_handle_devices,
885 sizeof(WINMM_Device *) * (g_devhandle_count + 1));
886 }else{
887 g_device_handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE));
888 g_handle_devices = HeapAlloc(GetProcessHeap(), 0,
889 sizeof(WINMM_Device *));
891 g_device_handles[g_devhandle_count] = device->event;
892 g_handle_devices[g_devhandle_count] = device;
893 ++g_devhandle_count;
896 hr = IAudioClient_SetEventHandle(device->client, device->event);
897 if(FAILED(hr)){
898 ERR("SetEventHandle failed: %08x\n", hr);
899 goto error;
902 device->bytes_per_frame = info->format->nBlockAlign;
903 device->samples_per_sec = info->format->nSamplesPerSec;
905 device->played_frames = 0;
906 device->last_clock_pos = 0;
907 device->ofs_bytes = 0;
908 device->loop_counter = 0;
909 device->stopped = TRUE;
910 device->first = device->last = device->playing = device->loop_start = NULL;
912 device->cb_info.flags = HIWORD(info->flags & CALLBACK_TYPEMASK);
913 device->cb_info.callback = info->callback;
914 device->cb_info.user = info->cb_user;
915 device->cb_info.hwave = device->handle;
917 info->handle = device->handle;
919 return MMSYSERR_NOERROR;
921 error:
922 if(device->client){
923 IAudioClient_Release(device->client);
924 device->client = NULL;
926 if(device->device){
927 IMMDevice_Release(device->device);
928 device->device = NULL;
931 return ret;
934 static LRESULT WOD_Open(WINMM_OpenInfo *info)
936 WINMM_MMDevice *mmdevice;
937 WINMM_Device *device = NULL;
938 LRESULT ret = MMSYSERR_ERROR;
939 HRESULT hr;
941 TRACE("(%u, %p, %08x)\n", info->req_device, info, info->flags);
943 if(WINMM_IsMapper(info->req_device) || (info->flags & WAVE_MAPPED))
944 return WINMM_MapDevice(info, TRUE);
946 if(info->req_device >= g_outmmdevices_count)
947 return MMSYSERR_BADDEVICEID;
949 mmdevice = &g_out_mmdevices[info->req_device];
951 if(!mmdevice->out_caps.szPname[0])
952 return MMSYSERR_NOTENABLED;
954 device = WINMM_FindUnusedDevice(TRUE, info->req_device);
955 if(!device)
956 return MMSYSERR_ALLOCATED;
958 ret = WINMM_OpenDevice(device, mmdevice, info);
959 if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
960 goto error;
961 ret = MMSYSERR_ERROR;
963 hr = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
964 (void**)&device->render);
965 if(FAILED(hr)){
966 ERR("GetService failed: %08x\n", hr);
967 goto error;
970 hr = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
971 (void**)&device->volume);
972 if(FAILED(hr)){
973 ERR("GetService failed: %08x\n", hr);
974 goto error;
977 LeaveCriticalSection(&device->lock);
979 return MMSYSERR_NOERROR;
981 error:
982 if(device->device){
983 IMMDevice_Release(device->device);
984 device->device = NULL;
986 if(device->client){
987 IAudioClient_Release(device->client);
988 device->client = NULL;
990 if(device->render){
991 IAudioRenderClient_Release(device->render);
992 device->render = NULL;
994 if(device->volume){
995 IAudioStreamVolume_Release(device->volume);
996 device->volume = NULL;
998 if(device->clock){
999 IAudioClock_Release(device->clock);
1000 device->clock = NULL;
1002 device->open = FALSE;
1003 LeaveCriticalSection(&device->lock);
1004 return ret;
1007 static LRESULT WID_Open(WINMM_OpenInfo *info)
1009 WINMM_MMDevice *mmdevice;
1010 WINMM_Device *device = NULL;
1011 LRESULT ret = MMSYSERR_ERROR;
1012 HRESULT hr;
1014 TRACE("(%u, %p, %08x)\n", info->req_device, info, info->flags);
1016 if(WINMM_IsMapper(info->req_device) || info->flags & WAVE_MAPPED)
1017 return WINMM_MapDevice(info, FALSE);
1019 if(info->req_device >= g_inmmdevices_count)
1020 return MMSYSERR_BADDEVICEID;
1022 mmdevice = &g_in_mmdevices[info->req_device];
1024 if(!mmdevice->in_caps.szPname[0])
1025 return MMSYSERR_NOTENABLED;
1027 device = WINMM_FindUnusedDevice(FALSE, info->req_device);
1028 if(!device)
1029 return MMSYSERR_ALLOCATED;
1031 ret = WINMM_OpenDevice(device, mmdevice, info);
1032 if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
1033 goto error;
1034 ret = MMSYSERR_ERROR;
1036 hr = IAudioClient_GetService(device->client, &IID_IAudioCaptureClient,
1037 (void**)&device->capture);
1038 if(FAILED(hr)){
1039 ERR("GetService failed: %08x\n", hr);
1040 goto error;
1043 LeaveCriticalSection(&device->lock);
1045 return MMSYSERR_NOERROR;
1047 error:
1048 if(device->device){
1049 IMMDevice_Release(device->device);
1050 device->device = NULL;
1052 if(device->client){
1053 IAudioClient_Release(device->client);
1054 device->client = NULL;
1056 if(device->capture){
1057 IAudioCaptureClient_Release(device->capture);
1058 device->capture = NULL;
1060 if(device->clock){
1061 IAudioClock_Release(device->clock);
1062 device->clock = NULL;
1064 device->open = FALSE;
1065 LeaveCriticalSection(&device->lock);
1066 return ret;
1069 static HRESULT WINMM_CloseDevice(WINMM_Device *device)
1071 device->open = FALSE;
1073 if(!device->stopped){
1074 IAudioClient_Stop(device->client);
1075 device->stopped = TRUE;
1078 if(device->acm_handle){
1079 acmStreamClose(device->acm_handle, 0);
1080 device->acm_handle = NULL;
1083 IMMDevice_Release(device->device);
1084 device->device = NULL;
1086 IAudioClient_Release(device->client);
1087 device->client = NULL;
1089 IAudioClock_Release(device->clock);
1090 device->clock = NULL;
1092 return S_OK;
1095 static LRESULT WOD_Close(HWAVEOUT hwave)
1097 WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1099 TRACE("(%p)\n", hwave);
1101 if(!WINMM_ValidateAndLock(device))
1102 return MMSYSERR_INVALHANDLE;
1104 WINMM_CloseDevice(device);
1106 IAudioRenderClient_Release(device->render);
1107 device->render = NULL;
1109 IAudioStreamVolume_Release(device->volume);
1110 device->volume = NULL;
1112 LeaveCriticalSection(&device->lock);
1114 return MMSYSERR_NOERROR;
1117 static LRESULT WID_Close(HWAVEIN hwave)
1119 WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1121 TRACE("(%p)\n", hwave);
1123 if(!WINMM_ValidateAndLock(device))
1124 return MMSYSERR_INVALHANDLE;
1126 WINMM_CloseDevice(device);
1128 IAudioCaptureClient_Release(device->capture);
1129 device->capture = NULL;
1131 LeaveCriticalSection(&device->lock);
1133 return MMSYSERR_NOERROR;
1136 static LRESULT WINMM_PrepareHeader(HWAVE hwave, WAVEHDR *header)
1138 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1140 TRACE("(%p, %p)\n", hwave, header);
1142 if(!WINMM_ValidateAndLock(device))
1143 return MMSYSERR_INVALHANDLE;
1145 if(device->render && device->acm_handle){
1146 ACMSTREAMHEADER *ash;
1147 DWORD size;
1148 MMRESULT mr;
1150 mr = acmStreamSize(device->acm_handle, header->dwBufferLength, &size,
1151 ACM_STREAMSIZEF_SOURCE);
1152 if(mr != MMSYSERR_NOERROR){
1153 LeaveCriticalSection(&device->lock);
1154 return mr;
1157 ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + size);
1158 if(!ash){
1159 LeaveCriticalSection(&device->lock);
1160 return MMSYSERR_NOMEM;
1163 ash->cbStruct = sizeof(*ash);
1164 ash->fdwStatus = 0;
1165 ash->dwUser = (DWORD_PTR)header;
1166 ash->pbSrc = (BYTE*)header->lpData;
1167 ash->cbSrcLength = header->dwBufferLength;
1168 ash->dwSrcUser = header->dwUser;
1169 ash->pbDst = (BYTE*)ash + sizeof(ACMSTREAMHEADER);
1170 ash->cbDstLength = size;
1171 ash->dwDstUser = 0;
1173 mr = acmStreamPrepareHeader(device->acm_handle, ash, 0);
1174 if(mr != MMSYSERR_NOERROR){
1175 HeapFree(GetProcessHeap(), 0, ash);
1176 LeaveCriticalSection(&device->lock);
1177 return mr;
1180 header->reserved = (DWORD_PTR)ash;
1183 LeaveCriticalSection(&device->lock);
1185 header->dwFlags |= WHDR_PREPARED;
1186 header->dwFlags &= ~WHDR_DONE;
1188 return MMSYSERR_NOERROR;
1191 static LRESULT WINMM_UnprepareHeader(HWAVE hwave, WAVEHDR *header)
1193 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1195 TRACE("(%p, %p)\n", hwave, header);
1197 if(!WINMM_ValidateAndLock(device))
1198 return MMSYSERR_INVALHANDLE;
1200 if(device->render && device->acm_handle){
1201 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1203 acmStreamUnprepareHeader(device->acm_handle, ash, 0);
1205 HeapFree(GetProcessHeap(), 0, ash);
1208 LeaveCriticalSection(&device->lock);
1210 header->dwFlags &= ~WHDR_PREPARED;
1211 header->dwFlags |= WHDR_DONE;
1213 return MMSYSERR_NOERROR;
1216 static UINT32 WINMM_HeaderLenBytes(WINMM_Device *device, WAVEHDR *header)
1218 if(device->acm_handle){
1219 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1220 return ash->cbDstLengthUsed;
1223 return header->dwBufferLength;
1226 static UINT32 WINMM_HeaderLenFrames(WINMM_Device *device, WAVEHDR *header)
1228 return WINMM_HeaderLenBytes(device, header) / device->bytes_per_frame;
1231 static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device)
1233 HRESULT hr;
1234 WAVEHDR *first = device->first, *queue = first, *last = NULL;
1235 UINT64 clock_freq, clock_pos, clock_frames;
1236 UINT32 nloops, queue_frames = 0;
1238 hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
1239 if(FAILED(hr)){
1240 ERR("GetFrequency failed: %08x\n", hr);
1241 return NULL;
1244 hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
1245 if(FAILED(hr)){
1246 ERR("GetPosition failed: %08x\n", hr);
1247 return NULL;
1250 clock_frames = (clock_pos / (double)clock_freq) * device->samples_per_sec;
1252 nloops = device->loop_counter;
1253 while(queue &&
1254 (queue_frames += WINMM_HeaderLenFrames(device, queue)) <=
1255 clock_frames - device->last_clock_pos){
1256 if(!nloops){
1257 last = queue;
1258 device->last_clock_pos += queue_frames;
1259 queue_frames = 0;
1260 queue = device->first = queue->lpNext;
1261 }else{
1262 if(queue->dwFlags & WHDR_BEGINLOOP){
1263 if(queue->dwFlags & WHDR_ENDLOOP)
1264 --nloops;
1265 else
1266 queue = queue->lpNext;
1267 }else if(queue->dwFlags & WHDR_ENDLOOP){
1268 queue = device->loop_start;
1269 --nloops;
1274 if(last){
1275 last->lpNext = NULL;
1276 return first;
1277 }else
1278 return NULL;
1281 static void WOD_PushData(WINMM_Device *device)
1283 WINMM_CBInfo cb_info;
1284 HRESULT hr;
1285 UINT32 pad, bufsize, avail_frames, queue_frames, written, ofs;
1286 UINT32 queue_bytes, nloops;
1287 BYTE *data;
1288 WAVEHDR *queue, *first = NULL;
1290 TRACE("(%p)\n", device->handle);
1292 EnterCriticalSection(&device->lock);
1294 if(!device->device)
1295 goto exit;
1297 if(!device->first){
1298 device->stopped = TRUE;
1299 device->last_clock_pos = 0;
1300 IAudioClient_Stop(device->client);
1301 IAudioClient_Reset(device->client);
1302 goto exit;
1305 hr = IAudioClient_GetBufferSize(device->client, &bufsize);
1306 if(FAILED(hr)){
1307 ERR("GetBufferSize failed: %08x\n", hr);
1308 goto exit;
1311 hr = IAudioClient_GetCurrentPadding(device->client, &pad);
1312 if(FAILED(hr)){
1313 ERR("GetCurrentPadding failed: %08x\n", hr);
1314 goto exit;
1317 first = WOD_MarkDoneHeaders(device);
1319 /* determine which is larger between the available buffer size and
1320 * the amount of data left in the queue */
1321 avail_frames = bufsize - pad;
1323 queue = device->playing;
1324 ofs = device->ofs_bytes;
1325 queue_frames = 0;
1326 nloops = 0;
1327 while(queue && queue_frames < avail_frames){
1328 queue_bytes = WINMM_HeaderLenBytes(device, queue);
1329 queue_frames += (queue_bytes - ofs) / device->bytes_per_frame;
1330 ofs = 0;
1332 if(queue->dwFlags & WHDR_ENDLOOP && nloops < device->loop_counter){
1333 queue = device->loop_start;
1334 ++nloops;
1335 }else
1336 queue = queue->lpNext;
1339 if(avail_frames != 0 && queue_frames == 0){
1340 hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
1341 if(FAILED(hr)){
1342 ERR("GetBuffer failed: %08x\n", hr);
1343 goto exit;
1346 hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames,
1347 AUDCLNT_BUFFERFLAGS_SILENT);
1348 if(FAILED(hr)){
1349 ERR("ReleaseBuffer failed: %08x\n", hr);
1350 goto exit;
1353 goto exit;
1356 if(queue_frames < avail_frames)
1357 avail_frames = queue_frames;
1358 if(avail_frames == 0)
1359 goto exit;
1361 hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
1362 if(FAILED(hr)){
1363 ERR("GetBuffer failed: %08x\n", hr);
1364 goto exit;
1367 written = 0;
1368 while(device->playing && written < avail_frames){
1369 UINT32 copy_frames, copy_bytes;
1370 BYTE *queue_data;
1372 queue = device->playing;
1374 if(device->acm_handle){
1375 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)queue->reserved;
1376 queue_bytes = ash->cbDstLengthUsed;
1377 queue_data = ash->pbDst;
1378 }else{
1379 queue_bytes = queue->dwBufferLength;
1380 queue_data = (BYTE*)queue->lpData;
1383 queue_frames = (queue_bytes - device->ofs_bytes) /
1384 device->bytes_per_frame;
1386 copy_frames = queue_frames < (avail_frames - written) ?
1387 queue_frames : avail_frames - written;
1388 copy_bytes = copy_frames * device->bytes_per_frame;
1390 memcpy(data, queue_data + device->ofs_bytes, copy_bytes);
1392 data += copy_bytes;
1393 written += copy_frames;
1394 device->ofs_bytes += copy_bytes;
1396 if(device->ofs_bytes >= queue_bytes){
1397 device->ofs_bytes = 0;
1399 if(!(queue->dwFlags & (WHDR_BEGINLOOP | WHDR_ENDLOOP)))
1400 device->playing = queue->lpNext;
1401 else{
1402 if(queue->dwFlags & WHDR_BEGINLOOP){
1403 device->loop_start = device->playing;
1404 device->playing = queue->lpNext;
1405 device->loop_counter = queue->dwLoops;
1407 if(queue->dwFlags & WHDR_ENDLOOP){
1408 --device->loop_counter;
1409 if(device->loop_counter)
1410 device->playing = device->loop_start;
1411 else
1412 device->loop_start = device->playing = queue->lpNext;
1418 hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames, 0);
1419 if(FAILED(hr)){
1420 ERR("ReleaseBuffer failed: %08x\n", hr);
1421 goto exit;
1424 device->played_frames += avail_frames;
1426 exit:
1427 cb_info = device->cb_info;
1429 LeaveCriticalSection(&device->lock);
1431 while(first){
1432 WAVEHDR *next = first->lpNext;
1433 first->dwFlags &= ~WHDR_INQUEUE;
1434 first->dwFlags |= WHDR_DONE;
1435 WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1436 first = next;
1440 static void WID_PullACMData(WINMM_Device *device)
1442 UINT32 packet, packet_bytes;
1443 DWORD flags;
1444 BYTE *data;
1445 WAVEHDR *queue;
1446 HRESULT hr;
1447 MMRESULT mr;
1449 if(device->acm_hdr.cbDstLength == 0){
1450 hr = IAudioClient_GetCurrentPadding(device->client, &packet);
1451 if(FAILED(hr)){
1452 ERR("GetCurrentPadding failed: %08x\n", hr);
1453 return;
1456 if(packet == 0)
1457 return;
1459 hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
1460 &flags, NULL, NULL);
1461 if(FAILED(hr)){
1462 ERR("GetBuffer failed: %08x\n", hr);
1463 return;
1466 acmStreamSize(device->acm_handle, packet * device->bytes_per_frame,
1467 &packet_bytes, ACM_STREAMSIZEF_SOURCE);
1469 device->acm_offs = 0;
1471 device->acm_hdr.cbStruct = sizeof(device->acm_hdr);
1472 device->acm_hdr.fdwStatus = 0;
1473 device->acm_hdr.dwUser = 0;
1474 device->acm_hdr.pbSrc = data;
1475 device->acm_hdr.cbSrcLength = packet * device->bytes_per_frame;
1476 device->acm_hdr.cbSrcLengthUsed = 0;
1477 device->acm_hdr.dwSrcUser = 0;
1478 device->acm_hdr.pbDst = HeapAlloc(GetProcessHeap(), 0, packet_bytes);
1479 device->acm_hdr.cbDstLength = packet_bytes;
1480 device->acm_hdr.cbDstLengthUsed = 0;
1481 device->acm_hdr.dwDstUser = 0;
1483 mr = acmStreamPrepareHeader(device->acm_handle, &device->acm_hdr, 0);
1484 if(mr != MMSYSERR_NOERROR){
1485 ERR("acmStreamPrepareHeader failed: %d\n", mr);
1486 return;
1489 mr = acmStreamConvert(device->acm_handle, &device->acm_hdr, 0);
1490 if(mr != MMSYSERR_NOERROR){
1491 ERR("acmStreamConvert failed: %d\n", mr);
1492 return;
1495 hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet);
1496 if(FAILED(hr))
1497 ERR("ReleaseBuffer failed: %08x\n", hr);
1499 device->played_frames += packet;
1502 queue = device->first;
1503 while(queue){
1504 UINT32 to_copy_bytes;
1506 to_copy_bytes = min(queue->dwBufferLength - queue->dwBytesRecorded,
1507 device->acm_hdr.cbDstLengthUsed - device->acm_offs);
1509 memcpy(queue->lpData + queue->dwBytesRecorded,
1510 device->acm_hdr.pbDst + device->acm_offs, to_copy_bytes);
1512 queue->dwBytesRecorded += to_copy_bytes;
1513 device->acm_offs += to_copy_bytes;
1515 if(queue->dwBufferLength - queue->dwBytesRecorded <
1516 device->bytes_per_frame){
1517 queue->dwFlags &= ~WHDR_INQUEUE;
1518 queue->dwFlags |= WHDR_DONE;
1519 device->first = queue = queue->lpNext;
1522 if(device->acm_offs >= device->acm_hdr.cbDstLengthUsed){
1523 acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1524 HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1525 device->acm_hdr.cbDstLength = 0;
1526 device->acm_hdr.cbDstLengthUsed = 0;
1528 /* done with this ACM Header, so try to pull more data */
1529 WID_PullACMData(device);
1530 return;
1534 /* out of WAVEHDRs to write into, so toss the rest of this packet */
1535 acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1536 HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1537 device->acm_hdr.cbDstLength = 0;
1538 device->acm_hdr.cbDstLengthUsed = 0;
1541 static void WID_PullData(WINMM_Device *device)
1543 WINMM_CBInfo cb_info;
1544 WAVEHDR *queue, *first = NULL, *last = NULL;
1545 HRESULT hr;
1547 TRACE("(%p)\n", device->handle);
1549 EnterCriticalSection(&device->lock);
1551 if(!device->device || !device->first)
1552 goto exit;
1554 first = device->first;
1556 if(device->acm_handle){
1557 WID_PullACMData(device);
1558 goto exit;
1561 while(device->first){
1562 BYTE *data;
1563 UINT32 pad, packet_len, packet;
1564 DWORD flags;
1566 hr = IAudioClient_GetCurrentPadding(device->client, &pad);
1567 if(FAILED(hr)){
1568 ERR("GetCurrentPadding failed: %08x\n", hr);
1569 goto exit;
1572 if(pad == 0)
1573 goto exit;
1575 hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
1576 &flags, NULL, NULL);
1577 if(FAILED(hr)){
1578 ERR("GetBuffer failed: %08x\n", hr);
1579 goto exit;
1582 packet_len = packet;
1583 queue = device->first;
1584 while(queue && packet > 0){
1585 UINT32 to_copy_bytes;
1587 to_copy_bytes = min(packet * device->bytes_per_frame,
1588 queue->dwBufferLength - queue->dwBytesRecorded);
1590 memcpy(queue->lpData + queue->dwBytesRecorded,
1591 data + (packet_len - packet) * device->bytes_per_frame,
1592 to_copy_bytes);
1594 queue->dwBytesRecorded += to_copy_bytes;
1596 if(queue->dwBufferLength - queue->dwBytesRecorded <
1597 device->bytes_per_frame){
1598 last = queue;
1599 device->first = queue = queue->lpNext;
1602 packet -= to_copy_bytes / device->bytes_per_frame;
1605 hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet_len);
1606 if(FAILED(hr))
1607 ERR("ReleaseBuffer failed: %08x\n", hr);
1609 device->played_frames += packet_len;
1612 exit:
1613 cb_info = device->cb_info;
1615 LeaveCriticalSection(&device->lock);
1617 if(last){
1618 last->lpNext = NULL;
1619 while(first){
1620 WAVEHDR *next = first->lpNext;
1621 first->dwFlags &= ~WHDR_INQUEUE;
1622 first->dwFlags |= WHDR_DONE;
1623 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1624 first = next;
1629 static HRESULT WINMM_BeginPlaying(WINMM_Device *device)
1631 HRESULT hr;
1633 TRACE("(%p)\n", device->handle);
1635 if(device->render)
1636 /* prebuffer data before starting */
1637 WOD_PushData(device);
1639 if(device->stopped){
1640 device->stopped = FALSE;
1642 hr = IAudioClient_Start(device->client);
1643 if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){
1644 device->stopped = TRUE;
1645 ERR("Start failed: %08x\n", hr);
1646 return hr;
1650 return S_OK;
1653 static LRESULT WINMM_Pause(HWAVE hwave)
1655 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1656 HRESULT hr;
1658 TRACE("(%p)\n", hwave);
1660 if(!WINMM_ValidateAndLock(device))
1661 return MMSYSERR_INVALHANDLE;
1663 hr = IAudioClient_Stop(device->client);
1664 if(FAILED(hr)){
1665 LeaveCriticalSection(&device->lock);
1666 ERR("Stop failed: %08x\n", hr);
1667 return MMSYSERR_ERROR;
1670 device->stopped = FALSE;
1672 LeaveCriticalSection(&device->lock);
1674 return MMSYSERR_NOERROR;
1677 static LRESULT WINMM_Reset(HWAVE hwave)
1679 WINMM_CBInfo cb_info;
1680 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1681 BOOL is_out;
1682 WAVEHDR *first;
1683 HRESULT hr;
1685 TRACE("(%p)\n", hwave);
1687 if(!WINMM_ValidateAndLock(device))
1688 return MMSYSERR_INVALHANDLE;
1690 hr = IAudioClient_Stop(device->client);
1691 if(FAILED(hr)){
1692 LeaveCriticalSection(&device->lock);
1693 ERR("Stop failed: %08x\n", hr);
1694 return MMSYSERR_ERROR;
1696 device->stopped = TRUE;
1698 first = device->first;
1699 device->first = device->last = device->playing = NULL;
1700 device->ofs_bytes = 0;
1701 device->played_frames = 0;
1702 device->loop_counter = 0;
1703 device->last_clock_pos = 0;
1704 IAudioClient_Reset(device->client);
1706 cb_info = device->cb_info;
1707 is_out = device->render ? TRUE : FALSE;
1709 LeaveCriticalSection(&device->lock);
1711 while(first){
1712 WAVEHDR *next = first->lpNext;
1713 first->dwFlags &= ~WHDR_INQUEUE;
1714 first->dwFlags |= WHDR_DONE;
1715 if(is_out)
1716 WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1717 else
1718 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1719 first = next;
1722 return MMSYSERR_NOERROR;
1725 static MMRESULT WINMM_FramesToMMTime(MMTIME *time, UINT32 played_frames,
1726 UINT32 sample_rate, UINT32 bytes_per_frame)
1728 switch(time->wType){
1729 case TIME_SAMPLES:
1730 time->u.sample = played_frames;
1731 return MMSYSERR_NOERROR;
1732 case TIME_MS:
1733 time->u.ms = (DWORD)((played_frames / (double)sample_rate) * 1000);
1734 return MMSYSERR_NOERROR;
1735 case TIME_SMPTE:
1736 time->u.smpte.fps = 30;
1737 if(played_frames >= sample_rate){
1738 time->u.smpte.sec = played_frames / (double)sample_rate;
1739 time->u.smpte.min = time->u.smpte.sec / 60;
1740 time->u.smpte.hour = time->u.smpte.min / 60;
1741 time->u.smpte.sec %= 60;
1742 time->u.smpte.min %= 60;
1743 played_frames %= sample_rate;
1744 }else{
1745 time->u.smpte.sec = 0;
1746 time->u.smpte.min = 0;
1747 time->u.smpte.hour = 0;
1749 time->u.smpte.frame = (played_frames / (double)sample_rate) * 30;
1750 return MMSYSERR_NOERROR;
1751 case TIME_BYTES:
1752 default:
1753 time->wType = TIME_BYTES;
1754 time->u.cb = played_frames * bytes_per_frame;
1755 return MMSYSERR_NOERROR;
1758 return MMSYSERR_ERROR;
1761 static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time)
1763 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1764 UINT32 played_frames, sample_rate, bytes_per_frame;
1766 TRACE("(%p, %p)\n", hwave, time);
1768 if(!WINMM_ValidateAndLock(device))
1769 return MMSYSERR_INVALHANDLE;
1771 played_frames = device->played_frames;
1772 sample_rate = device->samples_per_sec;
1773 bytes_per_frame = device->bytes_per_frame;
1775 LeaveCriticalSection(&device->lock);
1777 return WINMM_FramesToMMTime(time, played_frames, sample_rate,
1778 bytes_per_frame);
1781 static WINMM_MMDevice *WINMM_GetMixerMMDevice(HMIXEROBJ hmix, DWORD flags,
1782 UINT *mmdev_index)
1784 UINT mmdev, dev, junk, *out;
1785 BOOL is_out;
1787 if(!mmdev_index)
1788 out = &mmdev;
1789 else
1790 out = mmdev_index;
1792 switch(flags & 0xF0000000){
1793 case MIXER_OBJECTF_MIXER: /* == 0 */
1794 *out = HandleToULong(hmix);
1795 if(*out < g_outmmdevices_count)
1796 return &g_out_mmdevices[*out];
1797 if(*out - g_outmmdevices_count < g_inmmdevices_count){
1798 *out -= g_outmmdevices_count;
1799 return &g_in_mmdevices[*out];
1801 /* fall through -- if it's not a valid mixer device, then
1802 * it could be a valid mixer handle. windows seems to do
1803 * this as well. */
1804 case MIXER_OBJECTF_HMIXER:
1805 case MIXER_OBJECTF_HWAVEOUT:
1806 case MIXER_OBJECTF_HWAVEIN:
1807 WINMM_DecomposeHWAVE((HWAVE)hmix, out, &is_out, &dev, &junk);
1808 if(junk != 0x1 || (is_out && *out >= g_outmmdevices_count) ||
1809 (!is_out && *out >= g_inmmdevices_count))
1810 return NULL;
1811 if(is_out)
1812 return &g_out_mmdevices[*out];
1813 return &g_in_mmdevices[*out];
1814 case MIXER_OBJECTF_WAVEOUT:
1815 *out = HandleToULong(hmix);
1816 if(*out < g_outmmdevices_count)
1817 return &g_out_mmdevices[*out];
1818 return NULL;
1819 case MIXER_OBJECTF_WAVEIN:
1820 *out = HandleToULong(hmix);
1821 if(*out < g_inmmdevices_count)
1822 return &g_in_mmdevices[*out];
1823 return NULL;
1826 return NULL;
1829 static MMRESULT WINMM_SetupMMDeviceVolume(WINMM_MMDevice *mmdevice)
1831 IAudioSessionManager *sesman;
1832 IMMDevice *device;
1833 HRESULT hr;
1835 hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id, &device);
1836 if(FAILED(hr)){
1837 ERR("Device %s (%s) unavailable: %08x\n",
1838 wine_dbgstr_w(mmdevice->dev_id),
1839 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
1840 return MMSYSERR_ERROR;
1843 hr = IMMDevice_Activate(device, &IID_IAudioSessionManager,
1844 CLSCTX_INPROC_SERVER, NULL, (void**)&sesman);
1845 if(FAILED(hr)){
1846 ERR("Activate failed: %08x\n", hr);
1847 IMMDevice_Release(device);
1848 return MMSYSERR_ERROR;
1851 IMMDevice_Release(device);
1853 hr = IAudioSessionManager_GetSimpleAudioVolume(sesman, &mmdevice->session,
1854 FALSE, &mmdevice->volume);
1855 IAudioSessionManager_Release(sesman);
1856 if(FAILED(hr)){
1857 ERR("GetSimpleAudioVolume failed: %08x\n", hr);
1858 return MMSYSERR_ERROR;
1861 return MMSYSERR_NOERROR;
1864 static LRESULT MXD_GetControlDetails(WINMM_ControlDetails *details)
1866 WINMM_MMDevice *mmdevice;
1867 MIXERCONTROLDETAILS *control = details->details;
1868 HRESULT hr;
1870 TRACE("(%p)\n", details->hmix);
1872 mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
1873 if(!mmdevice)
1874 return MMSYSERR_INVALHANDLE;
1876 EnterCriticalSection(&mmdevice->lock);
1878 if(!mmdevice->volume){
1879 MMRESULT mr;
1881 mr = WINMM_SetupMMDeviceVolume(mmdevice);
1882 if(mr != MMSYSERR_NOERROR){
1883 LeaveCriticalSection(&mmdevice->lock);
1884 return mr;
1888 if(control->dwControlID == 0){
1889 float vol;
1890 MIXERCONTROLDETAILS_UNSIGNED *udet;
1892 if(!control->paDetails ||
1893 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
1894 LeaveCriticalSection(&mmdevice->lock);
1895 return MMSYSERR_INVALPARAM;
1898 hr = ISimpleAudioVolume_GetMasterVolume(mmdevice->volume, &vol);
1899 if(FAILED(hr)){
1900 ERR("GetMasterVolume failed: %08x\n", hr);
1901 LeaveCriticalSection(&mmdevice->lock);
1902 return MMSYSERR_ERROR;
1905 udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
1906 udet->dwValue = vol * ((unsigned int)0xFFFF);
1907 }else if(control->dwControlID == 1){
1908 BOOL mute;
1909 MIXERCONTROLDETAILS_BOOLEAN *bdet;
1911 if(!control->paDetails ||
1912 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
1913 LeaveCriticalSection(&mmdevice->lock);
1914 return MMSYSERR_INVALPARAM;
1917 hr = ISimpleAudioVolume_GetMute(mmdevice->volume, &mute);
1918 if(FAILED(hr)){
1919 ERR("GetMute failed: %08x\n", hr);
1920 LeaveCriticalSection(&mmdevice->lock);
1921 return MMSYSERR_ERROR;
1924 bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
1925 bdet->fValue = mute;
1926 }else if(control->dwControlID == 2 || control->dwControlID == 3){
1927 FIXME("What should the sw-side mixer controls map to?\n");
1928 }else{
1929 LeaveCriticalSection(&mmdevice->lock);
1930 return MIXERR_INVALCONTROL;
1933 LeaveCriticalSection(&mmdevice->lock);
1935 return MMSYSERR_NOERROR;
1938 static LRESULT MXD_SetControlDetails(WINMM_ControlDetails *details)
1940 WINMM_MMDevice *mmdevice;
1941 MIXERCONTROLDETAILS *control = details->details;
1942 HRESULT hr;
1944 TRACE("(%p)\n", details->hmix);
1946 mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
1947 if(!mmdevice)
1948 return MMSYSERR_INVALHANDLE;
1950 EnterCriticalSection(&mmdevice->lock);
1952 if(!mmdevice->volume){
1953 MMRESULT mr;
1955 mr = WINMM_SetupMMDeviceVolume(mmdevice);
1956 if(mr != MMSYSERR_NOERROR){
1957 LeaveCriticalSection(&mmdevice->lock);
1958 return mr;
1962 if(control->dwControlID == 0){
1963 float vol;
1964 MIXERCONTROLDETAILS_UNSIGNED *udet;
1966 if(!control->paDetails ||
1967 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
1968 LeaveCriticalSection(&mmdevice->lock);
1969 return MMSYSERR_INVALPARAM;
1972 udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
1974 if(udet->dwValue > 65535){
1975 LeaveCriticalSection(&mmdevice->lock);
1976 return MMSYSERR_INVALPARAM;
1979 vol = udet->dwValue / 65535.f;
1981 hr = ISimpleAudioVolume_SetMasterVolume(mmdevice->volume, vol, NULL);
1982 if(FAILED(hr)){
1983 ERR("SetMasterVolume failed: %08x\n", hr);
1984 LeaveCriticalSection(&mmdevice->lock);
1985 return MMSYSERR_ERROR;
1987 }else if(control->dwControlID == 1){
1988 BOOL mute;
1989 MIXERCONTROLDETAILS_BOOLEAN *bdet;
1991 if(!control->paDetails ||
1992 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
1993 LeaveCriticalSection(&mmdevice->lock);
1994 return MMSYSERR_INVALPARAM;
1997 bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
1998 mute = bdet->fValue;
2000 hr = ISimpleAudioVolume_SetMute(mmdevice->volume, mute, NULL);
2001 if(FAILED(hr)){
2002 ERR("SetMute failed: %08x\n", hr);
2003 LeaveCriticalSection(&mmdevice->lock);
2004 return MMSYSERR_ERROR;
2006 }else if(control->dwControlID == 2 || control->dwControlID == 3){
2007 FIXME("What should the sw-side mixer controls map to?\n");
2008 }else{
2009 LeaveCriticalSection(&mmdevice->lock);
2010 return MIXERR_INVALCONTROL;
2013 LeaveCriticalSection(&mmdevice->lock);
2015 return MMSYSERR_NOERROR;
2018 static LRESULT CALLBACK WINMM_DevicesMsgProc(HWND hwnd, UINT msg, WPARAM wparam,
2019 LPARAM lparam)
2021 switch(msg){
2022 case WODM_OPEN:
2023 return WOD_Open((WINMM_OpenInfo*)wparam);
2024 case WODM_CLOSE:
2025 return WOD_Close((HWAVEOUT)wparam);
2026 case WIDM_OPEN:
2027 return WID_Open((WINMM_OpenInfo*)wparam);
2028 case WIDM_CLOSE:
2029 return WID_Close((HWAVEIN)wparam);
2030 case MXDM_GETCONTROLDETAILS:
2031 return MXD_GetControlDetails((WINMM_ControlDetails*)wparam);
2032 case MXDM_SETCONTROLDETAILS:
2033 return MXD_SetControlDetails((WINMM_ControlDetails*)wparam);
2035 return DefWindowProcW(hwnd, msg, wparam, lparam);
2038 static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
2040 HANDLE evt = arg;
2041 HRESULT hr;
2042 static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0};
2044 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
2045 if(FAILED(hr)){
2046 ERR("CoInitializeEx failed: %08x\n", hr);
2047 return 1;
2050 hr = WINMM_InitMMDevices();
2051 if(FAILED(hr)){
2052 CoUninitialize();
2053 return 1;
2056 g_devices_hwnd = CreateWindowW(messageW, NULL, 0, 0, 0, 0, 0,
2057 HWND_MESSAGE, NULL, NULL, NULL);
2058 if(!g_devices_hwnd){
2059 ERR("CreateWindow failed: %d\n", GetLastError());
2060 CoUninitialize();
2061 return 1;
2064 SetWindowLongPtrW(g_devices_hwnd, GWLP_WNDPROC,
2065 (LONG_PTR)WINMM_DevicesMsgProc);
2067 /* inform caller that the thread is ready to process messages */
2068 SetEvent(evt);
2069 evt = NULL; /* do not use after this point */
2071 while(1){
2072 DWORD wait;
2073 wait = MsgWaitForMultipleObjects(g_devhandle_count, g_device_handles,
2074 FALSE, INFINITE, QS_ALLINPUT);
2075 if(wait == g_devhandle_count + WAIT_OBJECT_0){
2076 MSG msg;
2077 if(PeekMessageW(&msg, g_devices_hwnd, 0, 0, PM_REMOVE))
2078 ERR("Unexpected message: 0x%x\n", msg.message);
2079 }else if(wait < g_devhandle_count + WAIT_OBJECT_0){
2080 WINMM_Device *device = g_handle_devices[wait - WAIT_OBJECT_0];
2081 if(device->render)
2082 WOD_PushData(device);
2083 else
2084 WID_PullData(device);
2085 }else
2086 ERR("Unexpected MsgWait result 0x%x, GLE: %d\n", wait,
2087 GetLastError());
2090 DestroyWindow(g_devices_hwnd);
2092 CoUninitialize();
2094 return 0;
2097 static BOOL WINMM_StartDevicesThread(void)
2099 HANDLE events[2];
2100 DWORD wait;
2102 EnterCriticalSection(&g_devthread_lock);
2104 if(g_devices_thread){
2105 DWORD wait;
2107 wait = WaitForSingleObject(g_devices_thread, 0);
2108 if(wait == WAIT_TIMEOUT){
2109 LeaveCriticalSection(&g_devthread_lock);
2110 return TRUE;
2112 if(wait != WAIT_OBJECT_0){
2113 LeaveCriticalSection(&g_devthread_lock);
2114 return FALSE;
2117 g_devices_thread = NULL;
2118 g_devices_hwnd = NULL;
2121 TRACE("Starting up devices thread\n");
2123 events[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
2125 g_devices_thread = CreateThread(NULL, 0, WINMM_DevicesThreadProc,
2126 events[0], 0, NULL);
2127 if(!g_devices_thread){
2128 LeaveCriticalSection(&g_devthread_lock);
2129 CloseHandle(events[0]);
2130 return FALSE;
2133 events[1] = g_devices_thread;
2134 wait = WaitForMultipleObjects(2, events, FALSE, INFINITE);
2135 CloseHandle(events[0]);
2136 if(wait != WAIT_OBJECT_0){
2137 if(wait == 1 + WAIT_OBJECT_0){
2138 CloseHandle(g_devices_thread);
2139 g_devices_thread = NULL;
2140 g_devices_hwnd = NULL;
2142 LeaveCriticalSection(&g_devthread_lock);
2143 return FALSE;
2146 LeaveCriticalSection(&g_devthread_lock);
2148 return TRUE;
2151 /**************************************************************************
2152 * waveOutGetNumDevs [WINMM.@]
2154 UINT WINAPI waveOutGetNumDevs(void)
2156 if(!WINMM_StartDevicesThread())
2157 return 0;
2159 TRACE("count: %u\n", g_outmmdevices_count);
2161 return g_outmmdevices_count;
2164 /**************************************************************************
2165 * waveOutGetDevCapsA [WINMM.@]
2167 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
2168 UINT uSize)
2170 WAVEOUTCAPSW wocW;
2171 UINT ret;
2173 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2175 if(!WINMM_StartDevicesThread())
2176 return MMSYSERR_ERROR;
2178 if(!lpCaps)
2179 return MMSYSERR_INVALPARAM;
2181 ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
2183 if (ret == MMSYSERR_NOERROR) {
2184 WAVEOUTCAPSA wocA;
2185 wocA.wMid = wocW.wMid;
2186 wocA.wPid = wocW.wPid;
2187 wocA.vDriverVersion = wocW.vDriverVersion;
2188 WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,
2189 sizeof(wocA.szPname), NULL, NULL );
2190 wocA.dwFormats = wocW.dwFormats;
2191 wocA.wChannels = wocW.wChannels;
2192 wocA.dwSupport = wocW.dwSupport;
2193 memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));
2195 return ret;
2198 /**************************************************************************
2199 * waveOutGetDevCapsW [WINMM.@]
2201 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
2202 UINT uSize)
2204 WAVEOUTCAPSW mapper_caps, *caps;
2206 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2208 if(!WINMM_StartDevicesThread())
2209 return MMSYSERR_ERROR;
2211 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2213 if(WINMM_IsMapper(uDeviceID)){
2214 /* FIXME: Should be localized */
2215 static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2216 'n','d',' ','M','a','p','p','e','r',0};
2218 mapper_caps.wMid = 0xFF;
2219 mapper_caps.wPid = 0xFF;
2220 mapper_caps.vDriverVersion = 0x00010001;
2221 mapper_caps.dwFormats = 0xFFFFFFFF;
2222 mapper_caps.wReserved1 = 0;
2223 mapper_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
2224 WAVECAPS_SAMPLEACCURATE;
2225 mapper_caps.wChannels = 2;
2226 lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2228 caps = &mapper_caps;
2229 }else{
2230 if(uDeviceID >= g_outmmdevices_count)
2231 return MMSYSERR_BADDEVICEID;
2233 caps = &g_out_mmdevices[uDeviceID].out_caps;
2236 memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2238 return MMSYSERR_NOERROR;
2241 /**************************************************************************
2242 * waveOutGetErrorTextA [WINMM.@]
2243 * waveInGetErrorTextA [WINMM.@]
2245 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2247 UINT ret;
2249 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2250 else if (uSize == 0) ret = MMSYSERR_NOERROR;
2251 else
2253 LPWSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
2254 if (!xstr) ret = MMSYSERR_NOMEM;
2255 else
2257 ret = waveOutGetErrorTextW(uError, xstr, uSize);
2258 if (ret == MMSYSERR_NOERROR)
2259 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
2260 HeapFree(GetProcessHeap(), 0, xstr);
2263 return ret;
2266 /**************************************************************************
2267 * waveOutGetErrorTextW [WINMM.@]
2268 * waveInGetErrorTextW [WINMM.@]
2270 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2272 UINT ret = MMSYSERR_BADERRNUM;
2274 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2275 else if (uSize == 0) ret = MMSYSERR_NOERROR;
2276 else if (
2277 /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
2278 * a warning for the test was always true */
2279 (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
2280 (uError >= WAVERR_BASE && uError <= WAVERR_LASTERROR)) {
2281 if (LoadStringW(hWinMM32Instance,
2282 uError, lpText, uSize) > 0) {
2283 ret = MMSYSERR_NOERROR;
2286 return ret;
2289 /**************************************************************************
2290 * waveOutOpen [WINMM.@]
2291 * All the args/structs have the same layout as the win16 equivalents
2293 MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
2294 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2295 DWORD_PTR dwInstance, DWORD dwFlags)
2297 LRESULT res;
2298 WINMM_OpenInfo info;
2299 WINMM_CBInfo cb_info;
2301 TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut, uDeviceID, lpFormat,
2302 dwCallback, dwInstance, dwFlags);
2304 if(!WINMM_StartDevicesThread())
2305 return MMSYSERR_ERROR;
2307 if(!lphWaveOut && !(dwFlags & WAVE_FORMAT_QUERY))
2308 return MMSYSERR_INVALPARAM;
2310 res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2311 if(res != MMSYSERR_NOERROR)
2312 return res;
2314 info.format = (WAVEFORMATEX*)lpFormat;
2315 info.callback = dwCallback;
2316 info.cb_user = dwInstance;
2317 info.req_device = uDeviceID;
2318 info.flags = dwFlags;
2320 res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0);
2321 if(res != MMSYSERR_NOERROR)
2322 return res;
2324 if(lphWaveOut)
2325 *lphWaveOut = (HWAVEOUT)info.handle;
2327 cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2328 cb_info.callback = dwCallback;
2329 cb_info.user = dwInstance;
2330 cb_info.hwave = info.handle;
2332 WINMM_NotifyClient(&cb_info, WOM_OPEN, 0, 0);
2334 return res;
2337 /**************************************************************************
2338 * waveOutClose [WINMM.@]
2340 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
2342 UINT res;
2343 WINMM_Device *device;
2344 WINMM_CBInfo cb_info;
2346 TRACE("(%p)\n", hWaveOut);
2348 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2350 if(!WINMM_ValidateAndLock(device))
2351 return MMSYSERR_INVALHANDLE;
2353 cb_info = device->cb_info;
2355 LeaveCriticalSection(&device->lock);
2357 res = SendMessageW(g_devices_hwnd, WODM_CLOSE, (WPARAM)hWaveOut, 0);
2359 if(res == MMSYSERR_NOERROR)
2360 WINMM_NotifyClient(&cb_info, WOM_CLOSE, 0, 0);
2362 return res;
2365 /**************************************************************************
2366 * waveOutPrepareHeader [WINMM.@]
2368 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
2369 WAVEHDR* lpWaveOutHdr, UINT uSize)
2371 TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2373 if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2374 return MMSYSERR_INVALPARAM;
2376 if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2377 return WAVERR_STILLPLAYING;
2379 return WINMM_PrepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2382 /**************************************************************************
2383 * waveOutUnprepareHeader [WINMM.@]
2385 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
2386 LPWAVEHDR lpWaveOutHdr, UINT uSize)
2388 TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2390 if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2391 return MMSYSERR_INVALPARAM;
2393 if(!(lpWaveOutHdr->dwFlags & WHDR_PREPARED))
2394 return MMSYSERR_NOERROR;
2396 if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2397 return WAVERR_STILLPLAYING;
2399 return WINMM_UnprepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2402 /**************************************************************************
2403 * waveOutWrite [WINMM.@]
2405 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, WAVEHDR *header, UINT uSize)
2407 WINMM_Device *device;
2408 HRESULT hr;
2410 TRACE("(%p, %p, %u)\n", hWaveOut, header, uSize);
2412 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2414 if(!WINMM_ValidateAndLock(device))
2415 return MMSYSERR_INVALHANDLE;
2417 if(!header->lpData || !(header->dwFlags & WHDR_PREPARED)){
2418 LeaveCriticalSection(&device->lock);
2419 return WAVERR_UNPREPARED;
2422 if(header->dwFlags & WHDR_INQUEUE){
2423 LeaveCriticalSection(&device->lock);
2424 return WAVERR_STILLPLAYING;
2427 if(device->acm_handle){
2428 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
2429 MMRESULT mr;
2431 ash->cbSrcLength = header->dwBufferLength;
2432 mr = acmStreamConvert(device->acm_handle, ash, 0);
2433 if(mr != MMSYSERR_NOERROR){
2434 LeaveCriticalSection(&device->lock);
2435 return mr;
2439 if(device->first){
2440 device->last->lpNext = header;
2441 device->last = header;
2442 if(!device->playing)
2443 device->playing = header;
2444 }else{
2445 device->playing = device->first = device->last = header;
2446 if(header->dwFlags & WHDR_BEGINLOOP){
2447 device->loop_counter = header->dwLoops;
2448 device->loop_start = header;
2452 header->lpNext = NULL;
2453 header->dwFlags &= ~WHDR_DONE;
2454 header->dwFlags |= WHDR_INQUEUE;
2456 hr = WINMM_BeginPlaying(device);
2457 if(FAILED(hr)){
2458 LeaveCriticalSection(&device->lock);
2459 return MMSYSERR_ERROR;
2462 LeaveCriticalSection(&device->lock);
2464 return MMSYSERR_NOERROR;
2467 /**************************************************************************
2468 * waveOutBreakLoop [WINMM.@]
2470 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
2472 WINMM_Device *device;
2474 TRACE("(%p)\n", hWaveOut);
2476 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2478 if(!WINMM_ValidateAndLock(device))
2479 return MMSYSERR_INVALHANDLE;
2481 device->loop_counter = 0;
2483 LeaveCriticalSection(&device->lock);
2485 return MMSYSERR_NOERROR;
2488 /**************************************************************************
2489 * waveOutPause [WINMM.@]
2491 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
2493 TRACE("(%p)\n", hWaveOut);
2495 return WINMM_Pause((HWAVE)hWaveOut);
2498 /**************************************************************************
2499 * waveOutReset [WINMM.@]
2501 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2503 TRACE("(%p)\n", hWaveOut);
2505 return WINMM_Reset((HWAVE)hWaveOut);
2508 /**************************************************************************
2509 * waveOutRestart [WINMM.@]
2511 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2513 WINMM_Device *device;
2514 HRESULT hr;
2516 TRACE("(%p)\n", hWaveOut);
2518 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2520 if(!WINMM_ValidateAndLock(device))
2521 return MMSYSERR_INVALHANDLE;
2523 device->stopped = TRUE;
2525 hr = WINMM_BeginPlaying(device);
2526 if(FAILED(hr)){
2527 LeaveCriticalSection(&device->lock);
2528 return MMSYSERR_ERROR;
2531 LeaveCriticalSection(&device->lock);
2533 return MMSYSERR_NOERROR;
2536 /**************************************************************************
2537 * waveOutGetPosition [WINMM.@]
2539 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2540 UINT uSize)
2542 TRACE("(%p, %p, %u)\n", hWaveOut, lpTime, uSize);
2544 if(!uSize || !lpTime || uSize != sizeof(MMTIME))
2545 return MMSYSERR_INVALPARAM;
2547 return WINMM_GetPosition((HWAVE)hWaveOut, lpTime);
2550 /**************************************************************************
2551 * waveOutGetPitch [WINMM.@]
2553 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2555 TRACE("(%p, %p)\n", hWaveOut, lpdw);
2556 return MMSYSERR_NOTSUPPORTED;
2559 /**************************************************************************
2560 * waveOutSetPitch [WINMM.@]
2562 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
2564 TRACE("(%p, %08x)\n", hWaveOut, dw);
2566 return MMSYSERR_NOTSUPPORTED;
2569 /**************************************************************************
2570 * waveOutGetPlaybackRate [WINMM.@]
2572 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
2574 TRACE("(%p, %p)\n", hWaveOut, lpdw);
2576 return MMSYSERR_NOTSUPPORTED;
2579 /**************************************************************************
2580 * waveOutSetPlaybackRate [WINMM.@]
2582 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
2584 TRACE("(%p, %08x)\n", hWaveOut, dw);
2586 return MMSYSERR_NOTSUPPORTED;
2589 /**************************************************************************
2590 * waveOutGetVolume [WINMM.@]
2592 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, DWORD *out)
2594 WINMM_Device *device;
2595 UINT32 channels;
2596 float *vols;
2597 HRESULT hr;
2599 TRACE("(%p, %p)\n", hWaveOut, out);
2601 if(!out)
2602 return MMSYSERR_INVALPARAM;
2604 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2606 if(!WINMM_ValidateAndLock(device))
2607 return MMSYSERR_INVALHANDLE;
2609 hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2610 if(FAILED(hr)){
2611 LeaveCriticalSection(&device->lock);
2612 ERR("GetChannelCount failed: %08x\n", hr);
2613 return MMSYSERR_ERROR;
2616 vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2617 if(!vols){
2618 LeaveCriticalSection(&device->lock);
2619 return MMSYSERR_NOMEM;
2622 hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2623 if(FAILED(hr)){
2624 LeaveCriticalSection(&device->lock);
2625 HeapFree(GetProcessHeap(), 0, vols);
2626 ERR("GetAllVolumes failed: %08x\n", hr);
2627 return MMSYSERR_ERROR;
2630 LeaveCriticalSection(&device->lock);
2632 *out = ((UINT16)(vols[0] * (DWORD)0xFFFF));
2633 if(channels > 1)
2634 *out |= ((UINT16)(vols[1] * (DWORD)0xFFFF)) << 16;
2636 HeapFree(GetProcessHeap(), 0, vols);
2638 return MMSYSERR_NOERROR;
2641 /**************************************************************************
2642 * waveOutSetVolume [WINMM.@]
2644 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD in)
2646 WINMM_Device *device;
2647 UINT32 channels;
2648 float *vols;
2649 HRESULT hr;
2651 TRACE("(%p, %08x)\n", hWaveOut, in);
2653 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2655 if(!WINMM_ValidateAndLock(device))
2656 return MMSYSERR_INVALHANDLE;
2658 hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2659 if(FAILED(hr)){
2660 LeaveCriticalSection(&device->lock);
2661 ERR("GetChannelCount failed: %08x\n", hr);
2662 return MMSYSERR_ERROR;
2665 vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2666 if(!vols){
2667 LeaveCriticalSection(&device->lock);
2668 return MMSYSERR_NOMEM;
2671 hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2672 if(FAILED(hr)){
2673 LeaveCriticalSection(&device->lock);
2674 HeapFree(GetProcessHeap(), 0, vols);
2675 ERR("GetAllVolumes failed: %08x\n", hr);
2676 return MMSYSERR_ERROR;
2679 vols[0] = (float)((DWORD)(in & 0xFFFF) / (float)0xFFFF);
2680 if(channels > 1)
2681 vols[1] = (float)((DWORD)(in >> 16) / (float)0xFFFF);
2683 hr = IAudioStreamVolume_SetAllVolumes(device->volume, channels, vols);
2684 if(FAILED(hr)){
2685 LeaveCriticalSection(&device->lock);
2686 HeapFree(GetProcessHeap(), 0, vols);
2687 ERR("SetAllVolumes failed: %08x\n", hr);
2688 return MMSYSERR_ERROR;
2691 LeaveCriticalSection(&device->lock);
2693 HeapFree(GetProcessHeap(), 0, vols);
2695 return MMSYSERR_NOERROR;
2698 /**************************************************************************
2699 * waveOutGetID [WINMM.@]
2701 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
2703 WINMM_Device *device;
2704 UINT dev, junk;
2705 BOOL is_out;
2707 TRACE("(%p, %p)\n", hWaveOut, lpuDeviceID);
2709 if(!lpuDeviceID)
2710 return MMSYSERR_INVALPARAM;
2712 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2713 if(!WINMM_ValidateAndLock(device))
2714 return MMSYSERR_INVALHANDLE;
2716 LeaveCriticalSection(&device->lock);
2718 WINMM_DecomposeHWAVE((HWAVE)hWaveOut, lpuDeviceID, &is_out, &dev, &junk);
2720 return MMSYSERR_NOERROR;
2723 static UINT WINMM_QueryInstanceIDSize(UINT device, DWORD_PTR *len, BOOL is_out)
2725 UINT count;
2726 WINMM_MMDevice *devices;
2728 TRACE("(%u, %p, %d)\n", device, len, is_out);
2730 if(is_out){
2731 count = g_outmmdevices_count;
2732 devices = g_out_mmdevices;
2733 }else{
2734 count = g_inmmdevices_count;
2735 devices = g_in_mmdevices;
2738 if(device >= count)
2739 return MMSYSERR_INVALHANDLE;
2741 *len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2743 return MMSYSERR_NOERROR;
2746 static UINT WINMM_QueryInstanceID(UINT device, WCHAR *str, DWORD_PTR len,
2747 BOOL is_out)
2749 UINT count, id_len;
2750 WINMM_MMDevice *devices;
2752 TRACE("(%u, %p, %d)\n", device, str, is_out);
2754 if(is_out){
2755 count = g_outmmdevices_count;
2756 devices = g_out_mmdevices;
2757 }else{
2758 count = g_inmmdevices_count;
2759 devices = g_in_mmdevices;
2762 if(device >= count)
2763 return MMSYSERR_INVALHANDLE;
2765 id_len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2766 if(len < id_len)
2767 return MMSYSERR_ERROR;
2769 memcpy(str, devices[device].dev_id, id_len);
2771 return MMSYSERR_NOERROR;
2774 /**************************************************************************
2775 * waveOutMessage [WINMM.@]
2777 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2778 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2780 TRACE("(%p, %u, %lx, %lx)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2782 switch(uMessage){
2783 case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
2784 return WINMM_QueryInstanceIDSize(HandleToULong(hWaveOut),
2785 (DWORD_PTR*)dwParam1, TRUE);
2786 case DRV_QUERYFUNCTIONINSTANCEID:
2787 return WINMM_QueryInstanceID(HandleToULong(hWaveOut), (WCHAR*)dwParam1, dwParam2, TRUE);
2788 case DRV_QUERYMAPPABLE:
2789 return MMSYSERR_NOERROR;
2792 return MMSYSERR_NOTSUPPORTED;
2795 /**************************************************************************
2796 * waveInGetNumDevs [WINMM.@]
2798 UINT WINAPI waveInGetNumDevs(void)
2800 if(!WINMM_StartDevicesThread())
2801 return 0;
2803 TRACE("count: %u\n", g_inmmdevices_count);
2805 return g_inmmdevices_count;
2808 /**************************************************************************
2809 * waveInGetDevCapsW [WINMM.@]
2811 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2813 WAVEINCAPSW mapper_caps, *caps;
2815 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2817 if(!WINMM_StartDevicesThread())
2818 return MMSYSERR_ERROR;
2820 if(!lpCaps)
2821 return MMSYSERR_INVALPARAM;
2823 if(WINMM_IsMapper(uDeviceID)){
2824 /* FIXME: Should be localized */
2825 static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2826 'n','d',' ','M','a','p','p','e','r',0};
2828 mapper_caps.wMid = 0xFF;
2829 mapper_caps.wPid = 0xFF;
2830 mapper_caps.vDriverVersion = 0x00010001;
2831 mapper_caps.dwFormats = 0xFFFFFFFF;
2832 mapper_caps.wReserved1 = 0;
2833 mapper_caps.wChannels = 2;
2834 lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2836 caps = &mapper_caps;
2837 }else{
2838 if(uDeviceID >= g_inmmdevices_count)
2839 return MMSYSERR_BADDEVICEID;
2841 caps = &g_in_mmdevices[uDeviceID].in_caps;
2844 memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2846 return MMSYSERR_NOERROR;
2849 /**************************************************************************
2850 * waveInGetDevCapsA [WINMM.@]
2852 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2854 UINT ret;
2855 WAVEINCAPSW wicW;
2857 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2859 if(!WINMM_StartDevicesThread())
2860 return MMSYSERR_ERROR;
2862 if(!lpCaps)
2863 return MMSYSERR_INVALPARAM;
2865 ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2867 if (ret == MMSYSERR_NOERROR) {
2868 WAVEINCAPSA wicA;
2869 wicA.wMid = wicW.wMid;
2870 wicA.wPid = wicW.wPid;
2871 wicA.vDriverVersion = wicW.vDriverVersion;
2872 WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2873 sizeof(wicA.szPname), NULL, NULL );
2874 wicA.dwFormats = wicW.dwFormats;
2875 wicA.wChannels = wicW.wChannels;
2876 memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2878 return ret;
2881 /**************************************************************************
2882 * waveInOpen [WINMM.@]
2884 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2885 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2886 DWORD_PTR dwInstance, DWORD dwFlags)
2888 LRESULT res;
2889 WINMM_OpenInfo info;
2890 WINMM_CBInfo cb_info;
2892 TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
2893 dwCallback, dwInstance, dwFlags);
2895 if(!WINMM_StartDevicesThread())
2896 return MMSYSERR_ERROR;
2898 if(!lphWaveIn && !(dwFlags & WAVE_FORMAT_QUERY))
2899 return MMSYSERR_INVALPARAM;
2901 res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2902 if(res != MMSYSERR_NOERROR)
2903 return res;
2905 info.format = (WAVEFORMATEX*)lpFormat;
2906 info.callback = dwCallback;
2907 info.cb_user = dwInstance;
2908 info.req_device = uDeviceID;
2909 info.flags = dwFlags;
2911 res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
2912 if(res != MMSYSERR_NOERROR)
2913 return res;
2915 if(lphWaveIn)
2916 *lphWaveIn = (HWAVEIN)info.handle;
2918 cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2919 cb_info.callback = dwCallback;
2920 cb_info.user = dwInstance;
2921 cb_info.hwave = info.handle;
2923 WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
2925 return res;
2928 /**************************************************************************
2929 * waveInClose [WINMM.@]
2931 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2933 WINMM_Device *device;
2934 WINMM_CBInfo cb_info;
2935 UINT res;
2937 TRACE("(%p)\n", hWaveIn);
2939 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
2941 if(!WINMM_ValidateAndLock(device))
2942 return MMSYSERR_INVALHANDLE;
2944 cb_info = device->cb_info;
2946 LeaveCriticalSection(&device->lock);
2948 res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
2950 if(res == MMSYSERR_NOERROR)
2951 WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
2953 return res;
2956 /**************************************************************************
2957 * waveInPrepareHeader [WINMM.@]
2959 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2960 UINT uSize)
2962 TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2964 if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2965 return MMSYSERR_INVALPARAM;
2967 if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2968 return WAVERR_STILLPLAYING;
2970 return WINMM_PrepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
2973 /**************************************************************************
2974 * waveInUnprepareHeader [WINMM.@]
2976 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2977 UINT uSize)
2979 TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2981 if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2982 return MMSYSERR_INVALPARAM;
2984 if(!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
2985 return MMSYSERR_NOERROR;
2987 if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2988 return WAVERR_STILLPLAYING;
2990 return WINMM_UnprepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
2993 /**************************************************************************
2994 * waveInAddBuffer [WINMM.@]
2996 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn, WAVEHDR *header, UINT uSize)
2998 WINMM_Device *device;
3000 TRACE("(%p, %p, %u)\n", hWaveIn, header, uSize);
3002 if(!header || uSize < sizeof(WAVEHDR))
3003 return MMSYSERR_INVALPARAM;
3005 if(!(header->dwFlags & WHDR_PREPARED))
3006 return WAVERR_UNPREPARED;
3008 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3010 if(!WINMM_ValidateAndLock(device))
3011 return MMSYSERR_INVALHANDLE;
3013 if(!device->first)
3014 device->first = device->last = header;
3015 else{
3016 device->last->lpNext = header;
3017 device->last = header;
3020 header->dwBytesRecorded = 0;
3021 header->lpNext = NULL;
3022 header->dwFlags &= ~WHDR_DONE;
3023 header->dwFlags |= WHDR_INQUEUE;
3025 LeaveCriticalSection(&device->lock);
3027 return MMSYSERR_NOERROR;
3030 /**************************************************************************
3031 * waveInReset [WINMM.@]
3033 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
3035 TRACE("(%p)\n", hWaveIn);
3037 return WINMM_Reset((HWAVE)hWaveIn);
3040 /**************************************************************************
3041 * waveInStart [WINMM.@]
3043 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
3045 WINMM_Device *device;
3046 HRESULT hr;
3048 TRACE("(%p)\n", hWaveIn);
3050 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3052 if(!WINMM_ValidateAndLock(device))
3053 return MMSYSERR_INVALHANDLE;
3055 hr = WINMM_BeginPlaying(device);
3056 if(FAILED(hr)){
3057 LeaveCriticalSection(&device->lock);
3058 return MMSYSERR_ERROR;
3061 LeaveCriticalSection(&device->lock);
3063 return MMSYSERR_NOERROR;
3066 /**************************************************************************
3067 * waveInStop [WINMM.@]
3069 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
3071 WINMM_CBInfo cb_info;
3072 WINMM_Device *device;
3073 WAVEHDR *buf;
3074 HRESULT hr;
3076 TRACE("(%p)\n", hWaveIn);
3078 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3080 if(!WINMM_ValidateAndLock(device))
3081 return MMSYSERR_INVALHANDLE;
3083 hr = WINMM_Pause((HWAVE)hWaveIn);
3084 if(FAILED(hr)){
3085 LeaveCriticalSection(&device->lock);
3086 return MMSYSERR_ERROR;
3088 device->stopped = TRUE;
3090 buf = device->first;
3091 if(buf && buf->dwBytesRecorded > 0){
3092 device->first = buf->lpNext;
3093 }else
3094 buf = NULL;
3096 cb_info = device->cb_info;
3098 LeaveCriticalSection(&device->lock);
3100 if(buf){
3101 buf->dwFlags &= ~WHDR_INQUEUE;
3102 buf->dwFlags |= WHDR_DONE;
3103 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
3106 return MMSYSERR_NOERROR;
3109 /**************************************************************************
3110 * waveInGetPosition [WINMM.@]
3112 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
3113 UINT uSize)
3115 TRACE("(%p, %p, %u)\n", hWaveIn, lpTime, uSize);
3117 if(!uSize || !lpTime || uSize != sizeof(MMTIME))
3118 return MMSYSERR_INVALPARAM;
3120 return WINMM_GetPosition((HWAVE)hWaveIn, lpTime);
3123 /**************************************************************************
3124 * waveInGetID [WINMM.@]
3126 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
3128 UINT dev, junk;
3129 BOOL is_out;
3130 WINMM_Device *device;
3132 TRACE("(%p, %p)\n", hWaveIn, lpuDeviceID);
3134 if(!lpuDeviceID)
3135 return MMSYSERR_INVALPARAM;
3137 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3138 if(!WINMM_ValidateAndLock(device))
3139 return MMSYSERR_INVALHANDLE;
3141 LeaveCriticalSection(&device->lock);
3143 WINMM_DecomposeHWAVE((HWAVE)hWaveIn, lpuDeviceID, &is_out, &dev, &junk);
3145 return MMSYSERR_NOERROR;
3148 /**************************************************************************
3149 * waveInMessage [WINMM.@]
3151 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
3152 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3154 TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
3156 switch(uMessage){
3157 case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
3158 return WINMM_QueryInstanceIDSize(HandleToULong(hWaveIn),
3159 (DWORD_PTR*)dwParam1, FALSE);
3160 case DRV_QUERYFUNCTIONINSTANCEID:
3161 return WINMM_QueryInstanceID(HandleToULong(hWaveIn), (WCHAR*)dwParam1, dwParam2, FALSE);
3162 case DRV_QUERYMAPPABLE:
3163 return MMSYSERR_NOERROR;
3166 return MMSYSERR_NOTSUPPORTED;
3169 UINT WINAPI mixerGetNumDevs(void)
3171 TRACE("\n");
3173 if(!WINMM_StartDevicesThread())
3174 return 0;
3176 return g_outmmdevices_count + g_inmmdevices_count;
3179 /**************************************************************************
3180 * mixerGetDevCapsA [WINMM.@]
3182 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
3184 MIXERCAPSW micW;
3185 UINT ret;
3187 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3189 if(!lpCaps)
3190 return MMSYSERR_INVALPARAM;
3192 ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
3194 if (ret == MMSYSERR_NOERROR) {
3195 MIXERCAPSA micA;
3196 micA.wMid = micW.wMid;
3197 micA.wPid = micW.wPid;
3198 micA.vDriverVersion = micW.vDriverVersion;
3199 WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
3200 sizeof(micA.szPname), NULL, NULL );
3201 micA.fdwSupport = micW.fdwSupport;
3202 micA.cDestinations = micW.cDestinations;
3203 memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
3205 return ret;
3208 /**************************************************************************
3209 * mixerGetDevCapsW [WINMM.@]
3211 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
3213 WINMM_MMDevice *mmdevice;
3214 MIXERCAPSW caps;
3216 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3218 if(!WINMM_StartDevicesThread())
3219 return MMSYSERR_ERROR;
3221 if(!lpCaps)
3222 return MMSYSERR_INVALPARAM;
3224 if(!uSize)
3225 return MMSYSERR_NOERROR;
3227 if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3228 return MMSYSERR_BADDEVICEID;
3230 if(uDeviceID < g_outmmdevices_count){
3231 mmdevice = &g_out_mmdevices[uDeviceID];
3232 memcpy(caps.szPname, mmdevice->out_caps.szPname, sizeof(caps.szPname));
3233 }else{
3234 mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3235 memcpy(caps.szPname, mmdevice->in_caps.szPname, sizeof(caps.szPname));
3238 caps.wMid = 0xFF;
3239 caps.wPid = 0xFF;
3240 caps.vDriverVersion = 0x00010001;
3241 caps.fdwSupport = 0;
3242 caps.cDestinations = 1;
3244 memcpy(lpCaps, &caps, uSize);
3246 return MMSYSERR_NOERROR;
3249 /**************************************************************************
3250 * mixerOpen [WINMM.@]
3252 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
3253 DWORD_PTR dwInstance, DWORD fdwOpen)
3255 WINMM_MMDevice *mmdevice;
3256 MMRESULT mr;
3258 TRACE("(%p, %d, %lx, %lx, %x)\n", lphMix, uDeviceID, dwCallback,
3259 dwInstance, fdwOpen);
3261 if(!WINMM_StartDevicesThread())
3262 return MMSYSERR_ERROR;
3264 if(!lphMix)
3265 return MMSYSERR_INVALPARAM;
3267 mr = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
3268 if(mr != MMSYSERR_NOERROR)
3269 return mr;
3271 if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3272 return MMSYSERR_BADDEVICEID;
3274 if(uDeviceID < g_outmmdevices_count){
3275 mmdevice = &g_out_mmdevices[uDeviceID];
3276 *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID, TRUE,
3277 mmdevice->mixer_count);
3278 }else{
3279 mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3280 *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID - g_outmmdevices_count,
3281 FALSE, mmdevice->mixer_count);
3284 ++mmdevice->mixer_count;
3286 return MMSYSERR_NOERROR;
3289 /**************************************************************************
3290 * mixerClose [WINMM.@]
3292 UINT WINAPI mixerClose(HMIXER hMix)
3294 TRACE("(%p)\n", hMix);
3296 return MMSYSERR_NOERROR;
3299 /**************************************************************************
3300 * mixerGetID [WINMM.@]
3302 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
3304 WINMM_MMDevice *mmdevice;
3306 TRACE("(%p, %p, %x)\n", hmix, lpid, fdwID);
3308 if(!WINMM_StartDevicesThread())
3309 return MMSYSERR_ERROR;
3311 if(!lpid)
3312 return MMSYSERR_INVALPARAM;
3314 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwID, lpid);
3315 if(!mmdevice)
3316 return MMSYSERR_INVALHANDLE;
3318 if(mmdevice->in_caps.szPname[0] != '\0')
3319 *lpid += g_outmmdevices_count;
3321 return MMSYSERR_NOERROR;
3324 /**************************************************************************
3325 * mixerGetControlDetailsW [WINMM.@]
3327 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
3328 DWORD fdwDetails)
3330 WINMM_ControlDetails details;
3332 TRACE("(%p, %p, %x)\n", hmix, lpmcdW, fdwDetails);
3334 if(!WINMM_StartDevicesThread())
3335 return MMSYSERR_ERROR;
3337 if(!lpmcdW)
3338 return MMSYSERR_INVALPARAM;
3340 TRACE("dwControlID: %u\n", lpmcdW->dwControlID);
3342 details.hmix = hmix;
3343 details.details = lpmcdW;
3344 details.flags = fdwDetails;
3346 return SendMessageW(g_devices_hwnd, MXDM_GETCONTROLDETAILS,
3347 (DWORD_PTR)&details, 0);
3350 /**************************************************************************
3351 * mixerGetControlDetailsA [WINMM.@]
3353 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
3354 DWORD fdwDetails)
3356 UINT ret = MMSYSERR_NOTSUPPORTED;
3358 TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
3360 if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
3361 return MMSYSERR_INVALPARAM;
3363 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
3364 case MIXER_GETCONTROLDETAILSF_VALUE:
3365 /* can safely use A structure as it is, no string inside */
3366 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3367 break;
3368 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
3370 MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = lpmcdA->paDetails;
3371 MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
3372 int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3373 unsigned int i;
3375 if (lpmcdA->u.cMultipleItems != 0) {
3376 size *= lpmcdA->u.cMultipleItems;
3378 pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
3379 lpmcdA->paDetails = pDetailsW;
3380 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3381 /* set up lpmcd->paDetails */
3382 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3383 /* copy from lpmcd->paDetails back to paDetailsW; */
3384 if (ret == MMSYSERR_NOERROR) {
3385 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
3386 pDetailsA->dwParam1 = pDetailsW->dwParam1;
3387 pDetailsA->dwParam2 = pDetailsW->dwParam2;
3388 WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
3389 pDetailsA->szName,
3390 sizeof(pDetailsA->szName), NULL, NULL );
3391 pDetailsA++;
3392 pDetailsW++;
3394 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3395 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3397 HeapFree(GetProcessHeap(), 0, pDetailsW);
3398 lpmcdA->paDetails = pDetailsA;
3399 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
3401 break;
3402 default:
3403 ERR("Unsupported fdwDetails=0x%08x\n", fdwDetails);
3406 return ret;
3409 /**************************************************************************
3410 * mixerGetLineControlsA [WINMM.@]
3412 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
3413 DWORD fdwControls)
3415 MIXERLINECONTROLSW mlcW;
3416 DWORD ret;
3417 unsigned int i;
3419 TRACE("(%p, %p, %x)\n", hmix, lpmlcA, fdwControls);
3421 if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
3422 lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
3423 return MMSYSERR_INVALPARAM;
3425 mlcW.cbStruct = sizeof(mlcW);
3426 mlcW.dwLineID = lpmlcA->dwLineID;
3427 mlcW.u.dwControlID = lpmlcA->u.dwControlID;
3428 mlcW.u.dwControlType = lpmlcA->u.dwControlType;
3430 /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
3431 the control count is assumed to be 1 - This is relied upon by a game,
3432 "Dynomite Deluze" */
3433 if (MIXER_GETLINECONTROLSF_ONEBYTYPE == (fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK)) {
3434 mlcW.cControls = 1;
3435 } else {
3436 mlcW.cControls = lpmlcA->cControls;
3438 mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
3439 mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
3440 mlcW.cControls * mlcW.cbmxctrl);
3442 ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
3444 if (ret == MMSYSERR_NOERROR) {
3445 lpmlcA->dwLineID = mlcW.dwLineID;
3446 lpmlcA->u.dwControlID = mlcW.u.dwControlID;
3447 lpmlcA->u.dwControlType = mlcW.u.dwControlType;
3449 for (i = 0; i < mlcW.cControls; i++) {
3450 lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
3451 lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
3452 lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
3453 lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
3454 lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
3455 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
3456 lpmlcA->pamxctrl[i].szShortName,
3457 sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
3458 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
3459 lpmlcA->pamxctrl[i].szName,
3460 sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
3461 /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
3462 * sizeof(mlcW.pamxctrl[i].Bounds) */
3463 memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
3464 sizeof(mlcW.pamxctrl[i].Bounds));
3465 /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
3466 * sizeof(mlcW.pamxctrl[i].Metrics) */
3467 memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
3468 sizeof(mlcW.pamxctrl[i].Metrics));
3472 HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
3474 return ret;
3477 static UINT WINMM_GetVolumeLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3478 MIXERCONTROLW *ctl, DWORD flags)
3480 ctl->dwControlID = (line == 0xFFFF0000) ? 0 : 2;
3481 ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
3482 ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3483 ctl->cMultipleItems = 0;
3484 lstrcpyW(ctl->szShortName, volumeW);
3485 lstrcpyW(ctl->szName, volumeW);
3486 ctl->Bounds.s1.dwMinimum = 0;
3487 ctl->Bounds.s1.dwMaximum = 0xFFFF;
3488 ctl->Metrics.cSteps = 192;
3490 return MMSYSERR_NOERROR;
3493 static UINT WINMM_GetMuteLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3494 MIXERCONTROLW *ctl, DWORD flags)
3496 ctl->dwControlID = (line == 0xFFFF0000) ? 1 : 3;
3497 ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
3498 ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3499 ctl->cMultipleItems = 0;
3500 lstrcpyW(ctl->szShortName, muteW);
3501 lstrcpyW(ctl->szName, muteW);
3502 ctl->Bounds.s1.dwMinimum = 0;
3503 ctl->Bounds.s1.dwMaximum = 1;
3504 ctl->Metrics.cSteps = 0;
3506 return MMSYSERR_NOERROR;
3509 /**************************************************************************
3510 * mixerGetLineControlsW [WINMM.@]
3512 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
3513 DWORD fdwControls)
3515 WINMM_MMDevice *mmdevice;
3517 TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
3519 if(!WINMM_StartDevicesThread())
3520 return MMSYSERR_ERROR;
3522 if(fdwControls & ~(MIXER_GETLINECONTROLSF_ALL |
3523 MIXER_GETLINECONTROLSF_ONEBYID |
3524 MIXER_GETLINECONTROLSF_ONEBYTYPE |
3525 MIXER_OBJECTF_HMIXER |
3526 MIXER_OBJECTF_MIXER)){
3527 WARN("Unknown GetLineControls flag: %x\n", fdwControls);
3528 return MMSYSERR_INVALFLAG;
3531 if(!lpmlcW || lpmlcW->cbStruct < sizeof(*lpmlcW) || !lpmlcW->pamxctrl)
3532 return MMSYSERR_INVALPARAM;
3534 TRACE("dwLineID: %u\n", lpmlcW->dwLineID);
3535 TRACE("dwControl: %x\n", lpmlcW->u.dwControlID);
3536 TRACE("cControls: %u\n", lpmlcW->cControls);
3538 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwControls, NULL);
3539 if(!mmdevice)
3540 return MMSYSERR_INVALHANDLE;
3542 switch(fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK){
3543 case MIXER_GETLINECONTROLSF_ALL:
3544 if(lpmlcW->cControls != 2)
3545 return MMSYSERR_INVALPARAM;
3546 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3547 return MMSYSERR_INVALPARAM;
3548 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3549 return MIXERR_INVALLINE;
3550 WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3551 &lpmlcW->pamxctrl[0], fdwControls);
3552 WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3553 &lpmlcW->pamxctrl[1], fdwControls);
3554 return MMSYSERR_NOERROR;
3555 case MIXER_GETLINECONTROLSF_ONEBYID:
3556 if(lpmlcW->cControls != 1)
3557 return MMSYSERR_INVALPARAM;
3558 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3559 return MMSYSERR_INVALPARAM;
3560 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3561 return MIXERR_INVALLINE;
3562 if(lpmlcW->u.dwControlID == 0)
3563 return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3564 lpmlcW->pamxctrl, fdwControls);
3565 if(lpmlcW->u.dwControlID == 1)
3566 return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3567 lpmlcW->pamxctrl, fdwControls);
3568 return MMSYSERR_NOTSUPPORTED;
3569 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
3570 if(lpmlcW->cControls != 1)
3571 return MMSYSERR_INVALPARAM;
3572 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3573 return MMSYSERR_INVALPARAM;
3574 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3575 return MIXERR_INVALLINE;
3576 if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
3577 return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3578 lpmlcW->pamxctrl, fdwControls);
3579 if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
3580 return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3581 lpmlcW->pamxctrl, fdwControls);
3582 return MMSYSERR_NOTSUPPORTED;
3585 return MMSYSERR_NOTSUPPORTED;
3588 static UINT WINMM_GetSourceLineInfo(WINMM_MMDevice *mmdevice, UINT mmdev_index,
3589 MIXERLINEW *info, DWORD flags)
3591 BOOL is_out = TRUE;
3592 if(mmdevice->in_caps.szPname[0] != '\0')
3593 is_out = FALSE;
3595 if(info->dwSource != 0)
3596 return MIXERR_INVALLINE;
3598 info->dwDestination = 0;
3599 info->dwLineID = 0;
3600 info->fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
3601 info->cConnections = 0;
3602 info->cControls = 2;
3603 /* volume & mute always affect all channels, so claim 1 channel */
3604 info->cChannels = 1;
3605 info->Target.dwDeviceID = mmdev_index;
3606 info->Target.wMid = ~0;
3607 info->Target.wPid = ~0;
3608 info->Target.vDriverVersion = 0;
3610 lstrcpyW(info->szShortName, volumeW);
3611 lstrcpyW(info->szName, mastervolumeW);
3613 if(is_out){
3614 info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
3615 info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
3616 memcpy(info->Target.szPname, mmdevice->out_caps.szPname,
3617 sizeof(info->Target.szPname));
3618 }else{
3619 info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
3620 info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3621 info->Target.szPname[0] = '\0';
3624 return MMSYSERR_NOERROR;
3627 static UINT WINMM_GetDestinationLineInfo(WINMM_MMDevice *mmdevice,
3628 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3630 BOOL is_out = TRUE;
3631 if(mmdevice->in_caps.szPname[0] != '\0')
3632 is_out = FALSE;
3634 if(info->dwDestination != 0)
3635 return MIXERR_INVALLINE;
3637 info->dwSource = 0xFFFFFFFF;
3638 info->dwLineID = 0xFFFF0000;
3639 info->fdwLine = MIXERLINE_LINEF_ACTIVE;
3640 info->cConnections = 1;
3641 info->cControls = 2;
3643 lstrcpyW(info->szShortName, volumeW);
3644 lstrcpyW(info->szName, mastervolumeW);
3646 info->Target.dwDeviceID = mmdev_index;
3647 info->Target.wMid = ~0;
3648 info->Target.wPid = ~0;
3649 info->Target.vDriverVersion = 0;
3651 if(is_out){
3652 info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
3653 info->cChannels = mmdevice->out_caps.wChannels;
3654 info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3655 info->Target.szPname[0] = '\0';
3656 }else{
3657 info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
3658 info->cChannels = mmdevice->in_caps.wChannels;
3659 info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
3660 memcpy(info->Target.szPname, mmdevice->in_caps.szPname,
3661 sizeof(info->Target.szPname));
3664 return MMSYSERR_NOERROR;
3667 static UINT WINMM_GetComponentTypeLineInfo(WINMM_MMDevice *mmdevice,
3668 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3670 BOOL is_out = TRUE;
3671 if(mmdevice->in_caps.szPname[0] != '\0')
3672 is_out = FALSE;
3674 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN){
3675 if(is_out)
3676 return MIXERR_INVALLINE;
3677 info->dwDestination = 0;
3678 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3681 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS){
3682 if(!is_out)
3683 return MIXERR_INVALLINE;
3684 info->dwDestination = 0;
3685 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3688 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE){
3689 if(is_out)
3690 return MIXERR_INVALLINE;
3691 info->dwSource = 0;
3692 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3695 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
3696 if(!is_out)
3697 return MIXERR_INVALLINE;
3698 info->dwSource = 0;
3699 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3702 TRACE("Returning INVALLINE on this component type: %u\n",
3703 info->dwComponentType);
3705 return MIXERR_INVALLINE;
3708 static UINT WINMM_GetLineIDLineInfo(WINMM_MMDevice *mmdevice,
3709 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3711 if(info->dwLineID == 0xFFFF0000){
3712 info->dwDestination = 0;
3713 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3716 if(info->dwLineID == 0){
3717 info->dwSource = 0;
3718 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3721 TRACE("Returning INVALLINE on this dwLineID: %u\n", info->dwLineID);
3722 return MIXERR_INVALLINE;
3725 /**************************************************************************
3726 * mixerGetLineInfoW [WINMM.@]
3728 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
3730 UINT mmdev_index;
3731 WINMM_MMDevice *mmdevice;
3733 TRACE("(%p, %p, %x)\n", hmix, lpmliW, fdwInfo);
3735 if(!WINMM_StartDevicesThread())
3736 return MMSYSERR_ERROR;
3738 if(!lpmliW || lpmliW->cbStruct < sizeof(MIXERLINEW))
3739 return MMSYSERR_INVALPARAM;
3741 TRACE("dwDestination: %u\n", lpmliW->dwDestination);
3742 TRACE("dwSource: %u\n", lpmliW->dwSource);
3743 TRACE("dwLineID: %u\n", lpmliW->dwLineID);
3744 TRACE("fdwLine: 0x%x\n", lpmliW->fdwLine);
3745 TRACE("dwComponentType: 0x%x\n", lpmliW->dwComponentType);
3747 if(fdwInfo & ~(MIXER_GETLINEINFOF_COMPONENTTYPE |
3748 MIXER_GETLINEINFOF_DESTINATION |
3749 MIXER_GETLINEINFOF_LINEID |
3750 MIXER_GETLINEINFOF_SOURCE |
3751 MIXER_GETLINEINFOF_TARGETTYPE |
3752 MIXER_OBJECTF_HMIXER |
3753 MIXER_OBJECTF_MIXER)){
3754 WARN("Unknown GetLineInfo flag: %x\n", fdwInfo);
3755 return MMSYSERR_INVALFLAG;
3758 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwInfo, &mmdev_index);
3759 if(!mmdevice)
3760 return MMSYSERR_INVALHANDLE;
3762 switch(fdwInfo & MIXER_GETLINEINFOF_QUERYMASK){
3763 case MIXER_GETLINEINFOF_DESTINATION:
3764 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, lpmliW,
3765 fdwInfo);
3766 case MIXER_GETLINEINFOF_SOURCE:
3767 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3768 case MIXER_GETLINEINFOF_COMPONENTTYPE:
3769 return WINMM_GetComponentTypeLineInfo(mmdevice, mmdev_index, lpmliW,
3770 fdwInfo);
3771 case MIXER_GETLINEINFOF_LINEID:
3772 return WINMM_GetLineIDLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3773 case MIXER_GETLINEINFOF_TARGETTYPE:
3774 FIXME("TARGETTYPE flag not implemented!\n");
3775 return MIXERR_INVALLINE;
3778 TRACE("Returning INVALFLAG on these flags: %lx\n",
3779 fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
3780 return MMSYSERR_INVALFLAG;
3783 /**************************************************************************
3784 * mixerGetLineInfoA [WINMM.@]
3786 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
3787 DWORD fdwInfo)
3789 MIXERLINEW mliW;
3790 UINT ret;
3792 TRACE("(%p, %p, %x)\n", hmix, lpmliA, fdwInfo);
3794 if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
3795 return MMSYSERR_INVALPARAM;
3797 mliW.cbStruct = sizeof(mliW);
3798 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
3799 case MIXER_GETLINEINFOF_COMPONENTTYPE:
3800 mliW.dwComponentType = lpmliA->dwComponentType;
3801 break;
3802 case MIXER_GETLINEINFOF_DESTINATION:
3803 mliW.dwDestination = lpmliA->dwDestination;
3804 break;
3805 case MIXER_GETLINEINFOF_LINEID:
3806 mliW.dwLineID = lpmliA->dwLineID;
3807 break;
3808 case MIXER_GETLINEINFOF_SOURCE:
3809 mliW.dwDestination = lpmliA->dwDestination;
3810 mliW.dwSource = lpmliA->dwSource;
3811 break;
3812 case MIXER_GETLINEINFOF_TARGETTYPE:
3813 mliW.Target.dwType = lpmliA->Target.dwType;
3814 mliW.Target.wMid = lpmliA->Target.wMid;
3815 mliW.Target.wPid = lpmliA->Target.wPid;
3816 mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
3817 MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname)/sizeof(WCHAR));
3818 break;
3819 default:
3820 WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
3821 return MMSYSERR_INVALFLAG;
3824 ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
3826 if(ret == MMSYSERR_NOERROR)
3828 lpmliA->dwDestination = mliW.dwDestination;
3829 lpmliA->dwSource = mliW.dwSource;
3830 lpmliA->dwLineID = mliW.dwLineID;
3831 lpmliA->fdwLine = mliW.fdwLine;
3832 lpmliA->dwUser = mliW.dwUser;
3833 lpmliA->dwComponentType = mliW.dwComponentType;
3834 lpmliA->cChannels = mliW.cChannels;
3835 lpmliA->cConnections = mliW.cConnections;
3836 lpmliA->cControls = mliW.cControls;
3837 WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
3838 sizeof(lpmliA->szShortName), NULL, NULL);
3839 WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
3840 sizeof(lpmliA->szName), NULL, NULL );
3841 lpmliA->Target.dwType = mliW.Target.dwType;
3842 lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
3843 lpmliA->Target.wMid = mliW.Target.wMid;
3844 lpmliA->Target.wPid = mliW.Target.wPid;
3845 lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
3846 WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
3847 sizeof(lpmliA->Target.szPname), NULL, NULL );
3849 return ret;
3852 /**************************************************************************
3853 * mixerSetControlDetails [WINMM.@]
3855 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
3856 DWORD fdwDetails)
3858 WINMM_ControlDetails details;
3860 TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
3862 if(!WINMM_StartDevicesThread())
3863 return MMSYSERR_ERROR;
3865 if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
3866 MIXER_SETCONTROLDETAILSF_CUSTOM)
3867 return MMSYSERR_NOTSUPPORTED;
3869 if(!lpmcd)
3870 return MMSYSERR_INVALPARAM;
3872 TRACE("dwControlID: %u\n", lpmcd->dwControlID);
3874 details.hmix = hmix;
3875 details.details = lpmcd;
3876 details.flags = fdwDetails;
3878 return SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
3879 (DWORD_PTR)&details, 0);
3882 /**************************************************************************
3883 * mixerMessage [WINMM.@]
3885 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3887 TRACE("(%p, %d, %lx, %lx)\n", hmix, uMsg, dwParam1, dwParam2);
3889 return MMSYSERR_NOTSUPPORTED;