quartz: Free two assert calls from having side effects.
[wine/testsucceed.git] / dlls / winmm / waveform.c
blob658fd24f2e2702090bf704296444c81cd6666d89
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);
1500 queue = device->first;
1501 while(queue){
1502 UINT32 to_copy_bytes;
1504 to_copy_bytes = min(queue->dwBufferLength - queue->dwBytesRecorded,
1505 device->acm_hdr.cbDstLengthUsed - device->acm_offs);
1507 memcpy(queue->lpData + queue->dwBytesRecorded,
1508 device->acm_hdr.pbDst + device->acm_offs, to_copy_bytes);
1510 queue->dwBytesRecorded += to_copy_bytes;
1511 device->acm_offs += to_copy_bytes;
1513 if(queue->dwBufferLength - queue->dwBytesRecorded <
1514 device->bytes_per_frame){
1515 queue->dwFlags &= ~WHDR_INQUEUE;
1516 queue->dwFlags |= WHDR_DONE;
1517 device->first = queue = queue->lpNext;
1520 if(device->acm_offs >= device->acm_hdr.cbDstLengthUsed){
1521 acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1522 HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1523 device->acm_hdr.cbDstLength = 0;
1524 device->acm_hdr.cbDstLengthUsed = 0;
1526 /* done with this ACM Header, so try to pull more data */
1527 WID_PullACMData(device);
1528 return;
1532 /* out of WAVEHDRs to write into, so toss the rest of this packet */
1533 acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1534 HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1535 device->acm_hdr.cbDstLength = 0;
1536 device->acm_hdr.cbDstLengthUsed = 0;
1539 static void WID_PullData(WINMM_Device *device)
1541 WINMM_CBInfo cb_info;
1542 WAVEHDR *queue, *first = NULL, *last = NULL;
1543 HRESULT hr;
1545 TRACE("(%p)\n", device->handle);
1547 EnterCriticalSection(&device->lock);
1549 if(!device->device || !device->first)
1550 goto exit;
1552 first = device->first;
1554 if(device->acm_handle){
1555 WID_PullACMData(device);
1556 goto exit;
1559 while(device->first){
1560 BYTE *data;
1561 UINT32 pad, packet_len, packet;
1562 DWORD flags;
1564 hr = IAudioClient_GetCurrentPadding(device->client, &pad);
1565 if(FAILED(hr)){
1566 ERR("GetCurrentPadding failed: %08x\n", hr);
1567 goto exit;
1570 if(pad == 0)
1571 goto exit;
1573 hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
1574 &flags, NULL, NULL);
1575 if(FAILED(hr)){
1576 ERR("GetBuffer failed: %08x\n", hr);
1577 goto exit;
1580 packet_len = packet;
1581 queue = device->first;
1582 while(queue && packet > 0){
1583 UINT32 to_copy_bytes;
1585 to_copy_bytes = min(packet * device->bytes_per_frame,
1586 queue->dwBufferLength - queue->dwBytesRecorded);
1588 memcpy(queue->lpData + queue->dwBytesRecorded, data,
1589 to_copy_bytes);
1591 queue->dwBytesRecorded += to_copy_bytes;
1593 if(queue->dwBufferLength - queue->dwBytesRecorded <
1594 device->bytes_per_frame){
1595 last = queue;
1596 device->first = queue = queue->lpNext;
1599 packet -= to_copy_bytes / device->bytes_per_frame;
1602 hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet_len);
1603 if(FAILED(hr))
1604 ERR("ReleaseBuffer failed: %08x\n", hr);
1607 exit:
1608 cb_info = device->cb_info;
1610 LeaveCriticalSection(&device->lock);
1612 if(last){
1613 last->lpNext = NULL;
1614 while(first){
1615 WAVEHDR *next = first->lpNext;
1616 first->dwFlags &= ~WHDR_INQUEUE;
1617 first->dwFlags |= WHDR_DONE;
1618 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1619 first = next;
1624 static HRESULT WINMM_BeginPlaying(WINMM_Device *device)
1626 HRESULT hr;
1628 TRACE("(%p)\n", device->handle);
1630 if(device->render)
1631 /* prebuffer data before starting */
1632 WOD_PushData(device);
1634 if(device->stopped){
1635 device->stopped = FALSE;
1637 hr = IAudioClient_Start(device->client);
1638 if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){
1639 device->stopped = TRUE;
1640 ERR("Start failed: %08x\n", hr);
1641 return hr;
1645 return S_OK;
1648 static LRESULT WINMM_Pause(HWAVE hwave)
1650 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1651 HRESULT hr;
1653 TRACE("(%p)\n", hwave);
1655 if(!WINMM_ValidateAndLock(device))
1656 return MMSYSERR_INVALHANDLE;
1658 hr = IAudioClient_Stop(device->client);
1659 if(FAILED(hr)){
1660 LeaveCriticalSection(&device->lock);
1661 ERR("Stop failed: %08x\n", hr);
1662 return MMSYSERR_ERROR;
1665 device->stopped = FALSE;
1667 LeaveCriticalSection(&device->lock);
1669 return MMSYSERR_NOERROR;
1672 static LRESULT WINMM_Reset(HWAVE hwave)
1674 WINMM_CBInfo cb_info;
1675 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1676 BOOL is_out;
1677 WAVEHDR *first;
1678 HRESULT hr;
1680 TRACE("(%p)\n", hwave);
1682 if(!WINMM_ValidateAndLock(device))
1683 return MMSYSERR_INVALHANDLE;
1685 hr = IAudioClient_Stop(device->client);
1686 if(FAILED(hr)){
1687 LeaveCriticalSection(&device->lock);
1688 ERR("Stop failed: %08x\n", hr);
1689 return MMSYSERR_ERROR;
1691 device->stopped = TRUE;
1693 first = device->first;
1694 device->first = device->last = device->playing = NULL;
1695 device->ofs_bytes = 0;
1696 device->played_frames = 0;
1697 device->loop_counter = 0;
1698 device->last_clock_pos = 0;
1699 IAudioClient_Reset(device->client);
1701 cb_info = device->cb_info;
1702 is_out = device->render ? TRUE : FALSE;
1704 LeaveCriticalSection(&device->lock);
1706 while(first){
1707 WAVEHDR *next = first->lpNext;
1708 first->dwFlags &= ~WHDR_INQUEUE;
1709 first->dwFlags |= WHDR_DONE;
1710 if(is_out)
1711 WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1712 else
1713 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1714 first = next;
1717 return MMSYSERR_NOERROR;
1720 static MMRESULT WINMM_FramesToMMTime(MMTIME *time, UINT32 played_frames,
1721 UINT32 sample_rate, UINT32 bytes_per_frame)
1723 switch(time->wType){
1724 case TIME_SAMPLES:
1725 time->u.sample = played_frames;
1726 return MMSYSERR_NOERROR;
1727 case TIME_MS:
1728 time->u.ms = (DWORD)((played_frames / (double)sample_rate) * 1000);
1729 return MMSYSERR_NOERROR;
1730 case TIME_SMPTE:
1731 time->u.smpte.fps = 30;
1732 if(played_frames >= sample_rate){
1733 time->u.smpte.sec = played_frames / (double)sample_rate;
1734 time->u.smpte.min = time->u.smpte.sec / 60;
1735 time->u.smpte.hour = time->u.smpte.min / 60;
1736 time->u.smpte.sec %= 60;
1737 time->u.smpte.min %= 60;
1738 played_frames %= sample_rate;
1739 }else{
1740 time->u.smpte.sec = 0;
1741 time->u.smpte.min = 0;
1742 time->u.smpte.hour = 0;
1744 time->u.smpte.frame = (played_frames / (double)sample_rate) * 30;
1745 return MMSYSERR_NOERROR;
1746 case TIME_BYTES:
1747 default:
1748 time->wType = TIME_BYTES;
1749 time->u.cb = played_frames * bytes_per_frame;
1750 return MMSYSERR_NOERROR;
1753 return MMSYSERR_ERROR;
1756 static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time)
1758 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1759 UINT32 played_frames, sample_rate, bytes_per_frame;
1761 TRACE("(%p, %p)\n", hwave, time);
1763 if(!WINMM_ValidateAndLock(device))
1764 return MMSYSERR_INVALHANDLE;
1766 played_frames = device->played_frames;
1767 sample_rate = device->samples_per_sec;
1768 bytes_per_frame = device->bytes_per_frame;
1770 LeaveCriticalSection(&device->lock);
1772 return WINMM_FramesToMMTime(time, played_frames, sample_rate,
1773 bytes_per_frame);
1776 static WINMM_MMDevice *WINMM_GetMixerMMDevice(HMIXEROBJ hmix, DWORD flags,
1777 UINT *mmdev_index)
1779 UINT mmdev, dev, junk, *out;
1780 BOOL is_out;
1782 if(!mmdev_index)
1783 out = &mmdev;
1784 else
1785 out = mmdev_index;
1787 switch(flags & 0xF0000000){
1788 case MIXER_OBJECTF_MIXER: /* == 0 */
1789 *out = HandleToULong(hmix);
1790 if(*out < g_outmmdevices_count)
1791 return &g_out_mmdevices[*out];
1792 if(*out - g_outmmdevices_count < g_inmmdevices_count){
1793 *out -= g_outmmdevices_count;
1794 return &g_in_mmdevices[*out];
1796 /* fall through -- if it's not a valid mixer device, then
1797 * it could be a valid mixer handle. windows seems to do
1798 * this as well. */
1799 case MIXER_OBJECTF_HMIXER:
1800 case MIXER_OBJECTF_HWAVEOUT:
1801 case MIXER_OBJECTF_HWAVEIN:
1802 WINMM_DecomposeHWAVE((HWAVE)hmix, out, &is_out, &dev, &junk);
1803 if(junk != 0x1 || (is_out && *out >= g_outmmdevices_count) ||
1804 (!is_out && *out >= g_inmmdevices_count))
1805 return NULL;
1806 if(is_out)
1807 return &g_out_mmdevices[*out];
1808 return &g_in_mmdevices[*out];
1809 case MIXER_OBJECTF_WAVEOUT:
1810 *out = HandleToULong(hmix);
1811 if(*out < g_outmmdevices_count)
1812 return &g_out_mmdevices[*out];
1813 return NULL;
1814 case MIXER_OBJECTF_WAVEIN:
1815 *out = HandleToULong(hmix);
1816 if(*out < g_inmmdevices_count)
1817 return &g_in_mmdevices[*out];
1818 return NULL;
1821 return NULL;
1824 static MMRESULT WINMM_SetupMMDeviceVolume(WINMM_MMDevice *mmdevice)
1826 IAudioSessionManager *sesman;
1827 IMMDevice *device;
1828 HRESULT hr;
1830 hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id, &device);
1831 if(FAILED(hr)){
1832 ERR("Device %s (%s) unavailable: %08x\n",
1833 wine_dbgstr_w(mmdevice->dev_id),
1834 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
1835 return MMSYSERR_ERROR;
1838 hr = IMMDevice_Activate(device, &IID_IAudioSessionManager,
1839 CLSCTX_INPROC_SERVER, NULL, (void**)&sesman);
1840 if(FAILED(hr)){
1841 ERR("Activate failed: %08x\n", hr);
1842 IMMDevice_Release(device);
1843 return MMSYSERR_ERROR;
1846 IMMDevice_Release(device);
1848 hr = IAudioSessionManager_GetSimpleAudioVolume(sesman, &mmdevice->session,
1849 FALSE, &mmdevice->volume);
1850 IAudioSessionManager_Release(sesman);
1851 if(FAILED(hr)){
1852 ERR("GetSimpleAudioVolume failed: %08x\n", hr);
1853 return MMSYSERR_ERROR;
1856 return MMSYSERR_NOERROR;
1859 static LRESULT MXD_GetControlDetails(WINMM_ControlDetails *details)
1861 WINMM_MMDevice *mmdevice;
1862 MIXERCONTROLDETAILS *control = details->details;
1863 HRESULT hr;
1865 TRACE("(%p)\n", details->hmix);
1867 mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
1868 if(!mmdevice)
1869 return MMSYSERR_INVALHANDLE;
1871 EnterCriticalSection(&mmdevice->lock);
1873 if(!mmdevice->volume){
1874 MMRESULT mr;
1876 mr = WINMM_SetupMMDeviceVolume(mmdevice);
1877 if(mr != MMSYSERR_NOERROR){
1878 LeaveCriticalSection(&mmdevice->lock);
1879 return mr;
1883 if(control->dwControlID == 0){
1884 float vol;
1885 MIXERCONTROLDETAILS_UNSIGNED *udet;
1887 if(!control->paDetails ||
1888 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
1889 LeaveCriticalSection(&mmdevice->lock);
1890 return MMSYSERR_INVALPARAM;
1893 hr = ISimpleAudioVolume_GetMasterVolume(mmdevice->volume, &vol);
1894 if(FAILED(hr)){
1895 ERR("GetMasterVolume failed: %08x\n", hr);
1896 LeaveCriticalSection(&mmdevice->lock);
1897 return MMSYSERR_ERROR;
1900 udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
1901 udet->dwValue = vol * ((unsigned int)0xFFFF);
1902 }else if(control->dwControlID == 1){
1903 BOOL mute;
1904 MIXERCONTROLDETAILS_BOOLEAN *bdet;
1906 if(!control->paDetails ||
1907 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
1908 LeaveCriticalSection(&mmdevice->lock);
1909 return MMSYSERR_INVALPARAM;
1912 hr = ISimpleAudioVolume_GetMute(mmdevice->volume, &mute);
1913 if(FAILED(hr)){
1914 ERR("GetMute failed: %08x\n", hr);
1915 LeaveCriticalSection(&mmdevice->lock);
1916 return MMSYSERR_ERROR;
1919 bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
1920 bdet->fValue = mute;
1921 }else if(control->dwControlID == 2 || control->dwControlID == 3){
1922 FIXME("What should the sw-side mixer controls map to?\n");
1923 }else{
1924 LeaveCriticalSection(&mmdevice->lock);
1925 return MIXERR_INVALCONTROL;
1928 LeaveCriticalSection(&mmdevice->lock);
1930 return MMSYSERR_NOERROR;
1933 static LRESULT MXD_SetControlDetails(WINMM_ControlDetails *details)
1935 WINMM_MMDevice *mmdevice;
1936 MIXERCONTROLDETAILS *control = details->details;
1937 HRESULT hr;
1939 TRACE("(%p)\n", details->hmix);
1941 mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
1942 if(!mmdevice)
1943 return MMSYSERR_INVALHANDLE;
1945 EnterCriticalSection(&mmdevice->lock);
1947 if(!mmdevice->volume){
1948 MMRESULT mr;
1950 mr = WINMM_SetupMMDeviceVolume(mmdevice);
1951 if(mr != MMSYSERR_NOERROR){
1952 LeaveCriticalSection(&mmdevice->lock);
1953 return mr;
1957 if(control->dwControlID == 0){
1958 float vol;
1959 MIXERCONTROLDETAILS_UNSIGNED *udet;
1961 if(!control->paDetails ||
1962 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
1963 LeaveCriticalSection(&mmdevice->lock);
1964 return MMSYSERR_INVALPARAM;
1967 udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
1969 if(udet->dwValue > 65535){
1970 LeaveCriticalSection(&mmdevice->lock);
1971 return MMSYSERR_INVALPARAM;
1974 vol = udet->dwValue / 65535.f;
1976 hr = ISimpleAudioVolume_SetMasterVolume(mmdevice->volume, vol, NULL);
1977 if(FAILED(hr)){
1978 ERR("SetMasterVolume failed: %08x\n", hr);
1979 LeaveCriticalSection(&mmdevice->lock);
1980 return MMSYSERR_ERROR;
1982 }else if(control->dwControlID == 1){
1983 BOOL mute;
1984 MIXERCONTROLDETAILS_BOOLEAN *bdet;
1986 if(!control->paDetails ||
1987 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
1988 LeaveCriticalSection(&mmdevice->lock);
1989 return MMSYSERR_INVALPARAM;
1992 bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
1993 mute = bdet->fValue;
1995 hr = ISimpleAudioVolume_SetMute(mmdevice->volume, mute, NULL);
1996 if(FAILED(hr)){
1997 ERR("SetMute failed: %08x\n", hr);
1998 LeaveCriticalSection(&mmdevice->lock);
1999 return MMSYSERR_ERROR;
2001 }else if(control->dwControlID == 2 || control->dwControlID == 3){
2002 FIXME("What should the sw-side mixer controls map to?\n");
2003 }else{
2004 LeaveCriticalSection(&mmdevice->lock);
2005 return MIXERR_INVALCONTROL;
2008 LeaveCriticalSection(&mmdevice->lock);
2010 return MMSYSERR_NOERROR;
2013 static LRESULT CALLBACK WINMM_DevicesMsgProc(HWND hwnd, UINT msg, WPARAM wparam,
2014 LPARAM lparam)
2016 switch(msg){
2017 case WODM_OPEN:
2018 return WOD_Open((WINMM_OpenInfo*)wparam);
2019 case WODM_CLOSE:
2020 return WOD_Close((HWAVEOUT)wparam);
2021 case WIDM_OPEN:
2022 return WID_Open((WINMM_OpenInfo*)wparam);
2023 case WIDM_CLOSE:
2024 return WID_Close((HWAVEIN)wparam);
2025 case MXDM_GETCONTROLDETAILS:
2026 return MXD_GetControlDetails((WINMM_ControlDetails*)wparam);
2027 case MXDM_SETCONTROLDETAILS:
2028 return MXD_SetControlDetails((WINMM_ControlDetails*)wparam);
2030 return DefWindowProcW(hwnd, msg, wparam, lparam);
2033 static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
2035 HANDLE evt = arg;
2036 HRESULT hr;
2037 static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0};
2039 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
2040 if(FAILED(hr)){
2041 ERR("CoInitializeEx failed: %08x\n", hr);
2042 return 1;
2045 hr = WINMM_InitMMDevices();
2046 if(FAILED(hr)){
2047 CoUninitialize();
2048 return 1;
2051 g_devices_hwnd = CreateWindowW(messageW, NULL, 0, 0, 0, 0, 0,
2052 HWND_MESSAGE, NULL, NULL, NULL);
2053 if(!g_devices_hwnd){
2054 ERR("CreateWindow failed: %d\n", GetLastError());
2055 CoUninitialize();
2056 return 1;
2059 SetWindowLongPtrW(g_devices_hwnd, GWLP_WNDPROC,
2060 (LONG_PTR)WINMM_DevicesMsgProc);
2062 /* inform caller that the thread is ready to process messages */
2063 SetEvent(evt);
2064 evt = NULL; /* do not use after this point */
2066 while(1){
2067 DWORD wait;
2068 wait = MsgWaitForMultipleObjects(g_devhandle_count, g_device_handles,
2069 FALSE, INFINITE, QS_ALLINPUT);
2070 if(wait == g_devhandle_count + WAIT_OBJECT_0){
2071 MSG msg;
2072 if(PeekMessageW(&msg, g_devices_hwnd, 0, 0, PM_REMOVE))
2073 ERR("Unexpected message: 0x%x\n", msg.message);
2074 }else if(wait < g_devhandle_count + WAIT_OBJECT_0){
2075 WINMM_Device *device = g_handle_devices[wait - WAIT_OBJECT_0];
2076 if(device->render)
2077 WOD_PushData(device);
2078 else
2079 WID_PullData(device);
2080 }else
2081 ERR("Unexpected MsgWait result 0x%x, GLE: %d\n", wait,
2082 GetLastError());
2085 DestroyWindow(g_devices_hwnd);
2087 CoUninitialize();
2089 return 0;
2092 static BOOL WINMM_StartDevicesThread(void)
2094 HANDLE events[2];
2095 DWORD wait;
2097 EnterCriticalSection(&g_devthread_lock);
2099 if(g_devices_thread){
2100 DWORD wait;
2102 wait = WaitForSingleObject(g_devices_thread, 0);
2103 if(wait == WAIT_TIMEOUT){
2104 LeaveCriticalSection(&g_devthread_lock);
2105 return TRUE;
2107 if(wait != WAIT_OBJECT_0){
2108 LeaveCriticalSection(&g_devthread_lock);
2109 return FALSE;
2112 g_devices_thread = NULL;
2113 g_devices_hwnd = NULL;
2116 TRACE("Starting up devices thread\n");
2118 events[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
2120 g_devices_thread = CreateThread(NULL, 0, WINMM_DevicesThreadProc,
2121 events[0], 0, NULL);
2122 if(!g_devices_thread){
2123 LeaveCriticalSection(&g_devthread_lock);
2124 CloseHandle(events[0]);
2125 return FALSE;
2128 events[1] = g_devices_thread;
2129 wait = WaitForMultipleObjects(2, events, FALSE, INFINITE);
2130 CloseHandle(events[0]);
2131 if(wait != WAIT_OBJECT_0){
2132 if(wait == 1 + WAIT_OBJECT_0){
2133 CloseHandle(g_devices_thread);
2134 g_devices_thread = NULL;
2135 g_devices_hwnd = NULL;
2137 LeaveCriticalSection(&g_devthread_lock);
2138 return FALSE;
2141 LeaveCriticalSection(&g_devthread_lock);
2143 return TRUE;
2146 /**************************************************************************
2147 * waveOutGetNumDevs [WINMM.@]
2149 UINT WINAPI waveOutGetNumDevs(void)
2151 if(!WINMM_StartDevicesThread())
2152 return 0;
2154 TRACE("count: %u\n", g_outmmdevices_count);
2156 return g_outmmdevices_count;
2159 /**************************************************************************
2160 * waveOutGetDevCapsA [WINMM.@]
2162 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
2163 UINT uSize)
2165 WAVEOUTCAPSW wocW;
2166 UINT ret;
2168 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2170 if(!WINMM_StartDevicesThread())
2171 return MMSYSERR_ERROR;
2173 if(!lpCaps)
2174 return MMSYSERR_INVALPARAM;
2176 ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
2178 if (ret == MMSYSERR_NOERROR) {
2179 WAVEOUTCAPSA wocA;
2180 wocA.wMid = wocW.wMid;
2181 wocA.wPid = wocW.wPid;
2182 wocA.vDriverVersion = wocW.vDriverVersion;
2183 WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,
2184 sizeof(wocA.szPname), NULL, NULL );
2185 wocA.dwFormats = wocW.dwFormats;
2186 wocA.wChannels = wocW.wChannels;
2187 wocA.dwSupport = wocW.dwSupport;
2188 memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));
2190 return ret;
2193 /**************************************************************************
2194 * waveOutGetDevCapsW [WINMM.@]
2196 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
2197 UINT uSize)
2199 WAVEOUTCAPSW mapper_caps, *caps;
2201 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2203 if(!WINMM_StartDevicesThread())
2204 return MMSYSERR_ERROR;
2206 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2208 if(WINMM_IsMapper(uDeviceID)){
2209 /* FIXME: Should be localized */
2210 static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2211 'n','d',' ','M','a','p','p','e','r',0};
2213 mapper_caps.wMid = 0xFF;
2214 mapper_caps.wPid = 0xFF;
2215 mapper_caps.vDriverVersion = 0x00010001;
2216 mapper_caps.dwFormats = 0xFFFFFFFF;
2217 mapper_caps.wReserved1 = 0;
2218 mapper_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
2219 WAVECAPS_SAMPLEACCURATE;
2220 mapper_caps.wChannels = 2;
2221 lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2223 caps = &mapper_caps;
2224 }else{
2225 if(uDeviceID >= g_outmmdevices_count)
2226 return MMSYSERR_BADDEVICEID;
2228 caps = &g_out_mmdevices[uDeviceID].out_caps;
2231 memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2233 return MMSYSERR_NOERROR;
2236 /**************************************************************************
2237 * waveOutGetErrorTextA [WINMM.@]
2238 * waveInGetErrorTextA [WINMM.@]
2240 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2242 UINT ret;
2244 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2245 else if (uSize == 0) ret = MMSYSERR_NOERROR;
2246 else
2248 LPWSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
2249 if (!xstr) ret = MMSYSERR_NOMEM;
2250 else
2252 ret = waveOutGetErrorTextW(uError, xstr, uSize);
2253 if (ret == MMSYSERR_NOERROR)
2254 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
2255 HeapFree(GetProcessHeap(), 0, xstr);
2258 return ret;
2261 /**************************************************************************
2262 * waveOutGetErrorTextW [WINMM.@]
2263 * waveInGetErrorTextW [WINMM.@]
2265 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2267 UINT ret = MMSYSERR_BADERRNUM;
2269 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2270 else if (uSize == 0) ret = MMSYSERR_NOERROR;
2271 else if (
2272 /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
2273 * a warning for the test was always true */
2274 (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
2275 (uError >= WAVERR_BASE && uError <= WAVERR_LASTERROR)) {
2276 if (LoadStringW(hWinMM32Instance,
2277 uError, lpText, uSize) > 0) {
2278 ret = MMSYSERR_NOERROR;
2281 return ret;
2284 /**************************************************************************
2285 * waveOutOpen [WINMM.@]
2286 * All the args/structs have the same layout as the win16 equivalents
2288 MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
2289 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2290 DWORD_PTR dwInstance, DWORD dwFlags)
2292 LRESULT res;
2293 WINMM_OpenInfo info;
2294 WINMM_CBInfo cb_info;
2296 TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut, uDeviceID, lpFormat,
2297 dwCallback, dwInstance, dwFlags);
2299 if(!WINMM_StartDevicesThread())
2300 return MMSYSERR_ERROR;
2302 if(!lphWaveOut && !(dwFlags & WAVE_FORMAT_QUERY))
2303 return MMSYSERR_INVALPARAM;
2305 res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2306 if(res != MMSYSERR_NOERROR)
2307 return res;
2309 info.format = (WAVEFORMATEX*)lpFormat;
2310 info.callback = dwCallback;
2311 info.cb_user = dwInstance;
2312 info.req_device = uDeviceID;
2313 info.flags = dwFlags;
2315 res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0);
2316 if(res != MMSYSERR_NOERROR)
2317 return res;
2319 if(lphWaveOut)
2320 *lphWaveOut = (HWAVEOUT)info.handle;
2322 cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2323 cb_info.callback = dwCallback;
2324 cb_info.user = dwInstance;
2325 cb_info.hwave = info.handle;
2327 WINMM_NotifyClient(&cb_info, WOM_OPEN, 0, 0);
2329 return res;
2332 /**************************************************************************
2333 * waveOutClose [WINMM.@]
2335 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
2337 UINT res;
2338 WINMM_Device *device;
2339 WINMM_CBInfo cb_info;
2341 TRACE("(%p)\n", hWaveOut);
2343 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2345 if(!WINMM_ValidateAndLock(device))
2346 return MMSYSERR_INVALHANDLE;
2348 cb_info = device->cb_info;
2350 LeaveCriticalSection(&device->lock);
2352 res = SendMessageW(g_devices_hwnd, WODM_CLOSE, (WPARAM)hWaveOut, 0);
2354 if(res == MMSYSERR_NOERROR)
2355 WINMM_NotifyClient(&cb_info, WOM_CLOSE, 0, 0);
2357 return res;
2360 /**************************************************************************
2361 * waveOutPrepareHeader [WINMM.@]
2363 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
2364 WAVEHDR* lpWaveOutHdr, UINT uSize)
2366 TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2368 if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2369 return MMSYSERR_INVALPARAM;
2371 if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2372 return WAVERR_STILLPLAYING;
2374 return WINMM_PrepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2377 /**************************************************************************
2378 * waveOutUnprepareHeader [WINMM.@]
2380 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
2381 LPWAVEHDR lpWaveOutHdr, UINT uSize)
2383 TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2385 if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2386 return MMSYSERR_INVALPARAM;
2388 if(!(lpWaveOutHdr->dwFlags & WHDR_PREPARED))
2389 return MMSYSERR_NOERROR;
2391 if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2392 return WAVERR_STILLPLAYING;
2394 return WINMM_UnprepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2397 /**************************************************************************
2398 * waveOutWrite [WINMM.@]
2400 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, WAVEHDR *header, UINT uSize)
2402 WINMM_Device *device;
2403 HRESULT hr;
2405 TRACE("(%p, %p, %u)\n", hWaveOut, header, uSize);
2407 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2409 if(!WINMM_ValidateAndLock(device))
2410 return MMSYSERR_INVALHANDLE;
2412 if(!header->lpData || !(header->dwFlags & WHDR_PREPARED)){
2413 LeaveCriticalSection(&device->lock);
2414 return WAVERR_UNPREPARED;
2417 if(header->dwFlags & WHDR_INQUEUE){
2418 LeaveCriticalSection(&device->lock);
2419 return WAVERR_STILLPLAYING;
2422 if(device->acm_handle){
2423 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
2424 MMRESULT mr;
2426 ash->cbSrcLength = header->dwBufferLength;
2427 mr = acmStreamConvert(device->acm_handle, ash, 0);
2428 if(mr != MMSYSERR_NOERROR){
2429 LeaveCriticalSection(&device->lock);
2430 return mr;
2434 if(device->first){
2435 device->last->lpNext = header;
2436 device->last = header;
2437 if(!device->playing)
2438 device->playing = header;
2439 }else{
2440 device->playing = device->first = device->last = header;
2441 if(header->dwFlags & WHDR_BEGINLOOP){
2442 device->loop_counter = header->dwLoops;
2443 device->loop_start = header;
2447 header->lpNext = NULL;
2448 header->dwFlags &= ~WHDR_DONE;
2449 header->dwFlags |= WHDR_INQUEUE;
2451 hr = WINMM_BeginPlaying(device);
2452 if(FAILED(hr)){
2453 LeaveCriticalSection(&device->lock);
2454 return MMSYSERR_ERROR;
2457 LeaveCriticalSection(&device->lock);
2459 return MMSYSERR_NOERROR;
2462 /**************************************************************************
2463 * waveOutBreakLoop [WINMM.@]
2465 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
2467 WINMM_Device *device;
2469 TRACE("(%p)\n", hWaveOut);
2471 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2473 if(!WINMM_ValidateAndLock(device))
2474 return MMSYSERR_INVALHANDLE;
2476 device->loop_counter = 0;
2478 LeaveCriticalSection(&device->lock);
2480 return MMSYSERR_NOERROR;
2483 /**************************************************************************
2484 * waveOutPause [WINMM.@]
2486 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
2488 TRACE("(%p)\n", hWaveOut);
2490 return WINMM_Pause((HWAVE)hWaveOut);
2493 /**************************************************************************
2494 * waveOutReset [WINMM.@]
2496 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2498 TRACE("(%p)\n", hWaveOut);
2500 return WINMM_Reset((HWAVE)hWaveOut);
2503 /**************************************************************************
2504 * waveOutRestart [WINMM.@]
2506 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2508 WINMM_Device *device;
2509 HRESULT hr;
2511 TRACE("(%p)\n", hWaveOut);
2513 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2515 if(!WINMM_ValidateAndLock(device))
2516 return MMSYSERR_INVALHANDLE;
2518 device->stopped = TRUE;
2520 hr = WINMM_BeginPlaying(device);
2521 if(FAILED(hr)){
2522 LeaveCriticalSection(&device->lock);
2523 return MMSYSERR_ERROR;
2526 LeaveCriticalSection(&device->lock);
2528 return MMSYSERR_NOERROR;
2531 /**************************************************************************
2532 * waveOutGetPosition [WINMM.@]
2534 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2535 UINT uSize)
2537 TRACE("(%p, %p, %u)\n", hWaveOut, lpTime, uSize);
2539 if(!uSize || !lpTime || uSize != sizeof(MMTIME))
2540 return MMSYSERR_INVALPARAM;
2542 return WINMM_GetPosition((HWAVE)hWaveOut, lpTime);
2545 /**************************************************************************
2546 * waveOutGetPitch [WINMM.@]
2548 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2550 TRACE("(%p, %p)\n", hWaveOut, lpdw);
2551 return MMSYSERR_NOTSUPPORTED;
2554 /**************************************************************************
2555 * waveOutSetPitch [WINMM.@]
2557 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
2559 TRACE("(%p, %08x)\n", hWaveOut, dw);
2561 return MMSYSERR_NOTSUPPORTED;
2564 /**************************************************************************
2565 * waveOutGetPlaybackRate [WINMM.@]
2567 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
2569 TRACE("(%p, %p)\n", hWaveOut, lpdw);
2571 return MMSYSERR_NOTSUPPORTED;
2574 /**************************************************************************
2575 * waveOutSetPlaybackRate [WINMM.@]
2577 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
2579 TRACE("(%p, %08x)\n", hWaveOut, dw);
2581 return MMSYSERR_NOTSUPPORTED;
2584 /**************************************************************************
2585 * waveOutGetVolume [WINMM.@]
2587 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, DWORD *out)
2589 WINMM_Device *device;
2590 UINT32 channels;
2591 float *vols;
2592 HRESULT hr;
2594 TRACE("(%p, %p)\n", hWaveOut, out);
2596 if(!out)
2597 return MMSYSERR_INVALPARAM;
2599 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2601 if(!WINMM_ValidateAndLock(device))
2602 return MMSYSERR_INVALHANDLE;
2604 hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2605 if(FAILED(hr)){
2606 LeaveCriticalSection(&device->lock);
2607 ERR("GetChannelCount failed: %08x\n", hr);
2608 return MMSYSERR_ERROR;
2611 vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2612 if(!vols){
2613 LeaveCriticalSection(&device->lock);
2614 return MMSYSERR_NOMEM;
2617 hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2618 if(FAILED(hr)){
2619 LeaveCriticalSection(&device->lock);
2620 HeapFree(GetProcessHeap(), 0, vols);
2621 ERR("GetAllVolumes failed: %08x\n", hr);
2622 return MMSYSERR_ERROR;
2625 LeaveCriticalSection(&device->lock);
2627 *out = ((UINT16)(vols[0] * (DWORD)0xFFFF));
2628 if(channels > 1)
2629 *out |= ((UINT16)(vols[1] * (DWORD)0xFFFF)) << 16;
2631 HeapFree(GetProcessHeap(), 0, vols);
2633 return MMSYSERR_NOERROR;
2636 /**************************************************************************
2637 * waveOutSetVolume [WINMM.@]
2639 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD in)
2641 WINMM_Device *device;
2642 UINT32 channels;
2643 float *vols;
2644 HRESULT hr;
2646 TRACE("(%p, %08x)\n", hWaveOut, in);
2648 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2650 if(!WINMM_ValidateAndLock(device))
2651 return MMSYSERR_INVALHANDLE;
2653 hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2654 if(FAILED(hr)){
2655 LeaveCriticalSection(&device->lock);
2656 ERR("GetChannelCount failed: %08x\n", hr);
2657 return MMSYSERR_ERROR;
2660 vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2661 if(!vols){
2662 LeaveCriticalSection(&device->lock);
2663 return MMSYSERR_NOMEM;
2666 hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2667 if(FAILED(hr)){
2668 LeaveCriticalSection(&device->lock);
2669 HeapFree(GetProcessHeap(), 0, vols);
2670 ERR("GetAllVolumes failed: %08x\n", hr);
2671 return MMSYSERR_ERROR;
2674 vols[0] = (float)((DWORD)(in & 0xFFFF) / (float)0xFFFF);
2675 if(channels > 1)
2676 vols[1] = (float)((DWORD)(in >> 16) / (float)0xFFFF);
2678 hr = IAudioStreamVolume_SetAllVolumes(device->volume, channels, vols);
2679 if(FAILED(hr)){
2680 LeaveCriticalSection(&device->lock);
2681 HeapFree(GetProcessHeap(), 0, vols);
2682 ERR("SetAllVolumes failed: %08x\n", hr);
2683 return MMSYSERR_ERROR;
2686 LeaveCriticalSection(&device->lock);
2688 HeapFree(GetProcessHeap(), 0, vols);
2690 return MMSYSERR_NOERROR;
2693 /**************************************************************************
2694 * waveOutGetID [WINMM.@]
2696 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
2698 WINMM_Device *device;
2699 UINT dev, junk;
2700 BOOL is_out;
2702 TRACE("(%p, %p)\n", hWaveOut, lpuDeviceID);
2704 if(!lpuDeviceID)
2705 return MMSYSERR_INVALPARAM;
2707 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2708 if(!WINMM_ValidateAndLock(device))
2709 return MMSYSERR_INVALHANDLE;
2711 LeaveCriticalSection(&device->lock);
2713 WINMM_DecomposeHWAVE((HWAVE)hWaveOut, lpuDeviceID, &is_out, &dev, &junk);
2715 return MMSYSERR_NOERROR;
2718 static UINT WINMM_QueryInstanceIDSize(UINT device, DWORD_PTR *len, BOOL is_out)
2720 UINT count;
2721 WINMM_MMDevice *devices;
2723 TRACE("(%u, %p, %d)\n", device, len, is_out);
2725 if(is_out){
2726 count = g_outmmdevices_count;
2727 devices = g_out_mmdevices;
2728 }else{
2729 count = g_inmmdevices_count;
2730 devices = g_in_mmdevices;
2733 if(device >= count)
2734 return MMSYSERR_INVALHANDLE;
2736 *len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2738 return MMSYSERR_NOERROR;
2741 static UINT WINMM_QueryInstanceID(UINT device, WCHAR *str, DWORD_PTR len,
2742 BOOL is_out)
2744 UINT count, id_len;
2745 WINMM_MMDevice *devices;
2747 TRACE("(%u, %p, %d)\n", device, str, is_out);
2749 if(is_out){
2750 count = g_outmmdevices_count;
2751 devices = g_out_mmdevices;
2752 }else{
2753 count = g_inmmdevices_count;
2754 devices = g_in_mmdevices;
2757 if(device >= count)
2758 return MMSYSERR_INVALHANDLE;
2760 id_len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2761 if(len < id_len)
2762 return MMSYSERR_ERROR;
2764 memcpy(str, devices[device].dev_id, id_len);
2766 return MMSYSERR_NOERROR;
2769 /**************************************************************************
2770 * waveOutMessage [WINMM.@]
2772 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2773 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2775 TRACE("(%p, %u, %lx, %lx)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2777 switch(uMessage){
2778 case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
2779 return WINMM_QueryInstanceIDSize(HandleToULong(hWaveOut),
2780 (DWORD_PTR*)dwParam1, TRUE);
2781 case DRV_QUERYFUNCTIONINSTANCEID:
2782 return WINMM_QueryInstanceID(HandleToULong(hWaveOut), (WCHAR*)dwParam1, dwParam2, TRUE);
2783 case DRV_QUERYMAPPABLE:
2784 return MMSYSERR_NOERROR;
2787 return MMSYSERR_NOTSUPPORTED;
2790 /**************************************************************************
2791 * waveInGetNumDevs [WINMM.@]
2793 UINT WINAPI waveInGetNumDevs(void)
2795 if(!WINMM_StartDevicesThread())
2796 return 0;
2798 TRACE("count: %u\n", g_inmmdevices_count);
2800 return g_inmmdevices_count;
2803 /**************************************************************************
2804 * waveInGetDevCapsW [WINMM.@]
2806 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2808 WAVEINCAPSW mapper_caps, *caps;
2810 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2812 if(!WINMM_StartDevicesThread())
2813 return MMSYSERR_ERROR;
2815 if(!lpCaps)
2816 return MMSYSERR_INVALPARAM;
2818 if(WINMM_IsMapper(uDeviceID)){
2819 /* FIXME: Should be localized */
2820 static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2821 'n','d',' ','M','a','p','p','e','r',0};
2823 mapper_caps.wMid = 0xFF;
2824 mapper_caps.wPid = 0xFF;
2825 mapper_caps.vDriverVersion = 0x00010001;
2826 mapper_caps.dwFormats = 0xFFFFFFFF;
2827 mapper_caps.wReserved1 = 0;
2828 mapper_caps.wChannels = 2;
2829 lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2831 caps = &mapper_caps;
2832 }else{
2833 if(uDeviceID >= g_inmmdevices_count)
2834 return MMSYSERR_BADDEVICEID;
2836 caps = &g_in_mmdevices[uDeviceID].in_caps;
2839 memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2841 return MMSYSERR_NOERROR;
2844 /**************************************************************************
2845 * waveInGetDevCapsA [WINMM.@]
2847 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2849 UINT ret;
2850 WAVEINCAPSW wicW;
2852 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2854 if(!WINMM_StartDevicesThread())
2855 return MMSYSERR_ERROR;
2857 if(!lpCaps)
2858 return MMSYSERR_INVALPARAM;
2860 ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2862 if (ret == MMSYSERR_NOERROR) {
2863 WAVEINCAPSA wicA;
2864 wicA.wMid = wicW.wMid;
2865 wicA.wPid = wicW.wPid;
2866 wicA.vDriverVersion = wicW.vDriverVersion;
2867 WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2868 sizeof(wicA.szPname), NULL, NULL );
2869 wicA.dwFormats = wicW.dwFormats;
2870 wicA.wChannels = wicW.wChannels;
2871 memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2873 return ret;
2876 /**************************************************************************
2877 * waveInOpen [WINMM.@]
2879 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2880 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2881 DWORD_PTR dwInstance, DWORD dwFlags)
2883 LRESULT res;
2884 WINMM_OpenInfo info;
2885 WINMM_CBInfo cb_info;
2887 TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
2888 dwCallback, dwInstance, dwFlags);
2890 if(!WINMM_StartDevicesThread())
2891 return MMSYSERR_ERROR;
2893 if(!lphWaveIn && !(dwFlags & WAVE_FORMAT_QUERY))
2894 return MMSYSERR_INVALPARAM;
2896 res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2897 if(res != MMSYSERR_NOERROR)
2898 return res;
2900 info.format = (WAVEFORMATEX*)lpFormat;
2901 info.callback = dwCallback;
2902 info.cb_user = dwInstance;
2903 info.req_device = uDeviceID;
2904 info.flags = dwFlags;
2906 res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
2907 if(res != MMSYSERR_NOERROR)
2908 return res;
2910 if(lphWaveIn)
2911 *lphWaveIn = (HWAVEIN)info.handle;
2913 cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2914 cb_info.callback = dwCallback;
2915 cb_info.user = dwInstance;
2916 cb_info.hwave = info.handle;
2918 WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
2920 return res;
2923 /**************************************************************************
2924 * waveInClose [WINMM.@]
2926 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2928 WINMM_Device *device;
2929 WINMM_CBInfo cb_info;
2930 UINT res;
2932 TRACE("(%p)\n", hWaveIn);
2934 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
2936 if(!WINMM_ValidateAndLock(device))
2937 return MMSYSERR_INVALHANDLE;
2939 cb_info = device->cb_info;
2941 LeaveCriticalSection(&device->lock);
2943 res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
2945 if(res == MMSYSERR_NOERROR)
2946 WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
2948 return res;
2951 /**************************************************************************
2952 * waveInPrepareHeader [WINMM.@]
2954 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2955 UINT uSize)
2957 TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2959 if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2960 return MMSYSERR_INVALPARAM;
2962 if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2963 return WAVERR_STILLPLAYING;
2965 return WINMM_PrepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
2968 /**************************************************************************
2969 * waveInUnprepareHeader [WINMM.@]
2971 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2972 UINT uSize)
2974 TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2976 if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2977 return MMSYSERR_INVALPARAM;
2979 if(!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
2980 return MMSYSERR_NOERROR;
2982 if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2983 return WAVERR_STILLPLAYING;
2985 return WINMM_UnprepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
2988 /**************************************************************************
2989 * waveInAddBuffer [WINMM.@]
2991 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn, WAVEHDR *header, UINT uSize)
2993 WINMM_Device *device;
2995 TRACE("(%p, %p, %u)\n", hWaveIn, header, uSize);
2997 if(!header || uSize < sizeof(WAVEHDR))
2998 return MMSYSERR_INVALPARAM;
3000 if(!(header->dwFlags & WHDR_PREPARED))
3001 return WAVERR_UNPREPARED;
3003 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3005 if(!WINMM_ValidateAndLock(device))
3006 return MMSYSERR_INVALHANDLE;
3008 if(!device->first)
3009 device->first = device->last = header;
3010 else{
3011 device->last->lpNext = header;
3012 device->last = header;
3015 header->dwBytesRecorded = 0;
3016 header->lpNext = NULL;
3017 header->dwFlags &= ~WHDR_DONE;
3018 header->dwFlags |= WHDR_INQUEUE;
3020 LeaveCriticalSection(&device->lock);
3022 return MMSYSERR_NOERROR;
3025 /**************************************************************************
3026 * waveInReset [WINMM.@]
3028 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
3030 TRACE("(%p)\n", hWaveIn);
3032 return WINMM_Reset((HWAVE)hWaveIn);
3035 /**************************************************************************
3036 * waveInStart [WINMM.@]
3038 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
3040 WINMM_Device *device;
3041 HRESULT hr;
3043 TRACE("(%p)\n", hWaveIn);
3045 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3047 if(!WINMM_ValidateAndLock(device))
3048 return MMSYSERR_INVALHANDLE;
3050 hr = WINMM_BeginPlaying(device);
3051 if(FAILED(hr)){
3052 LeaveCriticalSection(&device->lock);
3053 return MMSYSERR_ERROR;
3056 LeaveCriticalSection(&device->lock);
3058 return MMSYSERR_NOERROR;
3061 /**************************************************************************
3062 * waveInStop [WINMM.@]
3064 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
3066 WINMM_CBInfo cb_info;
3067 WINMM_Device *device;
3068 WAVEHDR *buf;
3069 HRESULT hr;
3071 TRACE("(%p)\n", hWaveIn);
3073 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3075 if(!WINMM_ValidateAndLock(device))
3076 return MMSYSERR_INVALHANDLE;
3078 hr = WINMM_Pause((HWAVE)hWaveIn);
3079 if(FAILED(hr)){
3080 LeaveCriticalSection(&device->lock);
3081 return MMSYSERR_ERROR;
3083 device->stopped = TRUE;
3085 buf = device->first;
3086 if(buf && buf->dwBytesRecorded > 0){
3087 device->first = buf->lpNext;
3088 }else
3089 buf = NULL;
3091 cb_info = device->cb_info;
3093 LeaveCriticalSection(&device->lock);
3095 if(buf){
3096 buf->dwFlags &= ~WHDR_INQUEUE;
3097 buf->dwFlags |= WHDR_DONE;
3098 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
3101 return MMSYSERR_NOERROR;
3104 /**************************************************************************
3105 * waveInGetPosition [WINMM.@]
3107 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
3108 UINT uSize)
3110 TRACE("(%p, %p, %u)\n", hWaveIn, lpTime, uSize);
3112 if(!uSize || !lpTime || uSize != sizeof(MMTIME))
3113 return MMSYSERR_INVALPARAM;
3115 return WINMM_GetPosition((HWAVE)hWaveIn, lpTime);
3118 /**************************************************************************
3119 * waveInGetID [WINMM.@]
3121 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
3123 UINT dev, junk;
3124 BOOL is_out;
3125 WINMM_Device *device;
3127 TRACE("(%p, %p)\n", hWaveIn, lpuDeviceID);
3129 if(!lpuDeviceID)
3130 return MMSYSERR_INVALPARAM;
3132 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3133 if(!WINMM_ValidateAndLock(device))
3134 return MMSYSERR_INVALHANDLE;
3136 LeaveCriticalSection(&device->lock);
3138 WINMM_DecomposeHWAVE((HWAVE)hWaveIn, lpuDeviceID, &is_out, &dev, &junk);
3140 return MMSYSERR_NOERROR;
3143 /**************************************************************************
3144 * waveInMessage [WINMM.@]
3146 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
3147 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3149 TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
3151 switch(uMessage){
3152 case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
3153 return WINMM_QueryInstanceIDSize(HandleToULong(hWaveIn),
3154 (DWORD_PTR*)dwParam1, FALSE);
3155 case DRV_QUERYFUNCTIONINSTANCEID:
3156 return WINMM_QueryInstanceID(HandleToULong(hWaveIn), (WCHAR*)dwParam1, dwParam2, FALSE);
3157 case DRV_QUERYMAPPABLE:
3158 return MMSYSERR_NOERROR;
3161 return MMSYSERR_NOTSUPPORTED;
3164 UINT WINAPI mixerGetNumDevs(void)
3166 TRACE("\n");
3168 if(!WINMM_StartDevicesThread())
3169 return 0;
3171 return g_outmmdevices_count + g_inmmdevices_count;
3174 /**************************************************************************
3175 * mixerGetDevCapsA [WINMM.@]
3177 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
3179 MIXERCAPSW micW;
3180 UINT ret;
3182 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3184 if(!lpCaps)
3185 return MMSYSERR_INVALPARAM;
3187 ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
3189 if (ret == MMSYSERR_NOERROR) {
3190 MIXERCAPSA micA;
3191 micA.wMid = micW.wMid;
3192 micA.wPid = micW.wPid;
3193 micA.vDriverVersion = micW.vDriverVersion;
3194 WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
3195 sizeof(micA.szPname), NULL, NULL );
3196 micA.fdwSupport = micW.fdwSupport;
3197 micA.cDestinations = micW.cDestinations;
3198 memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
3200 return ret;
3203 /**************************************************************************
3204 * mixerGetDevCapsW [WINMM.@]
3206 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
3208 WINMM_MMDevice *mmdevice;
3209 MIXERCAPSW caps;
3211 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3213 if(!WINMM_StartDevicesThread())
3214 return MMSYSERR_ERROR;
3216 if(!lpCaps)
3217 return MMSYSERR_INVALPARAM;
3219 if(!uSize)
3220 return MMSYSERR_NOERROR;
3222 if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3223 return MMSYSERR_BADDEVICEID;
3225 if(uDeviceID < g_outmmdevices_count){
3226 mmdevice = &g_out_mmdevices[uDeviceID];
3227 memcpy(caps.szPname, mmdevice->out_caps.szPname, sizeof(caps.szPname));
3228 }else{
3229 mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3230 memcpy(caps.szPname, mmdevice->in_caps.szPname, sizeof(caps.szPname));
3233 caps.wMid = 0xFF;
3234 caps.wPid = 0xFF;
3235 caps.vDriverVersion = 0x00010001;
3236 caps.fdwSupport = 0;
3237 caps.cDestinations = 1;
3239 memcpy(lpCaps, &caps, uSize);
3241 return MMSYSERR_NOERROR;
3244 /**************************************************************************
3245 * mixerOpen [WINMM.@]
3247 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
3248 DWORD_PTR dwInstance, DWORD fdwOpen)
3250 WINMM_MMDevice *mmdevice;
3251 MMRESULT mr;
3253 TRACE("(%p, %d, %lx, %lx, %x)\n", lphMix, uDeviceID, dwCallback,
3254 dwInstance, fdwOpen);
3256 if(!WINMM_StartDevicesThread())
3257 return MMSYSERR_ERROR;
3259 if(!lphMix)
3260 return MMSYSERR_INVALPARAM;
3262 mr = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
3263 if(mr != MMSYSERR_NOERROR)
3264 return mr;
3266 if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3267 return MMSYSERR_BADDEVICEID;
3269 if(uDeviceID < g_outmmdevices_count){
3270 mmdevice = &g_out_mmdevices[uDeviceID];
3271 *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID, TRUE,
3272 mmdevice->mixer_count);
3273 }else{
3274 mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3275 *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID - g_outmmdevices_count,
3276 FALSE, mmdevice->mixer_count);
3279 ++mmdevice->mixer_count;
3281 return MMSYSERR_NOERROR;
3284 /**************************************************************************
3285 * mixerClose [WINMM.@]
3287 UINT WINAPI mixerClose(HMIXER hMix)
3289 TRACE("(%p)\n", hMix);
3291 return MMSYSERR_NOERROR;
3294 /**************************************************************************
3295 * mixerGetID [WINMM.@]
3297 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
3299 WINMM_MMDevice *mmdevice;
3301 TRACE("(%p, %p, %x)\n", hmix, lpid, fdwID);
3303 if(!WINMM_StartDevicesThread())
3304 return MMSYSERR_ERROR;
3306 if(!lpid)
3307 return MMSYSERR_INVALPARAM;
3309 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwID, lpid);
3310 if(!mmdevice)
3311 return MMSYSERR_INVALHANDLE;
3313 if(mmdevice->in_caps.szPname[0] != '\0')
3314 *lpid += g_outmmdevices_count;
3316 return MMSYSERR_NOERROR;
3319 /**************************************************************************
3320 * mixerGetControlDetailsW [WINMM.@]
3322 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
3323 DWORD fdwDetails)
3325 WINMM_ControlDetails details;
3327 TRACE("(%p, %p, %x)\n", hmix, lpmcdW, fdwDetails);
3329 if(!WINMM_StartDevicesThread())
3330 return MMSYSERR_ERROR;
3332 if(!lpmcdW)
3333 return MMSYSERR_INVALPARAM;
3335 TRACE("dwControlID: %u\n", lpmcdW->dwControlID);
3337 details.hmix = hmix;
3338 details.details = lpmcdW;
3339 details.flags = fdwDetails;
3341 return SendMessageW(g_devices_hwnd, MXDM_GETCONTROLDETAILS,
3342 (DWORD_PTR)&details, 0);
3345 /**************************************************************************
3346 * mixerGetControlDetailsA [WINMM.@]
3348 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
3349 DWORD fdwDetails)
3351 UINT ret = MMSYSERR_NOTSUPPORTED;
3353 TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
3355 if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
3356 return MMSYSERR_INVALPARAM;
3358 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
3359 case MIXER_GETCONTROLDETAILSF_VALUE:
3360 /* can safely use A structure as it is, no string inside */
3361 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3362 break;
3363 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
3365 MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = lpmcdA->paDetails;
3366 MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
3367 int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3368 unsigned int i;
3370 if (lpmcdA->u.cMultipleItems != 0) {
3371 size *= lpmcdA->u.cMultipleItems;
3373 pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
3374 lpmcdA->paDetails = pDetailsW;
3375 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3376 /* set up lpmcd->paDetails */
3377 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3378 /* copy from lpmcd->paDetails back to paDetailsW; */
3379 if (ret == MMSYSERR_NOERROR) {
3380 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
3381 pDetailsA->dwParam1 = pDetailsW->dwParam1;
3382 pDetailsA->dwParam2 = pDetailsW->dwParam2;
3383 WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
3384 pDetailsA->szName,
3385 sizeof(pDetailsA->szName), NULL, NULL );
3386 pDetailsA++;
3387 pDetailsW++;
3389 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3390 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3392 HeapFree(GetProcessHeap(), 0, pDetailsW);
3393 lpmcdA->paDetails = pDetailsA;
3394 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
3396 break;
3397 default:
3398 ERR("Unsupported fdwDetails=0x%08x\n", fdwDetails);
3401 return ret;
3404 /**************************************************************************
3405 * mixerGetLineControlsA [WINMM.@]
3407 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
3408 DWORD fdwControls)
3410 MIXERLINECONTROLSW mlcW;
3411 DWORD ret;
3412 unsigned int i;
3414 TRACE("(%p, %p, %x)\n", hmix, lpmlcA, fdwControls);
3416 if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
3417 lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
3418 return MMSYSERR_INVALPARAM;
3420 mlcW.cbStruct = sizeof(mlcW);
3421 mlcW.dwLineID = lpmlcA->dwLineID;
3422 mlcW.u.dwControlID = lpmlcA->u.dwControlID;
3423 mlcW.u.dwControlType = lpmlcA->u.dwControlType;
3425 /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
3426 the control count is assumed to be 1 - This is relied upon by a game,
3427 "Dynomite Deluze" */
3428 if (MIXER_GETLINECONTROLSF_ONEBYTYPE == (fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK)) {
3429 mlcW.cControls = 1;
3430 } else {
3431 mlcW.cControls = lpmlcA->cControls;
3433 mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
3434 mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
3435 mlcW.cControls * mlcW.cbmxctrl);
3437 ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
3439 if (ret == MMSYSERR_NOERROR) {
3440 lpmlcA->dwLineID = mlcW.dwLineID;
3441 lpmlcA->u.dwControlID = mlcW.u.dwControlID;
3442 lpmlcA->u.dwControlType = mlcW.u.dwControlType;
3444 for (i = 0; i < mlcW.cControls; i++) {
3445 lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
3446 lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
3447 lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
3448 lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
3449 lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
3450 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
3451 lpmlcA->pamxctrl[i].szShortName,
3452 sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
3453 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
3454 lpmlcA->pamxctrl[i].szName,
3455 sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
3456 /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
3457 * sizeof(mlcW.pamxctrl[i].Bounds) */
3458 memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
3459 sizeof(mlcW.pamxctrl[i].Bounds));
3460 /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
3461 * sizeof(mlcW.pamxctrl[i].Metrics) */
3462 memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
3463 sizeof(mlcW.pamxctrl[i].Metrics));
3467 HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
3469 return ret;
3472 static UINT WINMM_GetVolumeLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3473 MIXERCONTROLW *ctl, DWORD flags)
3475 ctl->dwControlID = (line == 0xFFFF0000) ? 0 : 2;
3476 ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
3477 ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3478 ctl->cMultipleItems = 0;
3479 lstrcpyW(ctl->szShortName, volumeW);
3480 lstrcpyW(ctl->szName, volumeW);
3481 ctl->Bounds.s1.dwMinimum = 0;
3482 ctl->Bounds.s1.dwMaximum = 0xFFFF;
3483 ctl->Metrics.cSteps = 192;
3485 return MMSYSERR_NOERROR;
3488 static UINT WINMM_GetMuteLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3489 MIXERCONTROLW *ctl, DWORD flags)
3491 ctl->dwControlID = (line == 0xFFFF0000) ? 1 : 3;
3492 ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
3493 ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3494 ctl->cMultipleItems = 0;
3495 lstrcpyW(ctl->szShortName, muteW);
3496 lstrcpyW(ctl->szName, muteW);
3497 ctl->Bounds.s1.dwMinimum = 0;
3498 ctl->Bounds.s1.dwMaximum = 1;
3499 ctl->Metrics.cSteps = 0;
3501 return MMSYSERR_NOERROR;
3504 /**************************************************************************
3505 * mixerGetLineControlsW [WINMM.@]
3507 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
3508 DWORD fdwControls)
3510 WINMM_MMDevice *mmdevice;
3512 TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
3514 if(!WINMM_StartDevicesThread())
3515 return MMSYSERR_ERROR;
3517 if(fdwControls & ~(MIXER_GETLINECONTROLSF_ALL |
3518 MIXER_GETLINECONTROLSF_ONEBYID |
3519 MIXER_GETLINECONTROLSF_ONEBYTYPE |
3520 MIXER_OBJECTF_HMIXER |
3521 MIXER_OBJECTF_MIXER)){
3522 WARN("Unknown GetLineControls flag: %x\n", fdwControls);
3523 return MMSYSERR_INVALFLAG;
3526 if(!lpmlcW || lpmlcW->cbStruct < sizeof(*lpmlcW) || !lpmlcW->pamxctrl)
3527 return MMSYSERR_INVALPARAM;
3529 TRACE("dwLineID: %u\n", lpmlcW->dwLineID);
3530 TRACE("dwControl: %x\n", lpmlcW->u.dwControlID);
3531 TRACE("cControls: %u\n", lpmlcW->cControls);
3533 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwControls, NULL);
3534 if(!mmdevice)
3535 return MMSYSERR_INVALHANDLE;
3537 switch(fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK){
3538 case MIXER_GETLINECONTROLSF_ALL:
3539 if(lpmlcW->cControls != 2)
3540 return MMSYSERR_INVALPARAM;
3541 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3542 return MMSYSERR_INVALPARAM;
3543 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3544 return MIXERR_INVALLINE;
3545 WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3546 &lpmlcW->pamxctrl[0], fdwControls);
3547 WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3548 &lpmlcW->pamxctrl[1], fdwControls);
3549 return MMSYSERR_NOERROR;
3550 case MIXER_GETLINECONTROLSF_ONEBYID:
3551 if(lpmlcW->cControls != 1)
3552 return MMSYSERR_INVALPARAM;
3553 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3554 return MMSYSERR_INVALPARAM;
3555 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3556 return MIXERR_INVALLINE;
3557 if(lpmlcW->u.dwControlID == 0)
3558 return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3559 lpmlcW->pamxctrl, fdwControls);
3560 if(lpmlcW->u.dwControlID == 1)
3561 return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3562 lpmlcW->pamxctrl, fdwControls);
3563 return MMSYSERR_NOTSUPPORTED;
3564 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
3565 if(lpmlcW->cControls != 1)
3566 return MMSYSERR_INVALPARAM;
3567 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3568 return MMSYSERR_INVALPARAM;
3569 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3570 return MIXERR_INVALLINE;
3571 if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
3572 return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3573 lpmlcW->pamxctrl, fdwControls);
3574 if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
3575 return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3576 lpmlcW->pamxctrl, fdwControls);
3577 return MMSYSERR_NOTSUPPORTED;
3580 return MMSYSERR_NOTSUPPORTED;
3583 static UINT WINMM_GetSourceLineInfo(WINMM_MMDevice *mmdevice, UINT mmdev_index,
3584 MIXERLINEW *info, DWORD flags)
3586 BOOL is_out = TRUE;
3587 if(mmdevice->in_caps.szPname[0] != '\0')
3588 is_out = FALSE;
3590 if(info->dwSource != 0)
3591 return MIXERR_INVALLINE;
3593 info->dwDestination = 0;
3594 info->dwLineID = 0;
3595 info->fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
3596 info->cConnections = 0;
3597 info->cControls = 2;
3598 /* volume & mute always affect all channels, so claim 1 channel */
3599 info->cChannels = 1;
3600 info->Target.dwDeviceID = mmdev_index;
3601 info->Target.wMid = ~0;
3602 info->Target.wPid = ~0;
3603 info->Target.vDriverVersion = 0;
3605 lstrcpyW(info->szShortName, volumeW);
3606 lstrcpyW(info->szName, mastervolumeW);
3608 if(is_out){
3609 info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
3610 info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
3611 memcpy(info->Target.szPname, mmdevice->out_caps.szPname,
3612 sizeof(info->Target.szPname));
3613 }else{
3614 info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
3615 info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3616 info->Target.szPname[0] = '\0';
3619 return MMSYSERR_NOERROR;
3622 static UINT WINMM_GetDestinationLineInfo(WINMM_MMDevice *mmdevice,
3623 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3625 BOOL is_out = TRUE;
3626 if(mmdevice->in_caps.szPname[0] != '\0')
3627 is_out = FALSE;
3629 if(info->dwDestination != 0)
3630 return MIXERR_INVALLINE;
3632 info->dwSource = 0xFFFFFFFF;
3633 info->dwLineID = 0xFFFF0000;
3634 info->fdwLine = MIXERLINE_LINEF_ACTIVE;
3635 info->cConnections = 1;
3636 info->cControls = 2;
3638 lstrcpyW(info->szShortName, volumeW);
3639 lstrcpyW(info->szName, mastervolumeW);
3641 info->Target.dwDeviceID = mmdev_index;
3642 info->Target.wMid = ~0;
3643 info->Target.wPid = ~0;
3644 info->Target.vDriverVersion = 0;
3646 if(is_out){
3647 info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
3648 info->cChannels = mmdevice->out_caps.wChannels;
3649 info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3650 info->Target.szPname[0] = '\0';
3651 }else{
3652 info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
3653 info->cChannels = mmdevice->in_caps.wChannels;
3654 info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
3655 memcpy(info->Target.szPname, mmdevice->in_caps.szPname,
3656 sizeof(info->Target.szPname));
3659 return MMSYSERR_NOERROR;
3662 static UINT WINMM_GetComponentTypeLineInfo(WINMM_MMDevice *mmdevice,
3663 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3665 BOOL is_out = TRUE;
3666 if(mmdevice->in_caps.szPname[0] != '\0')
3667 is_out = FALSE;
3669 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN){
3670 if(is_out)
3671 return MIXERR_INVALLINE;
3672 info->dwDestination = 0;
3673 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3676 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS){
3677 if(!is_out)
3678 return MIXERR_INVALLINE;
3679 info->dwDestination = 0;
3680 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3683 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE){
3684 if(is_out)
3685 return MIXERR_INVALLINE;
3686 info->dwSource = 0;
3687 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3690 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
3691 if(!is_out)
3692 return MIXERR_INVALLINE;
3693 info->dwSource = 0;
3694 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3697 TRACE("Returning INVALLINE on this component type: %u\n",
3698 info->dwComponentType);
3700 return MIXERR_INVALLINE;
3703 static UINT WINMM_GetLineIDLineInfo(WINMM_MMDevice *mmdevice,
3704 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3706 if(info->dwLineID == 0xFFFF0000){
3707 info->dwDestination = 0;
3708 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3711 if(info->dwLineID == 0){
3712 info->dwSource = 0;
3713 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3716 TRACE("Returning INVALLINE on this dwLineID: %u\n", info->dwLineID);
3717 return MIXERR_INVALLINE;
3720 /**************************************************************************
3721 * mixerGetLineInfoW [WINMM.@]
3723 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
3725 UINT mmdev_index;
3726 WINMM_MMDevice *mmdevice;
3728 TRACE("(%p, %p, %x)\n", hmix, lpmliW, fdwInfo);
3730 if(!WINMM_StartDevicesThread())
3731 return MMSYSERR_ERROR;
3733 if(!lpmliW || lpmliW->cbStruct < sizeof(MIXERLINEW))
3734 return MMSYSERR_INVALPARAM;
3736 TRACE("dwDestination: %u\n", lpmliW->dwDestination);
3737 TRACE("dwSource: %u\n", lpmliW->dwSource);
3738 TRACE("dwLineID: %u\n", lpmliW->dwLineID);
3739 TRACE("fdwLine: 0x%x\n", lpmliW->fdwLine);
3740 TRACE("dwComponentType: 0x%x\n", lpmliW->dwComponentType);
3742 if(fdwInfo & ~(MIXER_GETLINEINFOF_COMPONENTTYPE |
3743 MIXER_GETLINEINFOF_DESTINATION |
3744 MIXER_GETLINEINFOF_LINEID |
3745 MIXER_GETLINEINFOF_SOURCE |
3746 MIXER_GETLINEINFOF_TARGETTYPE |
3747 MIXER_OBJECTF_HMIXER |
3748 MIXER_OBJECTF_MIXER)){
3749 WARN("Unknown GetLineInfo flag: %x\n", fdwInfo);
3750 return MMSYSERR_INVALFLAG;
3753 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwInfo, &mmdev_index);
3754 if(!mmdevice)
3755 return MMSYSERR_INVALHANDLE;
3757 switch(fdwInfo & MIXER_GETLINEINFOF_QUERYMASK){
3758 case MIXER_GETLINEINFOF_DESTINATION:
3759 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, lpmliW,
3760 fdwInfo);
3761 case MIXER_GETLINEINFOF_SOURCE:
3762 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3763 case MIXER_GETLINEINFOF_COMPONENTTYPE:
3764 return WINMM_GetComponentTypeLineInfo(mmdevice, mmdev_index, lpmliW,
3765 fdwInfo);
3766 case MIXER_GETLINEINFOF_LINEID:
3767 return WINMM_GetLineIDLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3768 case MIXER_GETLINEINFOF_TARGETTYPE:
3769 FIXME("TARGETTYPE flag not implemented!\n");
3770 return MIXERR_INVALLINE;
3773 TRACE("Returning INVALFLAG on these flags: %lx\n",
3774 fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
3775 return MMSYSERR_INVALFLAG;
3778 /**************************************************************************
3779 * mixerGetLineInfoA [WINMM.@]
3781 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
3782 DWORD fdwInfo)
3784 MIXERLINEW mliW;
3785 UINT ret;
3787 TRACE("(%p, %p, %x)\n", hmix, lpmliA, fdwInfo);
3789 if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
3790 return MMSYSERR_INVALPARAM;
3792 mliW.cbStruct = sizeof(mliW);
3793 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
3794 case MIXER_GETLINEINFOF_COMPONENTTYPE:
3795 mliW.dwComponentType = lpmliA->dwComponentType;
3796 break;
3797 case MIXER_GETLINEINFOF_DESTINATION:
3798 mliW.dwDestination = lpmliA->dwDestination;
3799 break;
3800 case MIXER_GETLINEINFOF_LINEID:
3801 mliW.dwLineID = lpmliA->dwLineID;
3802 break;
3803 case MIXER_GETLINEINFOF_SOURCE:
3804 mliW.dwDestination = lpmliA->dwDestination;
3805 mliW.dwSource = lpmliA->dwSource;
3806 break;
3807 case MIXER_GETLINEINFOF_TARGETTYPE:
3808 mliW.Target.dwType = lpmliA->Target.dwType;
3809 mliW.Target.wMid = lpmliA->Target.wMid;
3810 mliW.Target.wPid = lpmliA->Target.wPid;
3811 mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
3812 MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname)/sizeof(WCHAR));
3813 break;
3814 default:
3815 WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
3816 return MMSYSERR_INVALFLAG;
3819 ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
3821 if(ret == MMSYSERR_NOERROR)
3823 lpmliA->dwDestination = mliW.dwDestination;
3824 lpmliA->dwSource = mliW.dwSource;
3825 lpmliA->dwLineID = mliW.dwLineID;
3826 lpmliA->fdwLine = mliW.fdwLine;
3827 lpmliA->dwUser = mliW.dwUser;
3828 lpmliA->dwComponentType = mliW.dwComponentType;
3829 lpmliA->cChannels = mliW.cChannels;
3830 lpmliA->cConnections = mliW.cConnections;
3831 lpmliA->cControls = mliW.cControls;
3832 WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
3833 sizeof(lpmliA->szShortName), NULL, NULL);
3834 WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
3835 sizeof(lpmliA->szName), NULL, NULL );
3836 lpmliA->Target.dwType = mliW.Target.dwType;
3837 lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
3838 lpmliA->Target.wMid = mliW.Target.wMid;
3839 lpmliA->Target.wPid = mliW.Target.wPid;
3840 lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
3841 WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
3842 sizeof(lpmliA->Target.szPname), NULL, NULL );
3844 return ret;
3847 /**************************************************************************
3848 * mixerSetControlDetails [WINMM.@]
3850 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
3851 DWORD fdwDetails)
3853 WINMM_ControlDetails details;
3855 TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
3857 if(!WINMM_StartDevicesThread())
3858 return MMSYSERR_ERROR;
3860 if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
3861 MIXER_SETCONTROLDETAILSF_CUSTOM)
3862 return MMSYSERR_NOTSUPPORTED;
3864 if(!lpmcd)
3865 return MMSYSERR_INVALPARAM;
3867 TRACE("dwControlID: %u\n", lpmcd->dwControlID);
3869 details.hmix = hmix;
3870 details.details = lpmcd;
3871 details.flags = fdwDetails;
3873 return SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
3874 (DWORD_PTR)&details, 0);
3877 /**************************************************************************
3878 * mixerMessage [WINMM.@]
3880 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3882 TRACE("(%p, %d, %lx, %lx)\n", hmix, uMsg, dwParam1, dwParam2);
3884 return MMSYSERR_NOTSUPPORTED;