winmm: Support WAVE_MAPPED flag in waveOut*.
[wine.git] / dlls / winmm / waveform.c
blob57c6add096db923c1e16156cac63dadf116e2be9
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 /* TODO: Remove after dsound has been rewritten for mmdevapi */
50 #include "dsound.h"
51 #include "dsdriver.h"
52 #define DS_HW_ACCEL_FULL 0
54 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
56 /* HWAVE (and HMIXER) format:
58 * XXXX... 1FDD DDDD IIII IIII
59 * X = unused (must be 0)
60 * 1 = the bit is set to 1, to avoid all-zero HWAVEs
61 * F = flow direction (0 = IN, 1 = OUT)
62 * D = index into g_out_mmdevices
63 * I = index in the mmdevice's devices array
65 * Two reasons that we don't just use pointers:
66 * - HWAVEs must fit into 16 bits for compatibility with old applications.
67 * - We must be able to identify bad devices without crashing.
70 #define MAX_DEVICES 256
72 typedef struct _WINMM_CBInfo {
73 DWORD_PTR callback;
74 DWORD_PTR user;
75 DWORD flags;
76 HWAVE hwave;
77 } WINMM_CBInfo;
79 struct _WINMM_MMDevice;
80 typedef struct _WINMM_MMDevice WINMM_MMDevice;
82 typedef struct _WINMM_Device {
83 WINMM_CBInfo cb_info;
85 HWAVE handle;
87 BOOL open;
89 IMMDevice *device;
90 IAudioClient *client;
91 IAudioRenderClient *render;
92 IAudioClock *clock;
93 IAudioStreamVolume *volume;
95 HACMSTREAM acm_handle;
96 ACMSTREAMHEADER acm_hdr;
97 UINT32 acm_offs;
99 WAVEHDR *first, *last, *playing, *loop_start;
101 BOOL stopped;
102 DWORD loop_counter;
103 UINT32 bytes_per_frame, samples_per_sec, ofs_bytes, played_frames;
105 /* stored in frames of sample rate, *not* AC::GetFrequency */
106 UINT64 last_clock_pos;
108 HANDLE event;
109 CRITICAL_SECTION lock;
111 WINMM_MMDevice *parent;
112 } WINMM_Device;
114 struct _WINMM_MMDevice {
115 WAVEOUTCAPSW out_caps; /* must not be modified outside of WINMM_InitMMDevices*/
116 WAVEINCAPSW in_caps; /* must not be modified outside of WINMM_InitMMDevices*/
117 WCHAR *dev_id;
119 GUID session;
121 CRITICAL_SECTION lock;
123 WINMM_Device *devices[MAX_DEVICES];
126 static WINMM_MMDevice *g_out_mmdevices;
127 static UINT g_outmmdevices_count;
129 static WINMM_MMDevice *g_in_mmdevices;
130 static UINT g_inmmdevices_count;
132 static IMMDeviceEnumerator *g_devenum;
134 static CRITICAL_SECTION g_devthread_lock;
135 static HANDLE g_devices_thread;
136 static HWND g_devices_hwnd;
138 static UINT g_devhandle_count;
139 static HANDLE *g_device_handles;
140 static WINMM_Device **g_handle_devices;
142 typedef struct _WINMM_OpenInfo {
143 HWAVE handle;
144 UINT req_device;
145 WAVEFORMATEX *format;
146 DWORD_PTR callback;
147 DWORD_PTR cb_user;
148 DWORD flags;
149 } WINMM_OpenInfo;
151 static LRESULT WOD_Open(WINMM_OpenInfo *info);
152 static LRESULT WOD_Close(HWAVEOUT hwave);
154 BOOL WINMM_InitWaveform(void)
156 InitializeCriticalSection(&g_devthread_lock);
157 return TRUE;
160 static inline HWAVE WINMM_MakeHWAVE(UINT mmdevice, BOOL is_out, UINT device)
162 return ULongToHandle((1 << 15) | ((!!is_out) << 14) |
163 (mmdevice << 8) | device);
166 static inline void WINMM_DecomposeHWAVE(HWAVE hwave, UINT *mmdevice_index,
167 BOOL *is_out, UINT *device_index, UINT *junk)
169 ULONG32 l = HandleToULong(hwave);
170 *device_index = l & 0xFF;
171 *mmdevice_index = (l >> 8) & 0x3F;
172 *is_out = (l >> 14) & 0x1;
173 *junk = l >> 15;
176 static void WINMM_InitDevice(WINMM_Device *device,
177 WINMM_MMDevice *parent, HWAVE hwave)
179 InitializeCriticalSection(&device->lock);
180 device->handle = hwave;
181 device->parent = parent;
184 /* finds the first unused Device, marks it as "open", and returns
185 * a pointer to the device
187 * IMPORTANT: it is the caller's responsibility to release the device's lock
188 * on success
190 static WINMM_Device *WINMM_FindUnusedDevice(BOOL is_out, UINT mmdevice_index)
192 WINMM_MMDevice *mmdevice;
193 UINT i;
195 if(is_out)
196 mmdevice = &g_out_mmdevices[mmdevice_index];
197 else
198 return NULL;
200 EnterCriticalSection(&mmdevice->lock);
201 for(i = 0; i < MAX_DEVICES; ++i){
202 WINMM_Device *device = mmdevice->devices[i];
204 if(!device){
205 device = mmdevice->devices[i] = HeapAlloc(GetProcessHeap(),
206 HEAP_ZERO_MEMORY, sizeof(WINMM_Device));
207 if(!device){
208 LeaveCriticalSection(&mmdevice->lock);
209 return NULL;
212 WINMM_InitDevice(device, mmdevice,
213 WINMM_MakeHWAVE(mmdevice_index, is_out, i));
214 EnterCriticalSection(&device->lock);
215 }else
216 EnterCriticalSection(&device->lock);
218 if(!device->open){
219 LeaveCriticalSection(&mmdevice->lock);
220 device->open = TRUE;
221 TRACE("Found free device: mmdevice: %u, device id: %u\n",
222 mmdevice_index, i);
223 return device;
226 LeaveCriticalSection(&device->lock);
229 LeaveCriticalSection(&mmdevice->lock);
231 TRACE("All devices in use: mmdevice: %u\n", mmdevice_index);
233 return NULL;
236 static inline BOOL WINMM_ValidateAndLock(WINMM_Device *device)
238 if(!device)
239 return FALSE;
241 EnterCriticalSection(&device->lock);
243 if(!device->open){
244 LeaveCriticalSection(&device->lock);
245 return FALSE;
248 return TRUE;
251 static WINMM_Device *WINMM_GetDeviceFromHWAVE(HWAVE hwave)
253 WINMM_MMDevice *mmdevice;
254 WINMM_Device *device;
255 UINT mmdevice_index, device_index, junk;
256 BOOL is_out;
258 WINMM_DecomposeHWAVE(hwave, &mmdevice_index, &is_out, &device_index, &junk);
260 if(junk != 0x1)
261 return NULL;
263 if(mmdevice_index >= (is_out ? g_outmmdevices_count : g_inmmdevices_count))
264 return NULL;
266 if(is_out)
267 mmdevice = &g_out_mmdevices[mmdevice_index];
268 else
269 mmdevice = &g_in_mmdevices[mmdevice_index];
271 EnterCriticalSection(&mmdevice->lock);
273 device = mmdevice->devices[device_index];
275 LeaveCriticalSection(&mmdevice->lock);
277 return device;
280 /* Note: NotifyClient should never be called while holding the device lock
281 * since the client may call wave* functions from within the callback. */
282 static DWORD WINMM_NotifyClient(WINMM_CBInfo *info, WORD msg, DWORD_PTR param1,
283 DWORD_PTR param2)
285 TRACE("(%p, %u, %lx, %lx)\n", info->hwave, msg, param1, param2);
287 if(info->flags & DCB_NULL)
288 return MMSYSERR_NOERROR;
290 if(!DriverCallback(info->callback, info->flags, (HDRVR)info->hwave,
291 msg, info->user, param1, param2))
292 return MMSYSERR_ERROR;
294 return MMSYSERR_NOERROR;
297 static HRESULT WINMM_GetFriendlyName(IMMDevice *device, WCHAR *out,
298 UINT outlen)
300 IPropertyStore *ps;
301 PROPVARIANT var;
302 HRESULT hr;
304 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
305 if(FAILED(hr))
306 return hr;
308 PropVariantInit(&var);
310 hr = IPropertyStore_GetValue(ps,
311 (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &var);
312 if(FAILED(hr)){
313 IPropertyStore_Release(ps);
314 return hr;
317 lstrcpynW(out, var.u.pwszVal, outlen);
319 PropVariantClear(&var);
321 IPropertyStore_Release(ps);
323 return S_OK;
326 static HRESULT WINMM_TestFormat(IAudioClient *client, DWORD rate, DWORD depth,
327 WORD channels)
329 WAVEFORMATEX fmt, *junk;
330 HRESULT hr;
332 fmt.wFormatTag = WAVE_FORMAT_PCM;
333 fmt.nChannels = channels;
334 fmt.nSamplesPerSec = rate;
335 fmt.wBitsPerSample = depth;
336 fmt.nBlockAlign = (channels * depth) / 8;
337 fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
338 fmt.cbSize = 0;
340 hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED,
341 &fmt, &junk);
342 if(SUCCEEDED(hr))
343 CoTaskMemFree(junk);
345 return hr;
348 static struct _TestFormat {
349 DWORD flag;
350 DWORD rate;
351 DWORD depth;
352 WORD channels;
353 } formats_to_test[] = {
354 { WAVE_FORMAT_1M08, 11025, 8, 1 },
355 { WAVE_FORMAT_1M16, 11025, 16, 1 },
356 { WAVE_FORMAT_1S08, 11025, 8, 2 },
357 { WAVE_FORMAT_1S16, 11025, 16, 2 },
358 { WAVE_FORMAT_2M08, 22050, 8, 1 },
359 { WAVE_FORMAT_2M16, 22050, 16, 1 },
360 { WAVE_FORMAT_2S08, 22050, 8, 2 },
361 { WAVE_FORMAT_2S16, 22050, 16, 2 },
362 { WAVE_FORMAT_4M08, 44100, 8, 1 },
363 { WAVE_FORMAT_4M16, 44100, 16, 1 },
364 { WAVE_FORMAT_4S08, 44100, 8, 2 },
365 { WAVE_FORMAT_4S16, 44100, 16, 2 },
366 { WAVE_FORMAT_48M08, 48000, 8, 1 },
367 { WAVE_FORMAT_48M16, 48000, 16, 1 },
368 { WAVE_FORMAT_48S08, 48000, 8, 2 },
369 { WAVE_FORMAT_48S16, 48000, 16, 2 },
370 { WAVE_FORMAT_96M08, 96000, 8, 1 },
371 { WAVE_FORMAT_96M16, 96000, 16, 1 },
372 { WAVE_FORMAT_96S08, 96000, 8, 2 },
373 { WAVE_FORMAT_96S16, 96000, 16, 2 },
377 static DWORD WINMM_GetSupportedFormats(IMMDevice *device)
379 DWORD flags = 0;
380 HRESULT hr;
381 struct _TestFormat *fmt;
382 IAudioClient *client;
384 hr = IMMDevice_Activate(device, &IID_IAudioClient,
385 CLSCTX_INPROC_SERVER, NULL, (void**)&client);
386 if(FAILED(hr))
387 return 0;
389 for(fmt = formats_to_test; fmt->flag; ++fmt){
390 hr = WINMM_TestFormat(client, fmt->rate, fmt->depth, fmt->channels);
391 if(hr == S_OK)
392 flags |= fmt->flag;
395 IAudioClient_Release(client);
397 return flags;
400 static HRESULT WINMM_InitMMDevice(EDataFlow flow, IMMDevice *device,
401 WINMM_MMDevice *dev, UINT index)
403 HRESULT hr;
405 if(flow == eRender){
406 dev->out_caps.wMid = 0xFF;
407 dev->out_caps.wPid = 0xFF;
408 dev->out_caps.vDriverVersion = 0x00010001;
409 dev->out_caps.dwFormats = WINMM_GetSupportedFormats(device);
410 dev->out_caps.wReserved1 = 0;
411 dev->out_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
412 WAVECAPS_SAMPLEACCURATE;
413 dev->out_caps.wChannels = 2;
414 dev->out_caps.szPname[0] = '\0';
416 hr = WINMM_GetFriendlyName(device, dev->out_caps.szPname,
417 sizeof(dev->out_caps.szPname) /
418 sizeof(*dev->out_caps.szPname));
419 if(FAILED(hr))
420 return hr;
421 }else{
422 dev->in_caps.wMid = 0xFF;
423 dev->in_caps.wPid = 0xFF;
424 dev->in_caps.vDriverVersion = 0x00010001;
425 dev->in_caps.dwFormats = WINMM_GetSupportedFormats(device);
426 dev->in_caps.wReserved1 = 0;
427 dev->in_caps.wChannels = 2;
428 dev->in_caps.szPname[0] = '\0';
430 hr = WINMM_GetFriendlyName(device, dev->in_caps.szPname,
431 sizeof(dev->in_caps.szPname) /
432 sizeof(*dev->in_caps.szPname));
433 if(FAILED(hr))
434 return hr;
437 hr = IMMDevice_GetId(device, &dev->dev_id);
438 if(FAILED(hr))
439 return hr;
441 CoCreateGuid(&dev->session);
443 InitializeCriticalSection(&dev->lock);
445 return S_OK;
448 static HRESULT WINMM_EnumDevices(WINMM_MMDevice **devices, UINT *devcount,
449 EDataFlow flow)
451 IMMDeviceCollection *devcoll;
452 HRESULT hr;
454 hr = IMMDeviceEnumerator_EnumAudioEndpoints(g_devenum, flow,
455 DEVICE_STATE_ACTIVE, &devcoll);
456 if(FAILED(hr))
457 return hr;
459 hr = IMMDeviceCollection_GetCount(devcoll, devcount);
460 if(FAILED(hr)){
461 IMMDeviceCollection_Release(devcoll);
462 return hr;
465 if(*devcount > 0){
466 UINT n, count;
467 IMMDevice *def_dev = NULL;
469 *devices = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
470 sizeof(WINMM_MMDevice) * (*devcount));
471 if(!*devices){
472 IMMDeviceCollection_Release(devcoll);
473 return E_OUTOFMEMORY;
476 count = 0;
478 /* make sure that device 0 is the default device */
479 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(g_devenum,
480 flow, eConsole, &def_dev);
481 if(SUCCEEDED(hr)){
482 WINMM_InitMMDevice(flow, def_dev, &(*devices)[0], 0);
483 count = 1;
486 for(n = 0; n < *devcount; ++n){
487 IMMDevice *device;
489 hr = IMMDeviceCollection_Item(devcoll, n, &device);
490 if(SUCCEEDED(hr)){
491 if(device != def_dev){
492 WINMM_InitMMDevice(flow, device, &(*devices)[count], count);
493 ++count;
496 IMMDevice_Release(device);
500 if(def_dev)
501 IMMDevice_Release(def_dev);
503 *devcount = count;
506 IMMDeviceCollection_Release(devcoll);
508 return S_OK;
511 static HRESULT WINMM_InitMMDevices(void)
513 HRESULT hr;
515 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
516 CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&g_devenum);
517 if(FAILED(hr))
518 return hr;
520 hr = WINMM_EnumDevices(&g_out_mmdevices, &g_outmmdevices_count, eRender);
521 if(FAILED(hr)){
522 g_outmmdevices_count = 0;
523 g_inmmdevices_count = 0;
524 return hr;
527 hr = WINMM_EnumDevices(&g_in_mmdevices, &g_inmmdevices_count, eCapture);
528 if(FAILED(hr)){
529 g_inmmdevices_count = 0;
530 return hr;
533 return S_OK;
536 static UINT WAVE_Open(HANDLE* lphndl, UINT uDeviceID, UINT uType,
537 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
538 DWORD_PTR dwInstance, DWORD dwFlags)
540 HANDLE handle;
541 LPWINE_MLD wmld;
542 DWORD dwRet;
543 WAVEOPENDESC wod;
545 TRACE("(%p, %d, %s, %p, %08lX, %08lX, %08X);\n",
546 lphndl, (int)uDeviceID, (uType==MMDRV_WAVEOUT)?"Out":"In", lpFormat, dwCallback,
547 dwInstance, dwFlags);
549 if (dwFlags & WAVE_FORMAT_QUERY)
550 TRACE("WAVE_FORMAT_QUERY requested !\n");
552 dwRet = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
553 if (dwRet != MMSYSERR_NOERROR)
554 return dwRet;
556 if (lpFormat == NULL) {
557 WARN("bad format\n");
558 return WAVERR_BADFORMAT;
561 if ((dwFlags & WAVE_MAPPED) && (uDeviceID == (UINT)-1)) {
562 WARN("invalid parameter\n");
563 return MMSYSERR_INVALPARAM;
566 /* may have a PCMWAVEFORMAT rather than a WAVEFORMATEX so don't read cbSize */
567 TRACE("wFormatTag=%u, nChannels=%u, nSamplesPerSec=%u, nAvgBytesPerSec=%u, nBlockAlign=%u, wBitsPerSample=%u\n",
568 lpFormat->wFormatTag, lpFormat->nChannels, lpFormat->nSamplesPerSec,
569 lpFormat->nAvgBytesPerSec, lpFormat->nBlockAlign, lpFormat->wBitsPerSample);
571 if ((wmld = MMDRV_Alloc(sizeof(WINE_WAVE), uType, &handle,
572 &dwFlags, &dwCallback, &dwInstance)) == NULL) {
573 return MMSYSERR_NOMEM;
576 wod.hWave = handle;
577 wod.lpFormat = (LPWAVEFORMATEX)lpFormat; /* should the struct be copied iso pointer? */
578 wod.dwCallback = dwCallback;
579 wod.dwInstance = dwInstance;
580 wod.dnDevNode = 0L;
582 TRACE("cb=%08lx\n", wod.dwCallback);
584 for (;;) {
585 if (dwFlags & WAVE_MAPPED) {
586 wod.uMappedDeviceID = uDeviceID;
587 uDeviceID = WAVE_MAPPER;
588 } else {
589 wod.uMappedDeviceID = -1;
591 wmld->uDeviceID = uDeviceID;
593 dwRet = MMDRV_Open(wmld, (uType == MMDRV_WAVEOUT) ? WODM_OPEN : WIDM_OPEN,
594 (DWORD_PTR)&wod, dwFlags);
596 TRACE("dwRet = %s\n", WINMM_ErrorToString(dwRet));
597 if (dwRet != WAVERR_BADFORMAT ||
598 ((dwFlags & (WAVE_MAPPED|WAVE_FORMAT_DIRECT)) != 0) || (uDeviceID == WAVE_MAPPER)) break;
599 /* if we ask for a format which isn't supported by the physical driver,
600 * let's try to map it through the wave mapper (except, if we already tried
601 * or user didn't allow us to use acm codecs or the device is already the mapper)
603 dwFlags |= WAVE_MAPPED;
604 /* we shall loop only one */
607 if ((dwFlags & WAVE_FORMAT_QUERY) || dwRet != MMSYSERR_NOERROR) {
608 MMDRV_Free(handle, wmld);
609 handle = 0;
612 if (lphndl != NULL) *lphndl = handle;
613 TRACE("=> %s hWave=%p\n", WINMM_ErrorToString(dwRet), handle);
615 return dwRet;
618 static inline BOOL WINMM_IsMapper(UINT device)
620 return (device == WAVE_MAPPER || device == (UINT16)WAVE_MAPPER);
623 static MMRESULT WINMM_TryDeviceMapping(WINMM_OpenInfo *info, WORD channels,
624 DWORD freq, DWORD bits_per_samp, BOOL is_out)
626 WINMM_Device *device;
627 WAVEFORMATEX target;
628 MMRESULT mr;
629 UINT i;
631 TRACE("format: %u, channels: %u, sample rate: %u, bit depth: %u\n",
632 WAVE_FORMAT_PCM, channels, freq, bits_per_samp);
634 target.wFormatTag = WAVE_FORMAT_PCM;
635 target.nChannels = channels;
636 target.nSamplesPerSec = freq;
637 target.wBitsPerSample = bits_per_samp;
638 target.nBlockAlign = (target.nChannels * target.wBitsPerSample) / 8;
639 target.nAvgBytesPerSec = target.nSamplesPerSec * target.nBlockAlign;
640 target.cbSize = 0;
642 if(is_out)
643 mr = acmStreamOpen(NULL, NULL, info->format, &target, NULL, 0,
644 0, ACM_STREAMOPENF_QUERY);
645 else
646 return MMSYSERR_ERROR;
647 if(mr != MMSYSERR_NOERROR)
648 return mr;
650 /* ACM can convert from src->dst, so try to find a device
651 * that supports dst */
652 if(WINMM_IsMapper(info->req_device)){
653 for(i = 0; i < g_outmmdevices_count; ++i){
654 WINMM_OpenInfo l_info = *info;
655 l_info.req_device = i;
656 l_info.format = &target;
657 mr = WOD_Open(&l_info);
658 if(mr == MMSYSERR_NOERROR){
659 info->handle = l_info.handle;
660 break;
663 }else{
664 WINMM_OpenInfo l_info = *info;
665 l_info.flags &= ~WAVE_MAPPED;
666 l_info.format = &target;
667 mr = WOD_Open(&l_info);
668 if(mr == MMSYSERR_NOERROR)
669 info->handle = l_info.handle;
671 if(mr != MMSYSERR_NOERROR)
672 return WAVERR_BADFORMAT;
674 device = WINMM_GetDeviceFromHWAVE((HWAVE)info->handle);
675 if(!device)
676 return MMSYSERR_INVALHANDLE;
678 /* set up the ACM stream */
679 mr = acmStreamOpen(&device->acm_handle, NULL, info->format, &target,
680 NULL, 0, 0, 0);
681 if(mr != MMSYSERR_NOERROR){
682 WOD_Close((HWAVEOUT)info->handle);
683 return mr;
686 TRACE("Success\n");
687 return MMSYSERR_NOERROR;
690 static MMRESULT WINMM_MapDevice(WINMM_OpenInfo *info, BOOL is_out)
692 UINT i;
693 MMRESULT mr;
694 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)info->format;
696 TRACE("(%p, %d)\n", info, is_out);
698 /* try to find a direct match */
699 if(is_out){
700 WINMM_OpenInfo l_info = *info;
701 if(WINMM_IsMapper(info->req_device)){
702 for(i = 0; i < g_outmmdevices_count; ++i){
703 l_info.req_device = i;
704 mr = WOD_Open(&l_info);
705 if(mr == MMSYSERR_NOERROR){
706 info->handle = l_info.handle;
707 return mr;
710 }else{
711 l_info.flags &= ~WAVE_MAPPED;
712 mr = WOD_Open(&l_info);
713 if(mr == MMSYSERR_NOERROR){
714 info->handle = l_info.handle;
715 return mr;
718 }else
719 return MMSYSERR_ERROR;
721 /* no direct match, so set up the ACM stream */
722 if(info->format->wFormatTag != WAVE_FORMAT_PCM ||
723 (info->format->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
724 !IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
725 /* convert to PCM format if it's not already */
726 mr = WINMM_TryDeviceMapping(info, info->format->nChannels,
727 info->format->nSamplesPerSec, 16, is_out);
728 if(mr == MMSYSERR_NOERROR)
729 return mr;
731 mr = WINMM_TryDeviceMapping(info, info->format->nChannels,
732 info->format->nSamplesPerSec, 8, is_out);
733 if(mr == MMSYSERR_NOERROR)
734 return mr;
735 }else{
736 WORD channels;
738 /* first try just changing bit depth and channels */
739 channels = info->format->nChannels;
740 mr = WINMM_TryDeviceMapping(info, channels,
741 info->format->nSamplesPerSec, 16, is_out);
742 if(mr == MMSYSERR_NOERROR)
743 return mr;
744 mr = WINMM_TryDeviceMapping(info, channels,
745 info->format->nSamplesPerSec, 8, is_out);
746 if(mr == MMSYSERR_NOERROR)
747 return mr;
749 channels = (channels == 2) ? 1 : 2;
750 mr = WINMM_TryDeviceMapping(info, channels,
751 info->format->nSamplesPerSec, 16, is_out);
752 if(mr == MMSYSERR_NOERROR)
753 return mr;
754 mr = WINMM_TryDeviceMapping(info, channels,
755 info->format->nSamplesPerSec, 8, is_out);
756 if(mr == MMSYSERR_NOERROR)
757 return mr;
759 /* that didn't work, so now try different sample rates */
760 channels = info->format->nChannels;
761 mr = WINMM_TryDeviceMapping(info, channels, 96000, 16, is_out);
762 if(mr == MMSYSERR_NOERROR)
763 return mr;
764 mr = WINMM_TryDeviceMapping(info, channels, 48000, 16, is_out);
765 if(mr == MMSYSERR_NOERROR)
766 return mr;
767 mr = WINMM_TryDeviceMapping(info, channels, 44100, 16, is_out);
768 if(mr == MMSYSERR_NOERROR)
769 return mr;
770 mr = WINMM_TryDeviceMapping(info, channels, 22050, 16, is_out);
771 if(mr == MMSYSERR_NOERROR)
772 return mr;
773 mr = WINMM_TryDeviceMapping(info, channels, 11025, 16, is_out);
774 if(mr == MMSYSERR_NOERROR)
775 return mr;
777 channels = (channels == 2) ? 1 : 2;
778 mr = WINMM_TryDeviceMapping(info, channels, 96000, 16, is_out);
779 if(mr == MMSYSERR_NOERROR)
780 return mr;
781 mr = WINMM_TryDeviceMapping(info, channels, 48000, 16, is_out);
782 if(mr == MMSYSERR_NOERROR)
783 return mr;
784 mr = WINMM_TryDeviceMapping(info, channels, 44100, 16, is_out);
785 if(mr == MMSYSERR_NOERROR)
786 return mr;
787 mr = WINMM_TryDeviceMapping(info, channels, 22050, 16, is_out);
788 if(mr == MMSYSERR_NOERROR)
789 return mr;
790 mr = WINMM_TryDeviceMapping(info, channels, 11025, 16, is_out);
791 if(mr == MMSYSERR_NOERROR)
792 return mr;
794 channels = info->format->nChannels;
795 mr = WINMM_TryDeviceMapping(info, channels, 96000, 8, is_out);
796 if(mr == MMSYSERR_NOERROR)
797 return mr;
798 mr = WINMM_TryDeviceMapping(info, channels, 48000, 8, is_out);
799 if(mr == MMSYSERR_NOERROR)
800 return mr;
801 mr = WINMM_TryDeviceMapping(info, channels, 44100, 8, is_out);
802 if(mr == MMSYSERR_NOERROR)
803 return mr;
804 mr = WINMM_TryDeviceMapping(info, channels, 22050, 8, is_out);
805 if(mr == MMSYSERR_NOERROR)
806 return mr;
807 mr = WINMM_TryDeviceMapping(info, channels, 11025, 8, is_out);
808 if(mr == MMSYSERR_NOERROR)
809 return mr;
811 channels = (channels == 2) ? 1 : 2;
812 mr = WINMM_TryDeviceMapping(info, channels, 96000, 8, is_out);
813 if(mr == MMSYSERR_NOERROR)
814 return mr;
815 mr = WINMM_TryDeviceMapping(info, channels, 48000, 8, is_out);
816 if(mr == MMSYSERR_NOERROR)
817 return mr;
818 mr = WINMM_TryDeviceMapping(info, channels, 44100, 8, is_out);
819 if(mr == MMSYSERR_NOERROR)
820 return mr;
821 mr = WINMM_TryDeviceMapping(info, channels, 22050, 8, is_out);
822 if(mr == MMSYSERR_NOERROR)
823 return mr;
824 mr = WINMM_TryDeviceMapping(info, channels, 11025, 8, is_out);
825 if(mr == MMSYSERR_NOERROR)
826 return mr;
829 WARN("Unable to find compatible device!\n");
830 return WAVERR_BADFORMAT;
833 static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_MMDevice *mmdevice,
834 WINMM_OpenInfo *info)
836 WAVEFORMATEX *closer_fmt = NULL, *passed_fmt;
837 LRESULT ret = MMSYSERR_ERROR;
838 HRESULT hr;
840 hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id,
841 &device->device);
842 if(FAILED(hr)){
843 ERR("Device %s (%s) unavailable: %08x\n",
844 wine_dbgstr_w(mmdevice->dev_id),
845 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
846 goto error;
849 hr = IMMDevice_Activate(device->device, &IID_IAudioClient,
850 CLSCTX_INPROC_SERVER, NULL, (void**)&device->client);
851 if(FAILED(hr)){
852 ERR("Activate failed: %08x\n", hr);
853 goto error;
856 if(info->format->wFormatTag == WAVE_FORMAT_PCM){
857 /* we aren't guaranteed that the struct in lpFormat is a full
858 * WAVEFORMATEX struct, which IAC::IsFormatSupported requires */
859 passed_fmt = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX));
860 memcpy(passed_fmt, info->format, sizeof(PCMWAVEFORMAT));
861 passed_fmt->cbSize = 0;
862 }else
863 passed_fmt = info->format;
865 hr = IAudioClient_IsFormatSupported(device->client,
866 AUDCLNT_SHAREMODE_SHARED, passed_fmt, &closer_fmt);
867 if(closer_fmt)
868 CoTaskMemFree(closer_fmt);
869 if(FAILED(hr) && hr != AUDCLNT_E_UNSUPPORTED_FORMAT){
870 if(info->format->wFormatTag == WAVE_FORMAT_PCM)
871 HeapFree(GetProcessHeap(), 0, passed_fmt);
872 ERR("IsFormatSupported failed: %08x\n", hr);
873 goto error;
875 if(hr == S_FALSE || hr == AUDCLNT_E_UNSUPPORTED_FORMAT){
876 if(info->format->wFormatTag == WAVE_FORMAT_PCM)
877 HeapFree(GetProcessHeap(), 0, passed_fmt);
878 ret = WAVERR_BADFORMAT;
879 goto error;
881 if(info->flags & WAVE_FORMAT_QUERY){
882 if(info->format->wFormatTag == WAVE_FORMAT_PCM)
883 HeapFree(GetProcessHeap(), 0, passed_fmt);
884 ret = MMSYSERR_NOERROR;
885 goto error;
888 /* buffer size = 10 * 100000 (100 ns) = 0.1 seconds */
889 hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED,
890 AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
891 10 * 100000, 50000, passed_fmt, &device->parent->session);
892 if(info->format->wFormatTag == WAVE_FORMAT_PCM)
893 HeapFree(GetProcessHeap(), 0, passed_fmt);
894 if(FAILED(hr)){
895 ERR("Initialize failed: %08x\n", hr);
896 goto error;
899 hr = IAudioClient_GetService(device->client, &IID_IAudioClock,
900 (void**)&device->clock);
901 if(FAILED(hr)){
902 ERR("GetService failed: %08x\n", hr);
903 goto error;
906 if(!device->event){
907 device->event = CreateEventW(NULL, FALSE, FALSE, NULL);
908 if(!device->event){
909 ERR("CreateEvent failed: %08x\n", hr);
910 goto error;
913 if(g_device_handles){
914 g_device_handles = HeapReAlloc(GetProcessHeap(), 0, g_device_handles,
915 sizeof(HANDLE) * (g_devhandle_count + 1));
916 g_handle_devices = HeapReAlloc(GetProcessHeap(), 0, g_handle_devices,
917 sizeof(WINMM_Device *) * (g_devhandle_count + 1));
918 }else{
919 g_device_handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE));
920 g_handle_devices = HeapAlloc(GetProcessHeap(), 0,
921 sizeof(WINMM_Device *));
923 g_device_handles[g_devhandle_count] = device->event;
924 g_handle_devices[g_devhandle_count] = device;
925 ++g_devhandle_count;
928 hr = IAudioClient_SetEventHandle(device->client, device->event);
929 if(FAILED(hr)){
930 ERR("SetEventHandle failed: %08x\n", hr);
931 goto error;
934 device->bytes_per_frame = info->format->nBlockAlign;
935 device->samples_per_sec = info->format->nSamplesPerSec;
937 device->played_frames = 0;
938 device->last_clock_pos = 0;
939 device->ofs_bytes = 0;
940 device->loop_counter = 0;
941 device->stopped = TRUE;
942 device->first = device->last = device->playing = device->loop_start = NULL;
944 device->cb_info.flags = HIWORD(info->flags & CALLBACK_TYPEMASK);
945 device->cb_info.callback = info->callback;
946 device->cb_info.user = info->cb_user;
947 device->cb_info.hwave = (HWAVE)device->handle;
949 info->handle = device->handle;
951 return MMSYSERR_NOERROR;
953 error:
954 if(device->client){
955 IAudioClient_Release(device->client);
956 device->client = NULL;
958 if(device->device){
959 IMMDevice_Release(device->device);
960 device->device = NULL;
963 return ret;
966 static LRESULT WOD_Open(WINMM_OpenInfo *info)
968 WINMM_MMDevice *mmdevice;
969 WINMM_Device *device = NULL;
970 WINMM_CBInfo cb_info;
971 LRESULT ret = MMSYSERR_ERROR;
972 HRESULT hr;
974 TRACE("(%u, %p, %08x)\n", info->req_device, info, info->flags);
976 if(WINMM_IsMapper(info->req_device) || (info->flags & WAVE_MAPPED))
977 return WINMM_MapDevice(info, TRUE);
979 if(info->req_device >= g_outmmdevices_count)
980 return MMSYSERR_BADDEVICEID;
982 mmdevice = &g_out_mmdevices[info->req_device];
984 if(!mmdevice->out_caps.szPname[0])
985 return MMSYSERR_NOTENABLED;
987 device = WINMM_FindUnusedDevice(TRUE, info->req_device);
988 if(!device)
989 return MMSYSERR_ALLOCATED;
991 ret = WINMM_OpenDevice(device, mmdevice, info);
992 if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
993 goto error;
994 ret = MMSYSERR_ERROR;
996 hr = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
997 (void**)&device->render);
998 if(FAILED(hr)){
999 ERR("GetService failed: %08x\n", hr);
1000 goto error;
1003 hr = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
1004 (void**)&device->volume);
1005 if(FAILED(hr)){
1006 ERR("GetService failed: %08x\n", hr);
1007 goto error;
1010 memcpy(&cb_info, &device->cb_info, sizeof(cb_info));
1012 LeaveCriticalSection(&device->lock);
1014 WINMM_NotifyClient(&cb_info, WOM_OPEN, 0, 0);
1016 return MMSYSERR_NOERROR;
1018 error:
1019 if(device->device){
1020 IMMDevice_Release(device->device);
1021 device->device = NULL;
1023 if(device->client){
1024 IAudioClient_Release(device->client);
1025 device->client = NULL;
1027 if(device->render){
1028 IAudioRenderClient_Release(device->render);
1029 device->render = NULL;
1031 if(device->volume){
1032 IAudioStreamVolume_Release(device->volume);
1033 device->volume = NULL;
1035 if(device->clock){
1036 IAudioClock_Release(device->clock);
1037 device->clock = NULL;
1039 device->open = FALSE;
1040 LeaveCriticalSection(&device->lock);
1041 return ret;
1044 static HRESULT WINMM_CloseDevice(WINMM_Device *device)
1046 device->open = FALSE;
1048 if(!device->stopped){
1049 IAudioClient_Stop(device->client);
1050 device->stopped = TRUE;
1053 IMMDevice_Release(device->device);
1054 device->device = NULL;
1056 IAudioClient_Release(device->client);
1057 device->client = NULL;
1059 IAudioClock_Release(device->clock);
1060 device->clock = NULL;
1062 return S_OK;
1065 static LRESULT WOD_Close(HWAVEOUT hwave)
1067 WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1068 WINMM_CBInfo cb_info;
1070 TRACE("(%p)\n", hwave);
1072 if(!WINMM_ValidateAndLock(device))
1073 return MMSYSERR_INVALHANDLE;
1075 WINMM_CloseDevice(device);
1077 IAudioRenderClient_Release(device->render);
1078 device->render = NULL;
1080 IAudioStreamVolume_Release(device->volume);
1081 device->volume = NULL;
1083 memcpy(&cb_info, &device->cb_info, sizeof(cb_info));
1085 LeaveCriticalSection(&device->lock);
1087 WINMM_NotifyClient(&cb_info, WOM_CLOSE, 0, 0);
1089 return MMSYSERR_NOERROR;
1092 static LRESULT WINMM_PrepareHeader(HWAVE hwave, WAVEHDR *header)
1094 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1096 TRACE("(%p, %p)\n", hwave, header);
1098 if(!WINMM_ValidateAndLock(device))
1099 return MMSYSERR_INVALHANDLE;
1101 if(device->render && device->acm_handle){
1102 ACMSTREAMHEADER *ash;
1103 DWORD size;
1104 MMRESULT mr;
1106 mr = acmStreamSize(device->acm_handle, header->dwBufferLength, &size,
1107 ACM_STREAMSIZEF_SOURCE);
1108 if(mr != MMSYSERR_NOERROR){
1109 LeaveCriticalSection(&device->lock);
1110 return mr;
1113 ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + size);
1114 if(!ash){
1115 LeaveCriticalSection(&device->lock);
1116 return MMSYSERR_NOMEM;
1119 ash->cbStruct = sizeof(*ash);
1120 ash->fdwStatus = 0;
1121 ash->dwUser = (DWORD_PTR)header;
1122 ash->pbSrc = (BYTE*)header->lpData;
1123 ash->cbSrcLength = header->dwBufferLength;
1124 ash->dwSrcUser = header->dwUser;
1125 ash->pbDst = (BYTE*)ash + sizeof(ACMSTREAMHEADER);
1126 ash->cbDstLength = size;
1127 ash->dwDstUser = 0;
1129 mr = acmStreamPrepareHeader(device->acm_handle, ash, 0);
1130 if(mr != MMSYSERR_NOERROR){
1131 LeaveCriticalSection(&device->lock);
1132 return mr;
1135 header->reserved = (DWORD_PTR)ash;
1138 LeaveCriticalSection(&device->lock);
1140 header->dwFlags |= WHDR_PREPARED;
1141 header->dwFlags &= ~WHDR_DONE;
1143 return MMSYSERR_NOERROR;
1146 static LRESULT WINMM_UnprepareHeader(HWAVE hwave, WAVEHDR *header)
1148 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1150 TRACE("(%p, %p)\n", hwave, header);
1152 if(!WINMM_ValidateAndLock(device))
1153 return MMSYSERR_INVALHANDLE;
1155 if(device->render && device->acm_handle){
1156 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1158 acmStreamUnprepareHeader(device->acm_handle, ash, 0);
1160 HeapFree(GetProcessHeap(), 0, ash);
1163 LeaveCriticalSection(&device->lock);
1165 header->dwFlags &= ~WHDR_PREPARED;
1166 header->dwFlags |= WHDR_DONE;
1168 return MMSYSERR_NOERROR;
1171 static UINT32 WINMM_HeaderLenBytes(WINMM_Device *device, WAVEHDR *header)
1173 if(device->acm_handle){
1174 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1175 return ash->cbDstLengthUsed;
1178 return header->dwBufferLength;
1181 static UINT32 WINMM_HeaderLenFrames(WINMM_Device *device, WAVEHDR *header)
1183 return WINMM_HeaderLenBytes(device, header) / device->bytes_per_frame;
1186 static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device)
1188 HRESULT hr;
1189 WAVEHDR *queue, *first = device->first;
1190 UINT64 clock_freq, clock_pos, clock_frames;
1191 UINT32 nloops, queue_frames;
1193 hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
1194 if(FAILED(hr)){
1195 ERR("GetFrequency failed: %08x\n", hr);
1196 return first;
1199 hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
1200 if(FAILED(hr)){
1201 ERR("GetPosition failed: %08x\n", hr);
1202 return first;
1205 clock_frames = (clock_pos / (double)clock_freq) * device->samples_per_sec;
1207 first = queue = device->first;
1208 nloops = device->loop_counter;
1209 while(queue &&
1210 (queue_frames = WINMM_HeaderLenFrames(device, queue)) <=
1211 clock_frames - device->last_clock_pos){
1212 WAVEHDR *next = queue->lpNext;
1213 if(!nloops){
1214 device->first->dwFlags &= ~WHDR_INQUEUE;
1215 device->first->dwFlags |= WHDR_DONE;
1216 device->last_clock_pos += queue_frames;
1217 queue = device->first = next;
1218 }else{
1219 if(queue->dwFlags & WHDR_BEGINLOOP)
1220 queue = next;
1222 if(queue->dwFlags & WHDR_ENDLOOP){
1223 queue = device->loop_start;
1224 --nloops;
1229 return first;
1232 static void WOD_PushData(WINMM_Device *device)
1234 WINMM_CBInfo cb_info;
1235 HRESULT hr;
1236 UINT32 pad, bufsize, avail_frames, queue_frames, written, ofs;
1237 UINT32 queue_bytes, nloops;
1238 BYTE *data;
1239 WAVEHDR *queue, *first = NULL;
1241 TRACE("(%p)\n", device->handle);
1243 EnterCriticalSection(&device->lock);
1245 if(!device->device)
1246 goto exit;
1248 if(!device->first){
1249 device->stopped = TRUE;
1250 device->last_clock_pos = 0;
1251 IAudioClient_Stop(device->client);
1252 IAudioClient_Reset(device->client);
1253 goto exit;
1256 hr = IAudioClient_GetBufferSize(device->client, &bufsize);
1257 if(FAILED(hr)){
1258 ERR("GetBufferSize failed: %08x\n", hr);
1259 goto exit;
1262 hr = IAudioClient_GetCurrentPadding(device->client, &pad);
1263 if(FAILED(hr)){
1264 ERR("GetCurrentPadding failed: %08x\n", hr);
1265 goto exit;
1268 first = WOD_MarkDoneHeaders(device);
1270 /* determine which is larger between the available buffer size and
1271 * the amount of data left in the queue */
1272 avail_frames = bufsize - pad;
1274 queue = device->playing;
1275 ofs = device->ofs_bytes;
1276 queue_frames = 0;
1277 nloops = 0;
1278 while(queue && queue_frames < avail_frames){
1279 queue_bytes = WINMM_HeaderLenBytes(device, queue);
1280 queue_frames = (queue_bytes - ofs) / device->bytes_per_frame;
1282 ofs = 0;
1284 if(queue->dwFlags & WHDR_ENDLOOP && nloops < device->loop_counter){
1285 queue = device->loop_start;
1286 ++nloops;
1287 }else
1288 queue = queue->lpNext;
1291 if(avail_frames != 0 && queue_frames == 0){
1292 hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
1293 if(FAILED(hr)){
1294 ERR("GetBuffer failed: %08x\n", hr);
1295 goto exit;
1298 hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames,
1299 AUDCLNT_BUFFERFLAGS_SILENT);
1300 if(FAILED(hr)){
1301 ERR("ReleaseBuffer failed: %08x\n", hr);
1302 goto exit;
1305 goto exit;
1308 if(queue_frames < avail_frames)
1309 avail_frames = queue_frames;
1310 if(avail_frames == 0)
1311 goto exit;
1313 hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
1314 if(FAILED(hr)){
1315 ERR("GetBuffer failed: %08x\n", hr);
1316 goto exit;
1319 written = 0;
1320 while(device->playing && written < avail_frames){
1321 UINT32 copy_frames, copy_bytes;
1322 BYTE *queue_data;
1324 queue = device->playing;
1326 if(device->acm_handle){
1327 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)queue->reserved;
1328 queue_bytes = ash->cbDstLengthUsed;
1329 queue_data = ash->pbDst;
1330 }else{
1331 queue_bytes = queue->dwBufferLength;
1332 queue_data = (BYTE*)queue->lpData;
1335 queue_frames = (queue_bytes - device->ofs_bytes) /
1336 device->bytes_per_frame;
1338 copy_frames = queue_frames < (avail_frames - written) ?
1339 queue_frames : avail_frames - written;
1340 copy_bytes = copy_frames * device->bytes_per_frame;
1342 memcpy(data, queue_data + device->ofs_bytes, copy_bytes);
1344 data += copy_bytes;
1345 written += copy_frames;
1346 device->ofs_bytes += copy_bytes;
1348 if(device->ofs_bytes >= queue_bytes){
1349 device->ofs_bytes = 0;
1351 if(!(queue->dwFlags & (WHDR_BEGINLOOP | WHDR_ENDLOOP)))
1352 device->playing = queue->lpNext;
1353 else{
1354 if(queue->dwFlags & WHDR_BEGINLOOP){
1355 device->loop_start = device->playing;
1356 device->playing = queue->lpNext;
1357 device->loop_counter = queue->dwLoops;
1359 if(queue->dwFlags & WHDR_ENDLOOP){
1360 --device->loop_counter;
1361 if(device->loop_counter)
1362 device->playing = device->loop_start;
1363 else
1364 device->loop_start = device->playing = queue->lpNext;
1370 hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames, 0);
1371 if(FAILED(hr)){
1372 ERR("ReleaseBuffer failed: %08x\n", hr);
1373 goto exit;
1376 device->played_frames += avail_frames;
1378 exit:
1379 memcpy(&cb_info, &device->cb_info, sizeof(cb_info));
1381 LeaveCriticalSection(&device->lock);
1383 while(first && (first->dwFlags & WHDR_DONE)){
1384 WAVEHDR *next = first->lpNext;
1385 WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1386 first = next;
1390 static HRESULT WINMM_BeginPlaying(WINMM_Device *device)
1392 HRESULT hr;
1394 TRACE("(%p)\n", device->handle);
1396 EnterCriticalSection(&device->lock);
1398 if(device->render)
1399 /* prebuffer data before starting */
1400 WOD_PushData(device);
1402 if(device->stopped){
1403 device->stopped = FALSE;
1405 hr = IAudioClient_Start(device->client);
1406 if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){
1407 device->stopped = TRUE;
1408 LeaveCriticalSection(&device->lock);
1409 ERR("Start failed: %08x\n", hr);
1410 return hr;
1414 LeaveCriticalSection(&device->lock);
1416 return S_OK;
1419 static LRESULT WINMM_Pause(HWAVE hwave)
1421 WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1422 HRESULT hr;
1424 TRACE("(%p)\n", hwave);
1426 if(!WINMM_ValidateAndLock(device))
1427 return MMSYSERR_INVALHANDLE;
1429 hr = IAudioClient_Stop(device->client);
1430 if(FAILED(hr)){
1431 LeaveCriticalSection(&device->lock);
1432 ERR("Stop failed: %08x\n", hr);
1433 return MMSYSERR_ERROR;
1436 device->stopped = FALSE;
1438 LeaveCriticalSection(&device->lock);
1440 return MMSYSERR_NOERROR;
1443 static LRESULT WINMM_Reset(HWAVE hwave)
1445 WINMM_CBInfo cb_info;
1446 WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1447 WAVEHDR *first;
1448 MMRESULT mr;
1450 TRACE("(%p)\n", hwave);
1452 if(!WINMM_ValidateAndLock(device))
1453 return MMSYSERR_INVALHANDLE;
1455 mr = WINMM_Pause(hwave);
1456 if(mr != MMSYSERR_NOERROR){
1457 LeaveCriticalSection(&device->lock);
1458 return mr;
1460 device->stopped = TRUE;
1462 first = WOD_MarkDoneHeaders(device);
1463 device->first = device->last = device->playing = NULL;
1464 device->ofs_bytes = 0;
1465 device->played_frames = 0;
1466 device->loop_counter = 0;
1467 device->last_clock_pos = 0;
1469 memcpy(&cb_info, &device->cb_info, sizeof(cb_info));
1471 LeaveCriticalSection(&device->lock);
1473 while(first){
1474 WAVEHDR *next = first->lpNext;
1475 first->dwFlags &= ~WHDR_INQUEUE;
1476 first->dwFlags |= WHDR_DONE;
1477 WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1478 first = next;
1481 return MMSYSERR_NOERROR;
1484 static MMRESULT WINMM_FramesToMMTime(MMTIME *time, UINT32 played_frames,
1485 UINT32 sample_rate, UINT32 bytes_per_frame)
1487 switch(time->wType){
1488 case TIME_SAMPLES:
1489 time->u.sample = played_frames;
1490 return MMSYSERR_NOERROR;
1491 case TIME_MS:
1492 time->u.ms = (DWORD)((played_frames / (double)sample_rate) * 1000);
1493 return MMSYSERR_NOERROR;
1494 case TIME_SMPTE:
1495 time->u.smpte.fps = 30;
1496 if(played_frames >= sample_rate){
1497 time->u.smpte.sec = played_frames / (double)sample_rate;
1498 time->u.smpte.min = time->u.smpte.sec / 60;
1499 time->u.smpte.hour = time->u.smpte.min / 60;
1500 time->u.smpte.sec %= 60;
1501 time->u.smpte.min %= 60;
1502 played_frames %= sample_rate;
1503 }else{
1504 time->u.smpte.sec = 0;
1505 time->u.smpte.min = 0;
1506 time->u.smpte.hour = 0;
1508 time->u.smpte.frame = (played_frames / (double)sample_rate) * 30;
1509 return MMSYSERR_NOERROR;
1510 case TIME_BYTES:
1511 default:
1512 time->wType = TIME_BYTES;
1513 time->u.cb = played_frames * bytes_per_frame;
1514 return MMSYSERR_NOERROR;
1517 return MMSYSERR_ERROR;
1520 static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time)
1522 WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1523 UINT32 played_frames, sample_rate, bytes_per_frame;
1525 TRACE("(%p, %p)\n", hwave, time);
1527 if(!WINMM_ValidateAndLock(device))
1528 return MMSYSERR_INVALHANDLE;
1530 played_frames = device->played_frames;
1531 sample_rate = device->samples_per_sec;
1532 bytes_per_frame = device->bytes_per_frame;
1534 LeaveCriticalSection(&device->lock);
1536 return WINMM_FramesToMMTime(time, played_frames, sample_rate,
1537 bytes_per_frame);
1540 static LRESULT CALLBACK WINMM_DevicesMsgProc(HWND hwnd, UINT msg, WPARAM wparam,
1541 LPARAM lparam)
1543 switch(msg){
1544 case WODM_OPEN:
1545 return WOD_Open((WINMM_OpenInfo*)wparam);
1546 case WODM_CLOSE:
1547 return WOD_Close((HWAVEOUT)wparam);
1549 return DefWindowProcW(hwnd, msg, wparam, lparam);
1552 static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
1554 HANDLE evt = arg;
1555 HRESULT hr;
1556 static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0};
1558 hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
1559 if(FAILED(hr)){
1560 ERR("CoInitializeEx failed: %08x\n", hr);
1561 return 1;
1564 hr = WINMM_InitMMDevices();
1565 if(FAILED(hr)){
1566 CoUninitialize();
1567 return 1;
1570 g_devices_hwnd = CreateWindowW(messageW, NULL, 0, 0, 0, 0, 0,
1571 HWND_MESSAGE, NULL, NULL, NULL);
1572 if(!g_devices_hwnd){
1573 ERR("CreateWindow failed: %d\n", GetLastError());
1574 CoUninitialize();
1575 return 1;
1578 SetWindowLongPtrW(g_devices_hwnd, GWLP_WNDPROC,
1579 (LONG_PTR)WINMM_DevicesMsgProc);
1581 /* inform caller that the thread is ready to process messages */
1582 SetEvent(evt);
1583 evt = NULL; /* do not use after this point */
1585 while(1){
1586 DWORD wait;
1587 wait = MsgWaitForMultipleObjects(g_devhandle_count, g_device_handles,
1588 FALSE, INFINITE, QS_ALLINPUT);
1589 if(wait == g_devhandle_count - WAIT_OBJECT_0){
1590 MSG msg;
1591 if(PeekMessageW(&msg, g_devices_hwnd, 0, 0, PM_REMOVE))
1592 ERR("Unexpected message: 0x%x\n", msg.message);
1593 }else if(wait < g_devhandle_count){
1594 WINMM_Device *device = g_handle_devices[wait - WAIT_OBJECT_0];
1595 WOD_PushData(device);
1596 }else
1597 ERR("Unexpected MsgWait result 0x%x, GLE: %d\n", wait,
1598 GetLastError());
1601 DestroyWindow(g_devices_hwnd);
1603 CoUninitialize();
1605 return 0;
1608 static BOOL WINMM_StartDevicesThread(void)
1610 HANDLE events[2];
1611 DWORD wait;
1613 EnterCriticalSection(&g_devthread_lock);
1615 if(g_devices_thread){
1616 DWORD wait;
1618 wait = WaitForSingleObject(g_devices_thread, 0);
1619 if(wait == WAIT_TIMEOUT){
1620 LeaveCriticalSection(&g_devthread_lock);
1621 return TRUE;
1623 if(wait != WAIT_OBJECT_0){
1624 LeaveCriticalSection(&g_devthread_lock);
1625 return FALSE;
1628 g_devices_thread = NULL;
1629 g_devices_hwnd = NULL;
1632 TRACE("Starting up devices thread\n");
1634 events[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
1636 g_devices_thread = CreateThread(NULL, 0, WINMM_DevicesThreadProc,
1637 events[0], 0, NULL);
1638 if(!g_devices_thread){
1639 LeaveCriticalSection(&g_devthread_lock);
1640 CloseHandle(events[0]);
1641 return FALSE;
1644 events[1] = g_devices_thread;
1645 wait = WaitForMultipleObjects(2, events, FALSE, INFINITE);
1646 CloseHandle(events[0]);
1647 if(wait != WAIT_OBJECT_0){
1648 if(wait == 1 - WAIT_OBJECT_0){
1649 CloseHandle(g_devices_thread);
1650 g_devices_thread = NULL;
1651 g_devices_hwnd = NULL;
1653 LeaveCriticalSection(&g_devthread_lock);
1654 return FALSE;
1657 LeaveCriticalSection(&g_devthread_lock);
1659 return TRUE;
1662 /**************************************************************************
1663 * waveOutGetNumDevs [WINMM.@]
1665 UINT WINAPI waveOutGetNumDevs(void)
1667 if(!WINMM_StartDevicesThread())
1668 return 0;
1670 TRACE("count: %u\n", g_outmmdevices_count);
1672 return g_outmmdevices_count;
1675 /**************************************************************************
1676 * waveOutGetDevCapsA [WINMM.@]
1678 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
1679 UINT uSize)
1681 WAVEOUTCAPSW wocW;
1682 UINT ret;
1684 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
1686 if(!WINMM_StartDevicesThread())
1687 return MMSYSERR_ERROR;
1689 if(!lpCaps)
1690 return MMSYSERR_INVALPARAM;
1692 ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
1694 if (ret == MMSYSERR_NOERROR) {
1695 WAVEOUTCAPSA wocA;
1696 wocA.wMid = wocW.wMid;
1697 wocA.wPid = wocW.wPid;
1698 wocA.vDriverVersion = wocW.vDriverVersion;
1699 WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,
1700 sizeof(wocA.szPname), NULL, NULL );
1701 wocA.dwFormats = wocW.dwFormats;
1702 wocA.wChannels = wocW.wChannels;
1703 wocA.dwSupport = wocW.dwSupport;
1704 memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));
1706 return ret;
1709 /**************************************************************************
1710 * waveOutGetDevCapsW [WINMM.@]
1712 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
1713 UINT uSize)
1715 WAVEOUTCAPSW mapper_caps, *caps;
1717 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
1719 if(!WINMM_StartDevicesThread())
1720 return MMSYSERR_ERROR;
1722 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
1724 if(WINMM_IsMapper(uDeviceID)){
1725 /* FIXME: Should be localized */
1726 static const WCHAR mapper_pnameW[] = {'W','i','n','e',' ','S','o','u',
1727 'n','d',' ','M','a','p','p','e','r',0};
1729 mapper_caps.wMid = 0xFF;
1730 mapper_caps.wPid = 0xFF;
1731 mapper_caps.vDriverVersion = 0x00010001;
1732 mapper_caps.dwFormats = 0xFFFFFFFF;
1733 mapper_caps.wReserved1 = 0;
1734 mapper_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
1735 WAVECAPS_SAMPLEACCURATE;
1736 mapper_caps.wChannels = 2;
1737 lstrcpyW(mapper_caps.szPname, mapper_pnameW);
1739 caps = &mapper_caps;
1740 }else{
1741 if(uDeviceID >= g_outmmdevices_count)
1742 return MMSYSERR_BADDEVICEID;
1744 caps = &g_out_mmdevices[uDeviceID].out_caps;
1747 memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
1749 return MMSYSERR_NOERROR;
1752 /**************************************************************************
1753 * waveOutGetErrorTextA [WINMM.@]
1754 * waveInGetErrorTextA [WINMM.@]
1756 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
1758 UINT ret;
1760 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
1761 else if (uSize == 0) ret = MMSYSERR_NOERROR;
1762 else
1764 LPWSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
1765 if (!xstr) ret = MMSYSERR_NOMEM;
1766 else
1768 ret = waveOutGetErrorTextW(uError, xstr, uSize);
1769 if (ret == MMSYSERR_NOERROR)
1770 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
1771 HeapFree(GetProcessHeap(), 0, xstr);
1774 return ret;
1777 /**************************************************************************
1778 * waveOutGetErrorTextW [WINMM.@]
1779 * waveInGetErrorTextW [WINMM.@]
1781 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
1783 UINT ret = MMSYSERR_BADERRNUM;
1785 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
1786 else if (uSize == 0) ret = MMSYSERR_NOERROR;
1787 else if (
1788 /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
1789 * a warning for the test was always true */
1790 (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
1791 (uError >= WAVERR_BASE && uError <= WAVERR_LASTERROR)) {
1792 if (LoadStringW(hWinMM32Instance,
1793 uError, lpText, uSize) > 0) {
1794 ret = MMSYSERR_NOERROR;
1797 return ret;
1800 /**************************************************************************
1801 * waveOutOpen [WINMM.@]
1802 * All the args/structs have the same layout as the win16 equivalents
1804 MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
1805 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
1806 DWORD_PTR dwInstance, DWORD dwFlags)
1808 LRESULT res;
1809 HRESULT hr;
1810 WINMM_OpenInfo info;
1812 TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut, uDeviceID, lpFormat,
1813 dwCallback, dwInstance, dwFlags);
1815 if(!WINMM_StartDevicesThread())
1816 return MMSYSERR_ERROR;
1818 if(!lphWaveOut && !(dwFlags & WAVE_FORMAT_QUERY))
1819 return MMSYSERR_INVALPARAM;
1821 hr = WINMM_StartDevicesThread();
1822 if(FAILED(hr)){
1823 ERR("Couldn't start the device thread: %08x\n", hr);
1824 return MMSYSERR_ERROR;
1827 info.format = (WAVEFORMATEX*)lpFormat;
1828 info.callback = dwCallback;
1829 info.cb_user = dwInstance;
1830 info.req_device = uDeviceID;
1831 info.flags = dwFlags;
1833 res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0);
1834 if(res != MMSYSERR_NOERROR)
1835 return res;
1837 if(lphWaveOut)
1838 *lphWaveOut = (HWAVEOUT)info.handle;
1840 return res;
1843 /**************************************************************************
1844 * waveOutClose [WINMM.@]
1846 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
1848 TRACE("(%p)\n", hWaveOut);
1850 if(!WINMM_StartDevicesThread())
1851 return MMSYSERR_ERROR;
1853 return SendMessageW(g_devices_hwnd, WODM_CLOSE, (WPARAM)hWaveOut, 0);
1856 /**************************************************************************
1857 * waveOutPrepareHeader [WINMM.@]
1859 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
1860 WAVEHDR* lpWaveOutHdr, UINT uSize)
1862 TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
1864 if(!WINMM_StartDevicesThread())
1865 return MMSYSERR_ERROR;
1867 if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
1868 return MMSYSERR_INVALPARAM;
1870 if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
1871 return WAVERR_STILLPLAYING;
1873 return WINMM_PrepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
1876 /**************************************************************************
1877 * waveOutUnprepareHeader [WINMM.@]
1879 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
1880 LPWAVEHDR lpWaveOutHdr, UINT uSize)
1882 TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
1884 if(!WINMM_StartDevicesThread())
1885 return MMSYSERR_ERROR;
1887 if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
1888 return MMSYSERR_INVALPARAM;
1890 if(!(lpWaveOutHdr->dwFlags & WHDR_PREPARED))
1891 return MMSYSERR_NOERROR;
1893 if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
1894 return WAVERR_STILLPLAYING;
1896 return WINMM_UnprepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
1899 /**************************************************************************
1900 * waveOutWrite [WINMM.@]
1902 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, WAVEHDR *header, UINT uSize)
1904 WINMM_Device *device;
1905 HRESULT hr;
1907 TRACE("(%p, %p, %u)\n", hWaveOut, header, uSize);
1909 if(!WINMM_StartDevicesThread())
1910 return MMSYSERR_ERROR;
1912 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
1914 if(!WINMM_ValidateAndLock(device))
1915 return MMSYSERR_INVALHANDLE;
1917 if(!header->lpData || !(header->dwFlags & WHDR_PREPARED)){
1918 LeaveCriticalSection(&device->lock);
1919 return WAVERR_UNPREPARED;
1922 if(header->dwFlags & WHDR_INQUEUE){
1923 LeaveCriticalSection(&device->lock);
1924 return WAVERR_STILLPLAYING;
1927 if(device->acm_handle){
1928 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1929 MMRESULT mr;
1931 ash->cbSrcLength = header->dwBufferLength;
1932 mr = acmStreamConvert(device->acm_handle, ash, 0);
1933 if(mr != MMSYSERR_NOERROR){
1934 LeaveCriticalSection(&device->lock);
1935 return mr;
1939 if(device->first){
1940 device->last->lpNext = header;
1941 device->last = header;
1942 if(!device->playing)
1943 device->playing = header;
1944 }else
1945 device->playing = device->first = device->last = header;
1947 header->lpNext = NULL;
1948 header->dwFlags &= ~WHDR_DONE;
1949 header->dwFlags |= WHDR_INQUEUE;
1951 hr = WINMM_BeginPlaying(device);
1952 if(FAILED(hr)){
1953 LeaveCriticalSection(&device->lock);
1954 return MMSYSERR_ERROR;
1957 LeaveCriticalSection(&device->lock);
1959 return MMSYSERR_NOERROR;
1962 /**************************************************************************
1963 * waveOutBreakLoop [WINMM.@]
1965 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
1967 WINMM_Device *device;
1969 TRACE("(%p)\n", hWaveOut);
1971 if(!WINMM_StartDevicesThread())
1972 return MMSYSERR_ERROR;
1974 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
1976 if(!WINMM_ValidateAndLock(device))
1977 return MMSYSERR_INVALHANDLE;
1979 device->loop_counter = 0;
1981 LeaveCriticalSection(&device->lock);
1983 return MMSYSERR_NOERROR;
1986 /**************************************************************************
1987 * waveOutPause [WINMM.@]
1989 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
1991 TRACE("(%p)\n", hWaveOut);
1993 if(!WINMM_StartDevicesThread())
1994 return MMSYSERR_ERROR;
1996 return WINMM_Pause((HWAVE)hWaveOut);
1999 /**************************************************************************
2000 * waveOutReset [WINMM.@]
2002 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2004 TRACE("(%p)\n", hWaveOut);
2006 if(!WINMM_StartDevicesThread())
2007 return MMSYSERR_ERROR;
2009 return WINMM_Reset((HWAVE)hWaveOut);
2012 /**************************************************************************
2013 * waveOutRestart [WINMM.@]
2015 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2017 WINMM_Device *device;
2018 HRESULT hr;
2020 TRACE("(%p)\n", hWaveOut);
2022 if(!WINMM_StartDevicesThread())
2023 return MMSYSERR_ERROR;
2025 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2027 if(!WINMM_ValidateAndLock(device))
2028 return MMSYSERR_INVALHANDLE;
2030 device->stopped = TRUE;
2032 hr = WINMM_BeginPlaying(device);
2033 if(FAILED(hr)){
2034 LeaveCriticalSection(&device->lock);
2035 return MMSYSERR_ERROR;
2038 LeaveCriticalSection(&device->lock);
2040 return MMSYSERR_NOERROR;
2043 /**************************************************************************
2044 * waveOutGetPosition [WINMM.@]
2046 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2047 UINT uSize)
2049 TRACE("(%p, %p, %u)\n", hWaveOut, lpTime, uSize);
2051 if(!WINMM_StartDevicesThread())
2052 return MMSYSERR_ERROR;
2054 if(!uSize || !lpTime || uSize != sizeof(MMTIME))
2055 return MMSYSERR_INVALPARAM;
2057 return WINMM_GetPosition((HWAVE)hWaveOut, lpTime);
2060 /**************************************************************************
2061 * waveOutGetPitch [WINMM.@]
2063 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2065 TRACE("(%p, %p)\n", hWaveOut, lpdw);
2066 return MMSYSERR_NOTSUPPORTED;
2069 /**************************************************************************
2070 * waveOutSetPitch [WINMM.@]
2072 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
2074 TRACE("(%p, %08x)\n", hWaveOut, dw);
2076 return MMSYSERR_NOTSUPPORTED;
2079 /**************************************************************************
2080 * waveOutGetPlaybackRate [WINMM.@]
2082 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
2084 TRACE("(%p, %p)\n", hWaveOut, lpdw);
2086 return MMSYSERR_NOTSUPPORTED;
2089 /**************************************************************************
2090 * waveOutSetPlaybackRate [WINMM.@]
2092 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
2094 TRACE("(%p, %08x)\n", hWaveOut, dw);
2096 return MMSYSERR_NOTSUPPORTED;
2099 /**************************************************************************
2100 * waveOutGetVolume [WINMM.@]
2102 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, DWORD *out)
2104 WINMM_Device *device;
2105 UINT32 channels;
2106 float *vols;
2107 HRESULT hr;
2109 TRACE("(%p, %p)\n", hWaveOut, out);
2111 if(!WINMM_StartDevicesThread())
2112 return MMSYSERR_ERROR;
2114 if(!out)
2115 return MMSYSERR_INVALPARAM;
2117 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2119 if(!WINMM_ValidateAndLock(device))
2120 return MMSYSERR_INVALHANDLE;
2122 hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2123 if(FAILED(hr)){
2124 LeaveCriticalSection(&device->lock);
2125 ERR("GetChannelCount failed: %08x\n", hr);
2126 return MMSYSERR_ERROR;
2129 vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2130 if(!vols){
2131 LeaveCriticalSection(&device->lock);
2132 return MMSYSERR_NOMEM;
2135 hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2136 if(FAILED(hr)){
2137 LeaveCriticalSection(&device->lock);
2138 HeapFree(GetProcessHeap(), 0, vols);
2139 ERR("GetAllVolumes failed: %08x\n", hr);
2140 return MMSYSERR_ERROR;
2143 LeaveCriticalSection(&device->lock);
2145 *out = ((UINT16)(vols[0] * (DWORD)0xFFFF));
2146 if(channels > 1)
2147 *out |= ((UINT16)(vols[1] * (DWORD)0xFFFF)) << 16;
2149 HeapFree(GetProcessHeap(), 0, vols);
2151 return MMSYSERR_NOERROR;
2154 /**************************************************************************
2155 * waveOutSetVolume [WINMM.@]
2157 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD in)
2159 WINMM_Device *device;
2160 UINT32 channels;
2161 float *vols;
2162 HRESULT hr;
2164 TRACE("(%p, %08x)\n", hWaveOut, in);
2166 if(!WINMM_StartDevicesThread())
2167 return MMSYSERR_ERROR;
2169 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2171 if(!WINMM_ValidateAndLock(device))
2172 return MMSYSERR_INVALHANDLE;
2174 hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
2175 if(FAILED(hr)){
2176 LeaveCriticalSection(&device->lock);
2177 ERR("GetChannelCount failed: %08x\n", hr);
2178 return MMSYSERR_ERROR;
2181 vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
2182 if(!vols){
2183 LeaveCriticalSection(&device->lock);
2184 return MMSYSERR_NOMEM;
2187 hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
2188 if(FAILED(hr)){
2189 LeaveCriticalSection(&device->lock);
2190 HeapFree(GetProcessHeap(), 0, vols);
2191 ERR("GetAllVolumes failed: %08x\n", hr);
2192 return MMSYSERR_ERROR;
2195 vols[0] = (float)((DWORD)(in & 0xFFFF) / (float)0xFFFF);
2196 if(channels > 1)
2197 vols[1] = (float)((DWORD)(in >> 16) / (float)0xFFFF);
2199 hr = IAudioStreamVolume_SetAllVolumes(device->volume, channels, vols);
2200 if(FAILED(hr)){
2201 LeaveCriticalSection(&device->lock);
2202 HeapFree(GetProcessHeap(), 0, vols);
2203 ERR("SetAllVolumes failed: %08x\n", hr);
2204 return MMSYSERR_ERROR;
2207 LeaveCriticalSection(&device->lock);
2209 HeapFree(GetProcessHeap(), 0, vols);
2211 return MMSYSERR_NOERROR;
2214 /**************************************************************************
2215 * waveOutGetID [WINMM.@]
2217 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
2219 WINMM_Device *device;
2220 UINT dev, junk;
2221 BOOL is_out;
2223 TRACE("(%p, %p)\n", hWaveOut, lpuDeviceID);
2225 if(!WINMM_StartDevicesThread())
2226 return MMSYSERR_ERROR;
2228 if(!lpuDeviceID)
2229 return MMSYSERR_INVALPARAM;
2231 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2232 if(!WINMM_ValidateAndLock(device))
2233 return MMSYSERR_INVALHANDLE;
2235 LeaveCriticalSection(&device->lock);
2237 WINMM_DecomposeHWAVE((HWAVE)hWaveOut, lpuDeviceID, &is_out, &dev, &junk);
2239 return MMSYSERR_NOERROR;
2242 static UINT WINMM_QueryInstanceIDSize(UINT device, DWORD_PTR *len, BOOL is_out)
2244 UINT count;
2245 WINMM_MMDevice *devices;
2247 TRACE("(%u, %p, %d)\n", device, len, is_out);
2249 if(is_out){
2250 count = g_outmmdevices_count;
2251 devices = g_out_mmdevices;
2252 }else{
2253 count = g_inmmdevices_count;
2254 devices = g_in_mmdevices;
2257 if(device >= count)
2258 return MMSYSERR_INVALHANDLE;
2260 *len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2262 return MMSYSERR_NOERROR;
2265 static UINT WINMM_QueryInstanceID(UINT device, WCHAR *str, DWORD_PTR len,
2266 BOOL is_out)
2268 UINT count, id_len;
2269 WINMM_MMDevice *devices;
2271 TRACE("(%u, %p, %d)\n", device, str, is_out);
2273 if(is_out){
2274 count = g_outmmdevices_count;
2275 devices = g_out_mmdevices;
2276 }else{
2277 count = g_inmmdevices_count;
2278 devices = g_in_mmdevices;
2281 if(device >= count)
2282 return MMSYSERR_INVALHANDLE;
2284 id_len = (lstrlenW(devices[device].dev_id) + 1) * sizeof(WCHAR);
2285 if(len < id_len)
2286 return MMSYSERR_ERROR;
2288 memcpy(str, devices[device].dev_id, id_len);
2290 return MMSYSERR_NOERROR;
2293 UINT WINMM_DRVMessage(UINT dev, UINT message, DWORD_PTR param1,
2294 DWORD_PTR param2)
2296 WINE_MLD *wmld;
2298 TRACE("(%u, %u, %ld, %ld)\n", dev, message, param1, param2);
2300 if((wmld = MMDRV_Get(ULongToHandle(dev), MMDRV_WAVEOUT, FALSE)) == NULL){
2301 if((wmld = MMDRV_Get(ULongToHandle(dev), MMDRV_WAVEOUT, TRUE)) != NULL)
2302 return MMDRV_PhysicalFeatures(wmld, message, param1, param2);
2303 return MMSYSERR_INVALHANDLE;
2306 if(message < DRVM_IOCTL ||
2307 (message >= DRVM_IOCTL_LAST && message < DRVM_MAPPER))
2308 return MMSYSERR_INVALPARAM;
2310 return MMDRV_Message(wmld, message, param1, param2);
2313 static UINT WINMM_FillDSDriverDesc(UINT dev, DSDRIVERDESC *desc, BOOL is_out)
2315 WCHAR *name;
2317 if(is_out){
2318 if(dev >= g_outmmdevices_count)
2319 return MMSYSERR_INVALHANDLE;
2320 name = g_out_mmdevices[dev].out_caps.szPname;
2321 }else{
2322 if(dev >= g_inmmdevices_count)
2323 return MMSYSERR_INVALHANDLE;
2324 name = g_in_mmdevices[dev].in_caps.szPname;
2327 memset(desc, 0, sizeof(*desc));
2329 strcpy(desc->szDesc, "WinMM: ");
2330 WideCharToMultiByte(CP_ACP, 0, name, -1,
2331 desc->szDesc + strlen(desc->szDesc),
2332 sizeof(desc->szDesc) - strlen(desc->szDesc), NULL, NULL);
2334 strcpy(desc->szDrvname, "winmm.dll");
2336 return MMSYSERR_NOERROR;
2339 /**************************************************************************
2340 * waveOutMessage [WINMM.@]
2342 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
2343 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2345 TRACE("(%p, %u, %lx, %lx)\n", hWaveOut, uMessage, dwParam1, dwParam2);
2347 switch(uMessage){
2348 case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
2349 return WINMM_QueryInstanceIDSize(HandleToULong(hWaveOut),
2350 (DWORD_PTR*)dwParam1, TRUE);
2351 case DRV_QUERYFUNCTIONINSTANCEID:
2352 return WINMM_QueryInstanceID(HandleToULong(hWaveOut),
2353 (WCHAR*)dwParam1, (DWORD_PTR)dwParam2, TRUE);
2354 /* TODO: Remove after dsound has been rewritten for mmdevapi */
2355 case DRV_QUERYDSOUNDDESC:
2356 case DRV_QUERYDSOUNDIFACE:
2357 if(dwParam2 == DS_HW_ACCEL_FULL)
2358 return WINMM_DRVMessage(HandleToULong(hWaveOut), uMessage,
2359 dwParam1, 0);
2360 return WINMM_FillDSDriverDesc(HandleToULong(hWaveOut),
2361 (DSDRIVERDESC*)dwParam1, TRUE);
2364 return MMSYSERR_NOTSUPPORTED;
2367 /**************************************************************************
2368 * waveInGetNumDevs [WINMM.@]
2370 UINT WINAPI waveInGetNumDevs(void)
2372 return MMDRV_GetNum(MMDRV_WAVEIN);
2375 /**************************************************************************
2376 * waveInGetDevCapsW [WINMM.@]
2378 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
2380 LPWINE_MLD wmld;
2382 TRACE("(%lu %p %u)!\n", uDeviceID, lpCaps, uSize);
2384 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2386 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_WAVEIN, TRUE)) == NULL)
2387 return MMSYSERR_BADDEVICEID;
2389 return MMDRV_Message(wmld, WIDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
2392 /**************************************************************************
2393 * waveInGetDevCapsA [WINMM.@]
2395 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
2397 WAVEINCAPSW wicW;
2398 UINT ret;
2400 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2402 ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
2404 if (ret == MMSYSERR_NOERROR) {
2405 WAVEINCAPSA wicA;
2406 wicA.wMid = wicW.wMid;
2407 wicA.wPid = wicW.wPid;
2408 wicA.vDriverVersion = wicW.vDriverVersion;
2409 WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
2410 sizeof(wicA.szPname), NULL, NULL );
2411 wicA.dwFormats = wicW.dwFormats;
2412 wicA.wChannels = wicW.wChannels;
2413 memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
2415 return ret;
2418 /**************************************************************************
2419 * waveInOpen [WINMM.@]
2421 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
2422 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2423 DWORD_PTR dwInstance, DWORD dwFlags)
2425 return WAVE_Open((HANDLE*)lphWaveIn, uDeviceID, MMDRV_WAVEIN, lpFormat,
2426 dwCallback, dwInstance, dwFlags);
2429 /**************************************************************************
2430 * waveInClose [WINMM.@]
2432 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
2434 LPWINE_MLD wmld;
2435 DWORD dwRet;
2437 TRACE("(%p)\n", hWaveIn);
2439 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2440 return MMSYSERR_INVALHANDLE;
2442 dwRet = MMDRV_Message(wmld, WIDM_CLOSE, 0L, 0L);
2443 if (dwRet != WAVERR_STILLPLAYING)
2444 MMDRV_Free(hWaveIn, wmld);
2445 return dwRet;
2448 /**************************************************************************
2449 * waveInPrepareHeader [WINMM.@]
2451 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2452 UINT uSize)
2454 LPWINE_MLD wmld;
2455 UINT result;
2457 TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2459 if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
2460 return MMSYSERR_INVALPARAM;
2462 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2463 return MMSYSERR_INVALHANDLE;
2465 if ((result = MMDRV_Message(wmld, WIDM_PREPARE, (DWORD_PTR)lpWaveInHdr,
2466 uSize)) != MMSYSERR_NOTSUPPORTED)
2467 return result;
2469 if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2470 return WAVERR_STILLPLAYING;
2472 lpWaveInHdr->dwFlags |= WHDR_PREPARED;
2473 lpWaveInHdr->dwFlags &= ~WHDR_DONE;
2474 lpWaveInHdr->dwBytesRecorded = 0;
2476 return MMSYSERR_NOERROR;
2479 /**************************************************************************
2480 * waveInUnprepareHeader [WINMM.@]
2482 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
2483 UINT uSize)
2485 LPWINE_MLD wmld;
2486 UINT result;
2488 TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2490 if (lpWaveInHdr == NULL || uSize < sizeof (WAVEHDR))
2491 return MMSYSERR_INVALPARAM;
2493 if (!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
2494 return MMSYSERR_NOERROR;
2496 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2497 return MMSYSERR_INVALHANDLE;
2499 if ((result = MMDRV_Message(wmld, WIDM_UNPREPARE, (DWORD_PTR)lpWaveInHdr,
2500 uSize)) != MMSYSERR_NOTSUPPORTED)
2501 return result;
2503 if (lpWaveInHdr->dwFlags & WHDR_INQUEUE)
2504 return WAVERR_STILLPLAYING;
2506 lpWaveInHdr->dwFlags &= ~WHDR_PREPARED;
2507 lpWaveInHdr->dwFlags |= WHDR_DONE;
2509 return MMSYSERR_NOERROR;
2512 /**************************************************************************
2513 * waveInAddBuffer [WINMM.@]
2515 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn,
2516 WAVEHDR* lpWaveInHdr, UINT uSize)
2518 LPWINE_MLD wmld;
2520 TRACE("(%p, %p, %u);\n", hWaveIn, lpWaveInHdr, uSize);
2522 if (lpWaveInHdr == NULL) return MMSYSERR_INVALPARAM;
2523 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2524 return MMSYSERR_INVALHANDLE;
2526 return MMDRV_Message(wmld, WIDM_ADDBUFFER, (DWORD_PTR)lpWaveInHdr, uSize);
2529 /**************************************************************************
2530 * waveInReset [WINMM.@]
2532 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
2534 LPWINE_MLD wmld;
2536 TRACE("(%p);\n", hWaveIn);
2538 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2539 return MMSYSERR_INVALHANDLE;
2541 return MMDRV_Message(wmld, WIDM_RESET, 0L, 0L);
2544 /**************************************************************************
2545 * waveInStart [WINMM.@]
2547 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
2549 LPWINE_MLD wmld;
2551 TRACE("(%p);\n", hWaveIn);
2553 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2554 return MMSYSERR_INVALHANDLE;
2556 return MMDRV_Message(wmld, WIDM_START, 0L, 0L);
2559 /**************************************************************************
2560 * waveInStop [WINMM.@]
2562 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
2564 LPWINE_MLD wmld;
2566 TRACE("(%p);\n", hWaveIn);
2568 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2569 return MMSYSERR_INVALHANDLE;
2571 return MMDRV_Message(wmld,WIDM_STOP, 0L, 0L);
2574 /**************************************************************************
2575 * waveInGetPosition [WINMM.@]
2577 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
2578 UINT uSize)
2580 LPWINE_MLD wmld;
2582 TRACE("(%p, %p, %u);\n", hWaveIn, lpTime, uSize);
2584 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2585 return MMSYSERR_INVALHANDLE;
2587 return MMDRV_Message(wmld, WIDM_GETPOS, (DWORD_PTR)lpTime, uSize);
2590 /**************************************************************************
2591 * waveInGetID [WINMM.@]
2593 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
2595 LPWINE_MLD wmld;
2597 TRACE("(%p, %p);\n", hWaveIn, lpuDeviceID);
2599 if (lpuDeviceID == NULL) return MMSYSERR_INVALHANDLE;
2601 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL)
2602 return MMSYSERR_INVALHANDLE;
2604 *lpuDeviceID = wmld->uDeviceID;
2605 return MMSYSERR_NOERROR;
2608 /**************************************************************************
2609 * waveInMessage [WINMM.@]
2611 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
2612 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
2614 LPWINE_MLD wmld;
2616 TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
2618 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, FALSE)) == NULL) {
2619 if ((wmld = MMDRV_Get(hWaveIn, MMDRV_WAVEIN, TRUE)) != NULL) {
2620 return MMDRV_PhysicalFeatures(wmld, uMessage, dwParam1, dwParam2);
2622 return MMSYSERR_INVALHANDLE;
2625 /* from M$ KB */
2626 if (uMessage < DRVM_IOCTL || (uMessage >= DRVM_IOCTL_LAST && uMessage < DRVM_MAPPER))
2627 return MMSYSERR_INVALPARAM;
2630 return MMDRV_Message(wmld, uMessage, dwParam1, dwParam2);
2633 /**************************************************************************
2634 * find out the real mixer ID depending on hmix (depends on dwFlags)
2636 static UINT MIXER_GetDev(HMIXEROBJ hmix, DWORD dwFlags, LPWINE_MIXER * lplpwm)
2638 LPWINE_MIXER lpwm = NULL;
2639 UINT uRet = MMSYSERR_NOERROR;
2641 switch (dwFlags & 0xF0000000ul) {
2642 case MIXER_OBJECTF_MIXER:
2643 lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, TRUE);
2644 break;
2645 case MIXER_OBJECTF_HMIXER:
2646 lpwm = (LPWINE_MIXER)MMDRV_Get(hmix, MMDRV_MIXER, FALSE);
2647 break;
2648 case MIXER_OBJECTF_WAVEOUT:
2649 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, TRUE, MMDRV_MIXER);
2650 break;
2651 case MIXER_OBJECTF_HWAVEOUT:
2652 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEOUT, FALSE, MMDRV_MIXER);
2653 break;
2654 case MIXER_OBJECTF_WAVEIN:
2655 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN, TRUE, MMDRV_MIXER);
2656 break;
2657 case MIXER_OBJECTF_HWAVEIN:
2658 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_WAVEIN, FALSE, MMDRV_MIXER);
2659 break;
2660 case MIXER_OBJECTF_MIDIOUT:
2661 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, TRUE, MMDRV_MIXER);
2662 break;
2663 case MIXER_OBJECTF_HMIDIOUT:
2664 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIOUT, FALSE, MMDRV_MIXER);
2665 break;
2666 case MIXER_OBJECTF_MIDIIN:
2667 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN, TRUE, MMDRV_MIXER);
2668 break;
2669 case MIXER_OBJECTF_HMIDIIN:
2670 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_MIDIIN, FALSE, MMDRV_MIXER);
2671 break;
2672 case MIXER_OBJECTF_AUX:
2673 lpwm = (LPWINE_MIXER)MMDRV_GetRelated(hmix, MMDRV_AUX, TRUE, MMDRV_MIXER);
2674 break;
2675 default:
2676 WARN("Unsupported flag (%08lx)\n", dwFlags & 0xF0000000ul);
2677 lpwm = 0;
2678 uRet = MMSYSERR_INVALFLAG;
2679 break;
2681 *lplpwm = lpwm;
2682 if (lpwm == 0 && uRet == MMSYSERR_NOERROR)
2683 uRet = MMSYSERR_INVALPARAM;
2684 return uRet;
2687 /**************************************************************************
2688 * mixerGetNumDevs [WINMM.@]
2690 UINT WINAPI mixerGetNumDevs(void)
2692 return MMDRV_GetNum(MMDRV_MIXER);
2695 /**************************************************************************
2696 * mixerGetDevCapsA [WINMM.@]
2698 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
2700 MIXERCAPSW micW;
2701 UINT ret;
2703 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2705 ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
2707 if (ret == MMSYSERR_NOERROR) {
2708 MIXERCAPSA micA;
2709 micA.wMid = micW.wMid;
2710 micA.wPid = micW.wPid;
2711 micA.vDriverVersion = micW.vDriverVersion;
2712 WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
2713 sizeof(micA.szPname), NULL, NULL );
2714 micA.fdwSupport = micW.fdwSupport;
2715 micA.cDestinations = micW.cDestinations;
2716 memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
2718 return ret;
2721 /**************************************************************************
2722 * mixerGetDevCapsW [WINMM.@]
2724 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
2726 LPWINE_MLD wmld;
2728 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2730 if ((wmld = MMDRV_Get((HANDLE)uDeviceID, MMDRV_MIXER, TRUE)) == NULL)
2731 return MMSYSERR_BADDEVICEID;
2733 return MMDRV_Message(wmld, MXDM_GETDEVCAPS, (DWORD_PTR)lpCaps, uSize);
2736 static void CALLBACK MIXER_WCallback(HMIXEROBJ hmx, UINT uMsg, DWORD_PTR dwInstance, DWORD_PTR dwParam, DWORD_PTR param2)
2738 HWND hWnd = (HWND)dwInstance;
2740 if (!dwInstance)
2741 return;
2743 PostMessageW(hWnd, uMsg, (WPARAM)hmx, (LPARAM)dwParam);
2746 /**************************************************************************
2747 * mixerOpen [WINMM.@]
2749 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
2750 DWORD_PTR dwInstance, DWORD fdwOpen)
2752 HANDLE hMix;
2753 LPWINE_MLD wmld;
2754 DWORD dwRet;
2755 MIXEROPENDESC mod;
2757 TRACE("(%p, %d, %08lx, %08lx, %08x)\n",
2758 lphMix, uDeviceID, dwCallback, dwInstance, fdwOpen);
2760 dwRet = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
2761 if (dwRet != MMSYSERR_NOERROR)
2762 return dwRet;
2764 mod.dwCallback = (DWORD_PTR)MIXER_WCallback;
2765 if ((fdwOpen & CALLBACK_TYPEMASK) == CALLBACK_WINDOW)
2766 mod.dwInstance = dwCallback;
2767 else
2768 mod.dwInstance = 0;
2770 /* We're remapping to CALLBACK_FUNCTION because that's what old winmm is
2771 * documented to do when opening the mixer driver.
2772 * FIXME: Native supports CALLBACK_EVENT + CALLBACK_THREAD flags since w2k.
2773 * FIXME: The non ALSA drivers ignore callback requests - bug.
2776 wmld = MMDRV_Alloc(sizeof(WINE_MIXER), MMDRV_MIXER, &hMix, &fdwOpen,
2777 &dwCallback, &dwInstance);
2778 wmld->uDeviceID = uDeviceID;
2779 mod.hmx = hMix;
2781 dwRet = MMDRV_Open(wmld, MXDM_OPEN, (DWORD_PTR)&mod, CALLBACK_FUNCTION);
2783 if (dwRet != MMSYSERR_NOERROR) {
2784 MMDRV_Free(hMix, wmld);
2785 hMix = 0;
2787 if (lphMix) *lphMix = hMix;
2788 TRACE("=> %d hMixer=%p\n", dwRet, hMix);
2790 return dwRet;
2793 /**************************************************************************
2794 * mixerClose [WINMM.@]
2796 UINT WINAPI mixerClose(HMIXER hMix)
2798 LPWINE_MLD wmld;
2799 DWORD dwRet;
2801 TRACE("(%p)\n", hMix);
2803 if ((wmld = MMDRV_Get(hMix, MMDRV_MIXER, FALSE)) == NULL) return MMSYSERR_INVALHANDLE;
2805 dwRet = MMDRV_Close(wmld, MXDM_CLOSE);
2806 MMDRV_Free(hMix, wmld);
2808 return dwRet;
2811 /**************************************************************************
2812 * mixerGetID [WINMM.@]
2814 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
2816 LPWINE_MIXER lpwm;
2817 UINT uRet = MMSYSERR_NOERROR;
2819 TRACE("(%p %p %08x)\n", hmix, lpid, fdwID);
2821 if ((uRet = MIXER_GetDev(hmix, fdwID, &lpwm)) != MMSYSERR_NOERROR)
2822 return uRet;
2824 if (lpid)
2825 *lpid = lpwm->mld.uDeviceID;
2827 return uRet;
2830 /**************************************************************************
2831 * mixerGetControlDetailsW [WINMM.@]
2833 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
2834 DWORD fdwDetails)
2836 LPWINE_MIXER lpwm;
2837 UINT uRet = MMSYSERR_NOERROR;
2839 TRACE("(%p, %p, %08x)\n", hmix, lpmcdW, fdwDetails);
2841 if ((uRet = MIXER_GetDev(hmix, fdwDetails, &lpwm)) != MMSYSERR_NOERROR)
2842 return uRet;
2844 if (lpmcdW == NULL || lpmcdW->cbStruct != sizeof(*lpmcdW))
2845 return MMSYSERR_INVALPARAM;
2847 return MMDRV_Message(&lpwm->mld, MXDM_GETCONTROLDETAILS, (DWORD_PTR)lpmcdW,
2848 fdwDetails);
2851 /**************************************************************************
2852 * mixerGetControlDetailsA [WINMM.@]
2854 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
2855 DWORD fdwDetails)
2857 DWORD ret = MMSYSERR_NOTENABLED;
2859 TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
2861 if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
2862 return MMSYSERR_INVALPARAM;
2864 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
2865 case MIXER_GETCONTROLDETAILSF_VALUE:
2866 /* can safely use A structure as it is, no string inside */
2867 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
2868 break;
2869 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
2871 MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = lpmcdA->paDetails;
2872 MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
2873 int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
2874 unsigned int i;
2876 if (lpmcdA->u.cMultipleItems != 0) {
2877 size *= lpmcdA->u.cMultipleItems;
2879 pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
2880 lpmcdA->paDetails = pDetailsW;
2881 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
2882 /* set up lpmcd->paDetails */
2883 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
2884 /* copy from lpmcd->paDetails back to paDetailsW; */
2885 if (ret == MMSYSERR_NOERROR) {
2886 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
2887 pDetailsA->dwParam1 = pDetailsW->dwParam1;
2888 pDetailsA->dwParam2 = pDetailsW->dwParam2;
2889 WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
2890 pDetailsA->szName,
2891 sizeof(pDetailsA->szName), NULL, NULL );
2892 pDetailsA++;
2893 pDetailsW++;
2895 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
2896 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
2898 HeapFree(GetProcessHeap(), 0, pDetailsW);
2899 lpmcdA->paDetails = pDetailsA;
2900 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
2902 break;
2903 default:
2904 ERR("Unsupported fdwDetails=0x%08x\n", fdwDetails);
2907 return ret;
2910 /**************************************************************************
2911 * mixerGetLineControlsA [WINMM.@]
2913 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
2914 DWORD fdwControls)
2916 MIXERLINECONTROLSW mlcW;
2917 DWORD ret;
2918 unsigned int i;
2920 TRACE("(%p, %p, %08x)\n", hmix, lpmlcA, fdwControls);
2922 if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
2923 lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
2924 return MMSYSERR_INVALPARAM;
2926 mlcW.cbStruct = sizeof(mlcW);
2927 mlcW.dwLineID = lpmlcA->dwLineID;
2928 mlcW.u.dwControlID = lpmlcA->u.dwControlID;
2929 mlcW.u.dwControlType = lpmlcA->u.dwControlType;
2931 /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
2932 the control count is assumed to be 1 - This is relied upon by a game,
2933 "Dynomite Deluze" */
2934 if (MIXER_GETLINECONTROLSF_ONEBYTYPE == (fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK)) {
2935 mlcW.cControls = 1;
2936 } else {
2937 mlcW.cControls = lpmlcA->cControls;
2939 mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
2940 mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
2941 mlcW.cControls * mlcW.cbmxctrl);
2943 ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
2945 if (ret == MMSYSERR_NOERROR) {
2946 lpmlcA->dwLineID = mlcW.dwLineID;
2947 lpmlcA->u.dwControlID = mlcW.u.dwControlID;
2948 lpmlcA->u.dwControlType = mlcW.u.dwControlType;
2950 for (i = 0; i < mlcW.cControls; i++) {
2951 lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
2952 lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
2953 lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
2954 lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
2955 lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
2956 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
2957 lpmlcA->pamxctrl[i].szShortName,
2958 sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
2959 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
2960 lpmlcA->pamxctrl[i].szName,
2961 sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
2962 /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
2963 * sizeof(mlcW.pamxctrl[i].Bounds) */
2964 memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
2965 sizeof(mlcW.pamxctrl[i].Bounds));
2966 /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
2967 * sizeof(mlcW.pamxctrl[i].Metrics) */
2968 memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
2969 sizeof(mlcW.pamxctrl[i].Metrics));
2973 HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
2975 return ret;
2978 /**************************************************************************
2979 * mixerGetLineControlsW [WINMM.@]
2981 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
2982 DWORD fdwControls)
2984 LPWINE_MIXER lpwm;
2985 UINT uRet = MMSYSERR_NOERROR;
2987 TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
2989 if ((uRet = MIXER_GetDev(hmix, fdwControls, &lpwm)) != MMSYSERR_NOERROR)
2990 return uRet;
2992 if (lpmlcW == NULL || lpmlcW->cbStruct != sizeof(*lpmlcW))
2993 return MMSYSERR_INVALPARAM;
2995 return MMDRV_Message(&lpwm->mld, MXDM_GETLINECONTROLS, (DWORD_PTR)lpmlcW,
2996 fdwControls);
2999 /**************************************************************************
3000 * mixerGetLineInfoW [WINMM.@]
3002 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
3004 LPWINE_MIXER lpwm;
3005 UINT uRet = MMSYSERR_NOERROR;
3007 TRACE("(%p, %p, %08x)\n", hmix, lpmliW, fdwInfo);
3009 if ((uRet = MIXER_GetDev(hmix, fdwInfo, &lpwm)) != MMSYSERR_NOERROR)
3010 return uRet;
3012 return MMDRV_Message(&lpwm->mld, MXDM_GETLINEINFO, (DWORD_PTR)lpmliW,
3013 fdwInfo);
3016 /**************************************************************************
3017 * mixerGetLineInfoA [WINMM.@]
3019 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
3020 DWORD fdwInfo)
3022 MIXERLINEW mliW;
3023 UINT ret;
3025 TRACE("(%p, %p, %08x)\n", hmix, lpmliA, fdwInfo);
3027 if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
3028 return MMSYSERR_INVALPARAM;
3030 mliW.cbStruct = sizeof(mliW);
3031 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
3032 case MIXER_GETLINEINFOF_COMPONENTTYPE:
3033 mliW.dwComponentType = lpmliA->dwComponentType;
3034 break;
3035 case MIXER_GETLINEINFOF_DESTINATION:
3036 mliW.dwDestination = lpmliA->dwDestination;
3037 break;
3038 case MIXER_GETLINEINFOF_LINEID:
3039 mliW.dwLineID = lpmliA->dwLineID;
3040 break;
3041 case MIXER_GETLINEINFOF_SOURCE:
3042 mliW.dwDestination = lpmliA->dwDestination;
3043 mliW.dwSource = lpmliA->dwSource;
3044 break;
3045 case MIXER_GETLINEINFOF_TARGETTYPE:
3046 mliW.Target.dwType = lpmliA->Target.dwType;
3047 mliW.Target.wMid = lpmliA->Target.wMid;
3048 mliW.Target.wPid = lpmliA->Target.wPid;
3049 mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
3050 MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname)/sizeof(WCHAR));
3051 break;
3052 default:
3053 WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
3054 return MMSYSERR_INVALFLAG;
3057 ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
3059 if(ret == MMSYSERR_NOERROR)
3061 lpmliA->dwDestination = mliW.dwDestination;
3062 lpmliA->dwSource = mliW.dwSource;
3063 lpmliA->dwLineID = mliW.dwLineID;
3064 lpmliA->fdwLine = mliW.fdwLine;
3065 lpmliA->dwUser = mliW.dwUser;
3066 lpmliA->dwComponentType = mliW.dwComponentType;
3067 lpmliA->cChannels = mliW.cChannels;
3068 lpmliA->cConnections = mliW.cConnections;
3069 lpmliA->cControls = mliW.cControls;
3070 WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
3071 sizeof(lpmliA->szShortName), NULL, NULL);
3072 WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
3073 sizeof(lpmliA->szName), NULL, NULL );
3074 lpmliA->Target.dwType = mliW.Target.dwType;
3075 lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
3076 lpmliA->Target.wMid = mliW.Target.wMid;
3077 lpmliA->Target.wPid = mliW.Target.wPid;
3078 lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
3079 WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
3080 sizeof(lpmliA->Target.szPname), NULL, NULL );
3082 return ret;
3085 /**************************************************************************
3086 * mixerSetControlDetails [WINMM.@]
3088 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
3089 DWORD fdwDetails)
3091 LPWINE_MIXER lpwm;
3092 UINT uRet = MMSYSERR_NOERROR;
3094 TRACE("(%p, %p, %08x)\n", hmix, lpmcd, fdwDetails);
3096 if ((uRet = MIXER_GetDev(hmix, fdwDetails, &lpwm)) != MMSYSERR_NOERROR)
3097 return uRet;
3099 return MMDRV_Message(&lpwm->mld, MXDM_SETCONTROLDETAILS, (DWORD_PTR)lpmcd,
3100 fdwDetails);
3103 /**************************************************************************
3104 * mixerMessage [WINMM.@]
3106 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3108 LPWINE_MLD wmld;
3110 TRACE("(%p, %d, %08lx, %08lx): semi-stub?\n",
3111 hmix, uMsg, dwParam1, dwParam2);
3113 if ((wmld = MMDRV_Get(hmix, MMDRV_MIXER, FALSE)) == NULL)
3114 return MMSYSERR_INVALHANDLE;
3116 return MMDRV_Message(wmld, uMsg, dwParam1, dwParam2);