winmm: Mark headers WHDR_DONE at the last possible time.
[wine/multimedia.git] / dlls / winmm / waveform.c
blob0aa6320b412ee9926bd8e139f2def11bbba67268
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);
2785 return MMSYSERR_NOTSUPPORTED;
2788 /**************************************************************************
2789 * waveInGetNumDevs [WINMM.@]
2791 UINT WINAPI waveInGetNumDevs(void)
2793 if(!WINMM_StartDevicesThread())
2794 return 0;
2796 TRACE("count: %u\n", g_inmmdevices_count);
2798 return g_inmmdevices_count;
2801 /**************************************************************************
2802 * waveInGetDevCapsW [WINMM.@]
2804 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2806 WAVEINCAPSW mapper_caps, *caps;
2808 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2810 if(!WINMM_StartDevicesThread())
2811 return MMSYSERR_ERROR;
2813 if(!lpCaps)
2814 return MMSYSERR_INVALPARAM;
2816 if(WINMM_IsMapper(uDeviceID)){
2817 /* FIXME: Should be localized */
2818 static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
2819 'n','d',' ','M','a','p','p','e','r',0};
2821 mapper_caps.wMid = 0xFF;
2822 mapper_caps.wPid = 0xFF;
2823 mapper_caps.vDriverVersion = 0x00010001;
2824 mapper_caps.dwFormats = 0xFFFFFFFF;
2825 mapper_caps.wReserved1 = 0;
2826 mapper_caps.wChannels = 2;
2827 lstrcpyW(mapper_caps.szPname, mapper_pnameW);
2829 caps = &mapper_caps;
2830 }else{
2831 if(uDeviceID >= g_inmmdevices_count)
2832 return MMSYSERR_BADDEVICEID;
2834 caps = &g_in_mmdevices[uDeviceID].in_caps;
2837 memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2839 return MMSYSERR_NOERROR;
2842 /**************************************************************************
2843 * waveInGetDevCapsA [WINMM.@]
2845 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2847 UINT ret;
2848 WAVEINCAPSW wicW;
2850 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2852 if(!WINMM_StartDevicesThread())
2853 return MMSYSERR_ERROR;
2855 if(!lpCaps)
2856 return MMSYSERR_INVALPARAM;
2858 ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2860 if (ret == MMSYSERR_NOERROR) {
2861 WAVEINCAPSA wicA;
2862 wicA.wMid = wicW.wMid;
2863 wicA.wPid = wicW.wPid;
2864 wicA.vDriverVersion = wicW.vDriverVersion;
2865 WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2866 sizeof(wicA.szPname), NULL, NULL );
2867 wicA.dwFormats = wicW.dwFormats;
2868 wicA.wChannels = wicW.wChannels;
2869 memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2871 return ret;
2874 /**************************************************************************
2875 * waveInOpen [WINMM.@]
2877 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2878 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2879 DWORD_PTR dwInstance, DWORD dwFlags)
2881 LRESULT res;
2882 WINMM_OpenInfo info;
2883 WINMM_CBInfo cb_info;
2885 TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
2886 dwCallback, dwInstance, dwFlags);
2888 if(!WINMM_StartDevicesThread())
2889 return MMSYSERR_ERROR;
2891 if(!lphWaveIn && !(dwFlags & WAVE_FORMAT_QUERY))
2892 return MMSYSERR_INVALPARAM;
2894 res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2895 if(res != MMSYSERR_NOERROR)
2896 return res;
2898 info.format = (WAVEFORMATEX*)lpFormat;
2899 info.callback = dwCallback;
2900 info.cb_user = dwInstance;
2901 info.req_device = uDeviceID;
2902 info.flags = dwFlags;
2904 res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
2905 if(res != MMSYSERR_NOERROR)
2906 return res;
2908 if(lphWaveIn)
2909 *lphWaveIn = (HWAVEIN)info.handle;
2911 cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2912 cb_info.callback = dwCallback;
2913 cb_info.user = dwInstance;
2914 cb_info.hwave = info.handle;
2916 WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
2918 return res;
2921 /**************************************************************************
2922 * waveInClose [WINMM.@]
2924 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2926 WINMM_Device *device;
2927 WINMM_CBInfo cb_info;
2928 UINT res;
2930 TRACE("(%p)\n", hWaveIn);
2932 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
2934 if(!WINMM_ValidateAndLock(device))
2935 return MMSYSERR_INVALHANDLE;
2937 cb_info = device->cb_info;
2939 LeaveCriticalSection(&device->lock);
2941 res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
2943 if(res == MMSYSERR_NOERROR)
2944 WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
2946 return res;
2949 /**************************************************************************
2950 * waveInPrepareHeader [WINMM.@]
2952 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2953 UINT uSize)
2955 TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2957 if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2958 return MMSYSERR_INVALPARAM;
2960 if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2961 return WAVERR_STILLPLAYING;
2963 return WINMM_PrepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
2966 /**************************************************************************
2967 * waveInUnprepareHeader [WINMM.@]
2969 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2970 UINT uSize)
2972 TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
2974 if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
2975 return MMSYSERR_INVALPARAM;
2977 if(!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
2978 return MMSYSERR_NOERROR;
2980 if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2981 return WAVERR_STILLPLAYING;
2983 return WINMM_UnprepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
2986 /**************************************************************************
2987 * waveInAddBuffer [WINMM.@]
2989 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn, WAVEHDR *header, UINT uSize)
2991 WINMM_Device *device;
2993 TRACE("(%p, %p, %u)\n", hWaveIn, header, uSize);
2995 if(!header || uSize < sizeof(WAVEHDR))
2996 return MMSYSERR_INVALPARAM;
2998 if(!(header->dwFlags & WHDR_PREPARED))
2999 return WAVERR_UNPREPARED;
3001 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3003 if(!WINMM_ValidateAndLock(device))
3004 return MMSYSERR_INVALHANDLE;
3006 if(!device->first)
3007 device->first = device->last = header;
3008 else{
3009 device->last->lpNext = header;
3010 device->last = header;
3013 header->dwBytesRecorded = 0;
3014 header->lpNext = NULL;
3015 header->dwFlags &= ~WHDR_DONE;
3016 header->dwFlags |= WHDR_INQUEUE;
3018 LeaveCriticalSection(&device->lock);
3020 return MMSYSERR_NOERROR;
3023 /**************************************************************************
3024 * waveInReset [WINMM.@]
3026 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
3028 TRACE("(%p)\n", hWaveIn);
3030 return WINMM_Reset((HWAVE)hWaveIn);
3033 /**************************************************************************
3034 * waveInStart [WINMM.@]
3036 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
3038 WINMM_Device *device;
3039 HRESULT hr;
3041 TRACE("(%p)\n", hWaveIn);
3043 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3045 if(!WINMM_ValidateAndLock(device))
3046 return MMSYSERR_INVALHANDLE;
3048 hr = WINMM_BeginPlaying(device);
3049 if(FAILED(hr)){
3050 LeaveCriticalSection(&device->lock);
3051 return MMSYSERR_ERROR;
3054 LeaveCriticalSection(&device->lock);
3056 return MMSYSERR_NOERROR;
3059 /**************************************************************************
3060 * waveInStop [WINMM.@]
3062 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
3064 WINMM_CBInfo cb_info;
3065 WINMM_Device *device;
3066 WAVEHDR *buf;
3067 HRESULT hr;
3069 TRACE("(%p)\n", hWaveIn);
3071 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3073 if(!WINMM_ValidateAndLock(device))
3074 return MMSYSERR_INVALHANDLE;
3076 hr = WINMM_Pause((HWAVE)hWaveIn);
3077 if(FAILED(hr)){
3078 LeaveCriticalSection(&device->lock);
3079 return MMSYSERR_ERROR;
3081 device->stopped = TRUE;
3083 buf = device->first;
3084 if(buf && buf->dwBytesRecorded > 0){
3085 device->first = buf->lpNext;
3086 }else
3087 buf = NULL;
3089 cb_info = device->cb_info;
3091 LeaveCriticalSection(&device->lock);
3093 if(buf){
3094 buf->dwFlags &= ~WHDR_INQUEUE;
3095 buf->dwFlags |= WHDR_DONE;
3096 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
3099 return MMSYSERR_NOERROR;
3102 /**************************************************************************
3103 * waveInGetPosition [WINMM.@]
3105 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
3106 UINT uSize)
3108 TRACE("(%p, %p, %u)\n", hWaveIn, lpTime, uSize);
3110 if(!uSize || !lpTime || uSize != sizeof(MMTIME))
3111 return MMSYSERR_INVALPARAM;
3113 return WINMM_GetPosition((HWAVE)hWaveIn, lpTime);
3116 /**************************************************************************
3117 * waveInGetID [WINMM.@]
3119 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
3121 UINT dev, junk;
3122 BOOL is_out;
3123 WINMM_Device *device;
3125 TRACE("(%p, %p)\n", hWaveIn, lpuDeviceID);
3127 if(!lpuDeviceID)
3128 return MMSYSERR_INVALPARAM;
3130 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3131 if(!WINMM_ValidateAndLock(device))
3132 return MMSYSERR_INVALHANDLE;
3134 LeaveCriticalSection(&device->lock);
3136 WINMM_DecomposeHWAVE((HWAVE)hWaveIn, lpuDeviceID, &is_out, &dev, &junk);
3138 return MMSYSERR_NOERROR;
3141 /**************************************************************************
3142 * waveInMessage [WINMM.@]
3144 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
3145 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3147 TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
3149 switch(uMessage){
3150 case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
3151 return WINMM_QueryInstanceIDSize(HandleToULong(hWaveIn),
3152 (DWORD_PTR*)dwParam1, FALSE);
3153 case DRV_QUERYFUNCTIONINSTANCEID:
3154 return WINMM_QueryInstanceID(HandleToULong(hWaveIn), (WCHAR*)dwParam1, dwParam2, FALSE);
3157 return MMSYSERR_NOTSUPPORTED;
3160 UINT WINAPI mixerGetNumDevs(void)
3162 TRACE("\n");
3164 if(!WINMM_StartDevicesThread())
3165 return 0;
3167 return g_outmmdevices_count + g_inmmdevices_count;
3170 /**************************************************************************
3171 * mixerGetDevCapsA [WINMM.@]
3173 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
3175 MIXERCAPSW micW;
3176 UINT ret;
3178 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3180 if(!lpCaps)
3181 return MMSYSERR_INVALPARAM;
3183 ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
3185 if (ret == MMSYSERR_NOERROR) {
3186 MIXERCAPSA micA;
3187 micA.wMid = micW.wMid;
3188 micA.wPid = micW.wPid;
3189 micA.vDriverVersion = micW.vDriverVersion;
3190 WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
3191 sizeof(micA.szPname), NULL, NULL );
3192 micA.fdwSupport = micW.fdwSupport;
3193 micA.cDestinations = micW.cDestinations;
3194 memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
3196 return ret;
3199 /**************************************************************************
3200 * mixerGetDevCapsW [WINMM.@]
3202 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
3204 WINMM_MMDevice *mmdevice;
3205 MIXERCAPSW caps;
3207 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3209 if(!WINMM_StartDevicesThread())
3210 return MMSYSERR_ERROR;
3212 if(!lpCaps)
3213 return MMSYSERR_INVALPARAM;
3215 if(!uSize)
3216 return MMSYSERR_NOERROR;
3218 if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3219 return MMSYSERR_BADDEVICEID;
3221 if(uDeviceID < g_outmmdevices_count){
3222 mmdevice = &g_out_mmdevices[uDeviceID];
3223 memcpy(caps.szPname, mmdevice->out_caps.szPname, sizeof(caps.szPname));
3224 }else{
3225 mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3226 memcpy(caps.szPname, mmdevice->in_caps.szPname, sizeof(caps.szPname));
3229 caps.wMid = 0xFF;
3230 caps.wPid = 0xFF;
3231 caps.vDriverVersion = 0x00010001;
3232 caps.fdwSupport = 0;
3233 caps.cDestinations = 1;
3235 memcpy(lpCaps, &caps, uSize);
3237 return MMSYSERR_NOERROR;
3240 /**************************************************************************
3241 * mixerOpen [WINMM.@]
3243 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
3244 DWORD_PTR dwInstance, DWORD fdwOpen)
3246 WINMM_MMDevice *mmdevice;
3247 MMRESULT mr;
3249 TRACE("(%p, %d, %lx, %lx, %x)\n", lphMix, uDeviceID, dwCallback,
3250 dwInstance, fdwOpen);
3252 if(!WINMM_StartDevicesThread())
3253 return MMSYSERR_ERROR;
3255 if(!lphMix)
3256 return MMSYSERR_INVALPARAM;
3258 mr = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
3259 if(mr != MMSYSERR_NOERROR)
3260 return mr;
3262 if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3263 return MMSYSERR_BADDEVICEID;
3265 if(uDeviceID < g_outmmdevices_count){
3266 mmdevice = &g_out_mmdevices[uDeviceID];
3267 *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID, TRUE,
3268 mmdevice->mixer_count);
3269 }else{
3270 mmdevice = &g_in_mmdevices[uDeviceID - g_outmmdevices_count];
3271 *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID - g_outmmdevices_count,
3272 FALSE, mmdevice->mixer_count);
3275 ++mmdevice->mixer_count;
3277 return MMSYSERR_NOERROR;
3280 /**************************************************************************
3281 * mixerClose [WINMM.@]
3283 UINT WINAPI mixerClose(HMIXER hMix)
3285 TRACE("(%p)\n", hMix);
3287 return MMSYSERR_NOERROR;
3290 /**************************************************************************
3291 * mixerGetID [WINMM.@]
3293 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
3295 WINMM_MMDevice *mmdevice;
3297 TRACE("(%p, %p, %x)\n", hmix, lpid, fdwID);
3299 if(!WINMM_StartDevicesThread())
3300 return MMSYSERR_ERROR;
3302 if(!lpid)
3303 return MMSYSERR_INVALPARAM;
3305 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwID, lpid);
3306 if(!mmdevice)
3307 return MMSYSERR_INVALHANDLE;
3309 if(mmdevice->in_caps.szPname[0] != '\0')
3310 *lpid += g_outmmdevices_count;
3312 return MMSYSERR_NOERROR;
3315 /**************************************************************************
3316 * mixerGetControlDetailsW [WINMM.@]
3318 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
3319 DWORD fdwDetails)
3321 WINMM_ControlDetails details;
3323 TRACE("(%p, %p, %x)\n", hmix, lpmcdW, fdwDetails);
3325 if(!WINMM_StartDevicesThread())
3326 return MMSYSERR_ERROR;
3328 if(!lpmcdW)
3329 return MMSYSERR_INVALPARAM;
3331 TRACE("dwControlID: %u\n", lpmcdW->dwControlID);
3333 details.hmix = hmix;
3334 details.details = lpmcdW;
3335 details.flags = fdwDetails;
3337 return SendMessageW(g_devices_hwnd, MXDM_GETCONTROLDETAILS,
3338 (DWORD_PTR)&details, 0);
3341 /**************************************************************************
3342 * mixerGetControlDetailsA [WINMM.@]
3344 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
3345 DWORD fdwDetails)
3347 UINT ret = MMSYSERR_NOTSUPPORTED;
3349 TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
3351 if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
3352 return MMSYSERR_INVALPARAM;
3354 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
3355 case MIXER_GETCONTROLDETAILSF_VALUE:
3356 /* can safely use A structure as it is, no string inside */
3357 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3358 break;
3359 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
3361 MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = lpmcdA->paDetails;
3362 MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
3363 int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3364 unsigned int i;
3366 if (lpmcdA->u.cMultipleItems != 0) {
3367 size *= lpmcdA->u.cMultipleItems;
3369 pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
3370 lpmcdA->paDetails = pDetailsW;
3371 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3372 /* set up lpmcd->paDetails */
3373 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3374 /* copy from lpmcd->paDetails back to paDetailsW; */
3375 if (ret == MMSYSERR_NOERROR) {
3376 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
3377 pDetailsA->dwParam1 = pDetailsW->dwParam1;
3378 pDetailsA->dwParam2 = pDetailsW->dwParam2;
3379 WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
3380 pDetailsA->szName,
3381 sizeof(pDetailsA->szName), NULL, NULL );
3382 pDetailsA++;
3383 pDetailsW++;
3385 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3386 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3388 HeapFree(GetProcessHeap(), 0, pDetailsW);
3389 lpmcdA->paDetails = pDetailsA;
3390 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
3392 break;
3393 default:
3394 ERR("Unsupported fdwDetails=0x%08x\n", fdwDetails);
3397 return ret;
3400 /**************************************************************************
3401 * mixerGetLineControlsA [WINMM.@]
3403 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
3404 DWORD fdwControls)
3406 MIXERLINECONTROLSW mlcW;
3407 DWORD ret;
3408 unsigned int i;
3410 TRACE("(%p, %p, %x)\n", hmix, lpmlcA, fdwControls);
3412 if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
3413 lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
3414 return MMSYSERR_INVALPARAM;
3416 mlcW.cbStruct = sizeof(mlcW);
3417 mlcW.dwLineID = lpmlcA->dwLineID;
3418 mlcW.u.dwControlID = lpmlcA->u.dwControlID;
3419 mlcW.u.dwControlType = lpmlcA->u.dwControlType;
3421 /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
3422 the control count is assumed to be 1 - This is relied upon by a game,
3423 "Dynomite Deluze" */
3424 if (MIXER_GETLINECONTROLSF_ONEBYTYPE == (fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK)) {
3425 mlcW.cControls = 1;
3426 } else {
3427 mlcW.cControls = lpmlcA->cControls;
3429 mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
3430 mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
3431 mlcW.cControls * mlcW.cbmxctrl);
3433 ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
3435 if (ret == MMSYSERR_NOERROR) {
3436 lpmlcA->dwLineID = mlcW.dwLineID;
3437 lpmlcA->u.dwControlID = mlcW.u.dwControlID;
3438 lpmlcA->u.dwControlType = mlcW.u.dwControlType;
3440 for (i = 0; i < mlcW.cControls; i++) {
3441 lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
3442 lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
3443 lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
3444 lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
3445 lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
3446 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
3447 lpmlcA->pamxctrl[i].szShortName,
3448 sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
3449 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
3450 lpmlcA->pamxctrl[i].szName,
3451 sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
3452 /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
3453 * sizeof(mlcW.pamxctrl[i].Bounds) */
3454 memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
3455 sizeof(mlcW.pamxctrl[i].Bounds));
3456 /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
3457 * sizeof(mlcW.pamxctrl[i].Metrics) */
3458 memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
3459 sizeof(mlcW.pamxctrl[i].Metrics));
3463 HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
3465 return ret;
3468 static UINT WINMM_GetVolumeLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3469 MIXERCONTROLW *ctl, DWORD flags)
3471 ctl->dwControlID = (line == 0xFFFF0000) ? 0 : 2;
3472 ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
3473 ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3474 ctl->cMultipleItems = 0;
3475 lstrcpyW(ctl->szShortName, volumeW);
3476 lstrcpyW(ctl->szName, volumeW);
3477 ctl->Bounds.s1.dwMinimum = 0;
3478 ctl->Bounds.s1.dwMaximum = 0xFFFF;
3479 ctl->Metrics.cSteps = 192;
3481 return MMSYSERR_NOERROR;
3484 static UINT WINMM_GetMuteLineControl(WINMM_MMDevice *mmdevice, DWORD line,
3485 MIXERCONTROLW *ctl, DWORD flags)
3487 ctl->dwControlID = (line == 0xFFFF0000) ? 1 : 3;
3488 ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
3489 ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
3490 ctl->cMultipleItems = 0;
3491 lstrcpyW(ctl->szShortName, muteW);
3492 lstrcpyW(ctl->szName, muteW);
3493 ctl->Bounds.s1.dwMinimum = 0;
3494 ctl->Bounds.s1.dwMaximum = 1;
3495 ctl->Metrics.cSteps = 0;
3497 return MMSYSERR_NOERROR;
3500 /**************************************************************************
3501 * mixerGetLineControlsW [WINMM.@]
3503 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
3504 DWORD fdwControls)
3506 WINMM_MMDevice *mmdevice;
3508 TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
3510 if(!WINMM_StartDevicesThread())
3511 return MMSYSERR_ERROR;
3513 if(fdwControls & ~(MIXER_GETLINECONTROLSF_ALL |
3514 MIXER_GETLINECONTROLSF_ONEBYID |
3515 MIXER_GETLINECONTROLSF_ONEBYTYPE |
3516 MIXER_OBJECTF_HMIXER |
3517 MIXER_OBJECTF_MIXER)){
3518 WARN("Unknown GetLineControls flag: %x\n", fdwControls);
3519 return MMSYSERR_INVALFLAG;
3522 if(!lpmlcW || lpmlcW->cbStruct < sizeof(*lpmlcW) || !lpmlcW->pamxctrl)
3523 return MMSYSERR_INVALPARAM;
3525 TRACE("dwLineID: %u\n", lpmlcW->dwLineID);
3526 TRACE("dwControl: %x\n", lpmlcW->u.dwControlID);
3527 TRACE("cControls: %u\n", lpmlcW->cControls);
3529 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwControls, NULL);
3530 if(!mmdevice)
3531 return MMSYSERR_INVALHANDLE;
3533 switch(fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK){
3534 case MIXER_GETLINECONTROLSF_ALL:
3535 if(lpmlcW->cControls != 2)
3536 return MMSYSERR_INVALPARAM;
3537 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3538 return MMSYSERR_INVALPARAM;
3539 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3540 return MIXERR_INVALLINE;
3541 WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3542 &lpmlcW->pamxctrl[0], fdwControls);
3543 WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3544 &lpmlcW->pamxctrl[1], fdwControls);
3545 return MMSYSERR_NOERROR;
3546 case MIXER_GETLINECONTROLSF_ONEBYID:
3547 if(lpmlcW->cControls != 1)
3548 return MMSYSERR_INVALPARAM;
3549 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3550 return MMSYSERR_INVALPARAM;
3551 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3552 return MIXERR_INVALLINE;
3553 if(lpmlcW->u.dwControlID == 0)
3554 return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3555 lpmlcW->pamxctrl, fdwControls);
3556 if(lpmlcW->u.dwControlID == 1)
3557 return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3558 lpmlcW->pamxctrl, fdwControls);
3559 return MMSYSERR_NOTSUPPORTED;
3560 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
3561 if(lpmlcW->cControls != 1)
3562 return MMSYSERR_INVALPARAM;
3563 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
3564 return MMSYSERR_INVALPARAM;
3565 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
3566 return MIXERR_INVALLINE;
3567 if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
3568 return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
3569 lpmlcW->pamxctrl, fdwControls);
3570 if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
3571 return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
3572 lpmlcW->pamxctrl, fdwControls);
3573 return MMSYSERR_NOTSUPPORTED;
3576 return MMSYSERR_NOTSUPPORTED;
3579 static UINT WINMM_GetSourceLineInfo(WINMM_MMDevice *mmdevice, UINT mmdev_index,
3580 MIXERLINEW *info, DWORD flags)
3582 BOOL is_out = TRUE;
3583 if(mmdevice->in_caps.szPname[0] != '\0')
3584 is_out = FALSE;
3586 if(info->dwSource != 0)
3587 return MIXERR_INVALLINE;
3589 info->dwDestination = 0;
3590 info->dwLineID = 0;
3591 info->fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
3592 info->cConnections = 0;
3593 info->cControls = 2;
3594 /* volume & mute always affect all channels, so claim 1 channel */
3595 info->cChannels = 1;
3596 info->Target.dwDeviceID = mmdev_index;
3597 info->Target.wMid = ~0;
3598 info->Target.wPid = ~0;
3599 info->Target.vDriverVersion = 0;
3601 lstrcpyW(info->szShortName, volumeW);
3602 lstrcpyW(info->szName, mastervolumeW);
3604 if(is_out){
3605 info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
3606 info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
3607 memcpy(info->Target.szPname, mmdevice->out_caps.szPname,
3608 sizeof(info->Target.szPname));
3609 }else{
3610 info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE;
3611 info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3612 info->Target.szPname[0] = '\0';
3615 return MMSYSERR_NOERROR;
3618 static UINT WINMM_GetDestinationLineInfo(WINMM_MMDevice *mmdevice,
3619 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3621 BOOL is_out = TRUE;
3622 if(mmdevice->in_caps.szPname[0] != '\0')
3623 is_out = FALSE;
3625 if(info->dwDestination != 0)
3626 return MIXERR_INVALLINE;
3628 info->dwSource = 0xFFFFFFFF;
3629 info->dwLineID = 0xFFFF0000;
3630 info->fdwLine = MIXERLINE_LINEF_ACTIVE;
3631 info->cConnections = 1;
3632 info->cControls = 2;
3634 lstrcpyW(info->szShortName, volumeW);
3635 lstrcpyW(info->szName, mastervolumeW);
3637 info->Target.dwDeviceID = mmdev_index;
3638 info->Target.wMid = ~0;
3639 info->Target.wPid = ~0;
3640 info->Target.vDriverVersion = 0;
3642 if(is_out){
3643 info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
3644 info->cChannels = mmdevice->out_caps.wChannels;
3645 info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
3646 info->Target.szPname[0] = '\0';
3647 }else{
3648 info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
3649 info->cChannels = mmdevice->in_caps.wChannels;
3650 info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
3651 memcpy(info->Target.szPname, mmdevice->in_caps.szPname,
3652 sizeof(info->Target.szPname));
3655 return MMSYSERR_NOERROR;
3658 static UINT WINMM_GetComponentTypeLineInfo(WINMM_MMDevice *mmdevice,
3659 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3661 BOOL is_out = TRUE;
3662 if(mmdevice->in_caps.szPname[0] != '\0')
3663 is_out = FALSE;
3665 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN){
3666 if(is_out)
3667 return MIXERR_INVALLINE;
3668 info->dwDestination = 0;
3669 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3672 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS){
3673 if(!is_out)
3674 return MIXERR_INVALLINE;
3675 info->dwDestination = 0;
3676 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3679 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_LINE){
3680 if(is_out)
3681 return MIXERR_INVALLINE;
3682 info->dwSource = 0;
3683 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3686 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
3687 if(!is_out)
3688 return MIXERR_INVALLINE;
3689 info->dwSource = 0;
3690 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3693 TRACE("Returning INVALLINE on this component type: %u\n",
3694 info->dwComponentType);
3696 return MIXERR_INVALLINE;
3699 static UINT WINMM_GetLineIDLineInfo(WINMM_MMDevice *mmdevice,
3700 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
3702 if(info->dwLineID == 0xFFFF0000){
3703 info->dwDestination = 0;
3704 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
3707 if(info->dwLineID == 0){
3708 info->dwSource = 0;
3709 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
3712 TRACE("Returning INVALLINE on this dwLineID: %u\n", info->dwLineID);
3713 return MIXERR_INVALLINE;
3716 /**************************************************************************
3717 * mixerGetLineInfoW [WINMM.@]
3719 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
3721 UINT mmdev_index;
3722 WINMM_MMDevice *mmdevice;
3724 TRACE("(%p, %p, %x)\n", hmix, lpmliW, fdwInfo);
3726 if(!WINMM_StartDevicesThread())
3727 return MMSYSERR_ERROR;
3729 if(!lpmliW || lpmliW->cbStruct < sizeof(MIXERLINEW))
3730 return MMSYSERR_INVALPARAM;
3732 TRACE("dwDestination: %u\n", lpmliW->dwDestination);
3733 TRACE("dwSource: %u\n", lpmliW->dwSource);
3734 TRACE("dwLineID: %u\n", lpmliW->dwLineID);
3735 TRACE("fdwLine: 0x%x\n", lpmliW->fdwLine);
3736 TRACE("dwComponentType: 0x%x\n", lpmliW->dwComponentType);
3738 if(fdwInfo & ~(MIXER_GETLINEINFOF_COMPONENTTYPE |
3739 MIXER_GETLINEINFOF_DESTINATION |
3740 MIXER_GETLINEINFOF_LINEID |
3741 MIXER_GETLINEINFOF_SOURCE |
3742 MIXER_GETLINEINFOF_TARGETTYPE |
3743 MIXER_OBJECTF_HMIXER |
3744 MIXER_OBJECTF_MIXER)){
3745 WARN("Unknown GetLineInfo flag: %x\n", fdwInfo);
3746 return MMSYSERR_INVALFLAG;
3749 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwInfo, &mmdev_index);
3750 if(!mmdevice)
3751 return MMSYSERR_INVALHANDLE;
3753 switch(fdwInfo & MIXER_GETLINEINFOF_QUERYMASK){
3754 case MIXER_GETLINEINFOF_DESTINATION:
3755 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, lpmliW,
3756 fdwInfo);
3757 case MIXER_GETLINEINFOF_SOURCE:
3758 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3759 case MIXER_GETLINEINFOF_COMPONENTTYPE:
3760 return WINMM_GetComponentTypeLineInfo(mmdevice, mmdev_index, lpmliW,
3761 fdwInfo);
3762 case MIXER_GETLINEINFOF_LINEID:
3763 return WINMM_GetLineIDLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
3764 case MIXER_GETLINEINFOF_TARGETTYPE:
3765 FIXME("TARGETTYPE flag not implemented!\n");
3766 return MIXERR_INVALLINE;
3769 TRACE("Returning INVALFLAG on these flags: %lx\n",
3770 fdwInfo & MIXER_GETLINEINFOF_QUERYMASK);
3771 return MMSYSERR_INVALFLAG;
3774 /**************************************************************************
3775 * mixerGetLineInfoA [WINMM.@]
3777 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
3778 DWORD fdwInfo)
3780 MIXERLINEW mliW;
3781 UINT ret;
3783 TRACE("(%p, %p, %x)\n", hmix, lpmliA, fdwInfo);
3785 if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
3786 return MMSYSERR_INVALPARAM;
3788 mliW.cbStruct = sizeof(mliW);
3789 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
3790 case MIXER_GETLINEINFOF_COMPONENTTYPE:
3791 mliW.dwComponentType = lpmliA->dwComponentType;
3792 break;
3793 case MIXER_GETLINEINFOF_DESTINATION:
3794 mliW.dwDestination = lpmliA->dwDestination;
3795 break;
3796 case MIXER_GETLINEINFOF_LINEID:
3797 mliW.dwLineID = lpmliA->dwLineID;
3798 break;
3799 case MIXER_GETLINEINFOF_SOURCE:
3800 mliW.dwDestination = lpmliA->dwDestination;
3801 mliW.dwSource = lpmliA->dwSource;
3802 break;
3803 case MIXER_GETLINEINFOF_TARGETTYPE:
3804 mliW.Target.dwType = lpmliA->Target.dwType;
3805 mliW.Target.wMid = lpmliA->Target.wMid;
3806 mliW.Target.wPid = lpmliA->Target.wPid;
3807 mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
3808 MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname)/sizeof(WCHAR));
3809 break;
3810 default:
3811 WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
3812 return MMSYSERR_INVALFLAG;
3815 ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
3817 if(ret == MMSYSERR_NOERROR)
3819 lpmliA->dwDestination = mliW.dwDestination;
3820 lpmliA->dwSource = mliW.dwSource;
3821 lpmliA->dwLineID = mliW.dwLineID;
3822 lpmliA->fdwLine = mliW.fdwLine;
3823 lpmliA->dwUser = mliW.dwUser;
3824 lpmliA->dwComponentType = mliW.dwComponentType;
3825 lpmliA->cChannels = mliW.cChannels;
3826 lpmliA->cConnections = mliW.cConnections;
3827 lpmliA->cControls = mliW.cControls;
3828 WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
3829 sizeof(lpmliA->szShortName), NULL, NULL);
3830 WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
3831 sizeof(lpmliA->szName), NULL, NULL );
3832 lpmliA->Target.dwType = mliW.Target.dwType;
3833 lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
3834 lpmliA->Target.wMid = mliW.Target.wMid;
3835 lpmliA->Target.wPid = mliW.Target.wPid;
3836 lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
3837 WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
3838 sizeof(lpmliA->Target.szPname), NULL, NULL );
3840 return ret;
3843 /**************************************************************************
3844 * mixerSetControlDetails [WINMM.@]
3846 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
3847 DWORD fdwDetails)
3849 WINMM_ControlDetails details;
3851 TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
3853 if(!WINMM_StartDevicesThread())
3854 return MMSYSERR_ERROR;
3856 if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
3857 MIXER_SETCONTROLDETAILSF_CUSTOM)
3858 return MMSYSERR_NOTSUPPORTED;
3860 if(!lpmcd)
3861 return MMSYSERR_INVALPARAM;
3863 TRACE("dwControlID: %u\n", lpmcd->dwControlID);
3865 details.hmix = hmix;
3866 details.details = lpmcd;
3867 details.flags = fdwDetails;
3869 return SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
3870 (DWORD_PTR)&details, 0);
3873 /**************************************************************************
3874 * mixerMessage [WINMM.@]
3876 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3878 TRACE("(%p, %d, %lx, %lx)\n", hmix, uMsg, dwParam1, dwParam2);
3880 return MMSYSERR_NOTSUPPORTED;