TESTING -- override pthreads to fix gstreamer v5
[wine/multimedia.git] / dlls / winmm / waveform.c
blob93a5004999b5519cc24e810ffb850a7e7b054e18
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"
39 #include "resource.h"
41 #include "ole2.h"
42 #include "initguid.h"
43 #include "devpkey.h"
44 #include "mmdeviceapi.h"
45 #include "audioclient.h"
46 #include "audiopolicy.h"
48 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
52 /* HWAVE (and HMIXER) format:
54 * XXXX... 1FDD DDDD IIII IIII
55 * X = unused (must be 0)
56 * 1 = the bit is set to 1, to avoid all-zero HWAVEs
57 * F = flow direction (0 = IN, 1 = OUT)
58 * D = index into g_out_mmdevices, or all 1s for the MAPPER device
59 * I = index in the mmdevice's devices array
61 * Two reasons that we don't just use pointers:
62 * - HWAVEs must fit into 16 bits for compatibility with old applications.
63 * - We must be able to identify bad devices without crashing.
66 /* buffer size = 10 * 100000 (100 ns) = 0.1 seconds */
67 #define AC_BUFLEN (10 * 100000)
68 #define MAX_DEVICES 256
69 #define MAPPER_INDEX 0x3F
71 typedef struct _WINMM_CBInfo {
72 DWORD_PTR callback;
73 DWORD_PTR user;
74 DWORD flags;
75 HWAVE hwave;
76 } WINMM_CBInfo;
78 struct _WINMM_MMDevice;
79 typedef struct _WINMM_MMDevice WINMM_MMDevice;
81 typedef struct _WINMM_Device {
82 WINMM_CBInfo cb_info;
84 HWAVE handle;
86 BOOL open;
88 IMMDevice *device;
89 IAudioClient *client;
90 IAudioRenderClient *render;
91 IAudioCaptureClient *capture;
92 IAudioClock *clock;
93 IAudioStreamVolume *volume;
95 WAVEFORMATEX *orig_fmt;
96 HACMSTREAM acm_handle;
97 ACMSTREAMHEADER acm_hdr;
98 UINT32 acm_offs;
100 WAVEHDR *first, *last, *playing, *loop_start;
102 BOOL stopped;
103 DWORD loop_counter;
104 UINT32 bytes_per_frame, samples_per_sec, ofs_bytes, played_frames;
105 UINT32 remainder_frames; /* header chunk frames already played when a device switch occurred */
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;
120 EDataFlow dataflow;
122 ISimpleAudioVolume *volume;
124 GUID session;
126 UINT index;
128 /* HMIXER format is the same as the HWAVE format, but the I bits are
129 * replaced by the value of this counter, to keep each HMIXER unique */
130 UINT mixer_count;
132 CRITICAL_SECTION lock;
134 WINMM_Device *devices[MAX_DEVICES];
137 static WINMM_MMDevice *g_out_mmdevices;
138 static WINMM_MMDevice **g_out_map;
139 static UINT g_outmmdevices_count;
140 static WINMM_Device *g_out_mapper_devices[MAX_DEVICES];
142 static WINMM_MMDevice *g_in_mmdevices;
143 static WINMM_MMDevice **g_in_map;
144 static UINT g_inmmdevices_count;
145 static WINMM_Device *g_in_mapper_devices[MAX_DEVICES];
147 static IMMDeviceEnumerator *g_devenum;
149 static CRITICAL_SECTION g_devthread_lock;
150 static CRITICAL_SECTION_DEBUG g_devthread_lock_debug =
152 0, 0, &g_devthread_lock,
153 { &g_devthread_lock_debug.ProcessLocksList, &g_devthread_lock_debug.ProcessLocksList },
154 0, 0, { (DWORD_PTR)(__FILE__ ": g_devthread_lock") }
156 static CRITICAL_SECTION g_devthread_lock = { &g_devthread_lock_debug, -1, 0, 0, 0, 0 };
157 static LONG g_devthread_token;
158 static HANDLE g_devices_thread;
159 static HWND g_devices_hwnd;
160 static HMODULE g_devthread_module;
162 static UINT g_devhandle_count;
163 static HANDLE *g_device_handles;
164 static WINMM_Device **g_handle_devices;
166 typedef struct _WINMM_OpenInfo {
167 HWAVE handle;
168 UINT req_device;
169 WAVEFORMATEX *format;
170 DWORD_PTR callback;
171 DWORD_PTR cb_user;
172 DWORD flags;
173 BOOL reset;
174 } WINMM_OpenInfo;
176 typedef struct _WINMM_ControlDetails {
177 HMIXEROBJ hmix;
178 MIXERCONTROLDETAILS *details;
179 DWORD flags;
180 } WINMM_ControlDetails;
182 typedef struct _WINMM_QueryInterfaceInfo {
183 BOOL is_out;
184 UINT index;
185 WCHAR *str;
186 UINT *len_bytes;
187 } WINMM_QueryInterfaceInfo;
189 static LRESULT WOD_Open(WINMM_OpenInfo *info);
190 static LRESULT WOD_Close(HWAVEOUT hwave);
191 static LRESULT WID_Open(WINMM_OpenInfo *info);
192 static LRESULT WID_Close(HWAVEIN hwave);
193 static MMRESULT WINMM_BeginPlaying(WINMM_Device *device);
195 void WINMM_DeleteWaveform(void)
197 UINT i, j;
199 if(g_devices_thread)
200 CloseHandle(g_devices_thread);
202 for(i = 0; i < g_outmmdevices_count; ++i){
203 WINMM_MMDevice *mmdevice = &g_out_mmdevices[i];
205 for(j = 0; j < MAX_DEVICES && mmdevice->devices[j]; ++j){
206 WINMM_Device *device = mmdevice->devices[j];
207 if(device->handle)
208 CloseHandle(device->handle);
209 DeleteCriticalSection(&device->lock);
212 if(mmdevice->volume)
213 ISimpleAudioVolume_Release(mmdevice->volume);
214 CoTaskMemFree(mmdevice->dev_id);
215 DeleteCriticalSection(&mmdevice->lock);
218 for(i = 0; i < g_inmmdevices_count; ++i){
219 WINMM_MMDevice *mmdevice = &g_in_mmdevices[i];
221 for(j = 0; j < MAX_DEVICES && mmdevice->devices[j]; ++j){
222 WINMM_Device *device = mmdevice->devices[j];
223 if(device->handle)
224 CloseHandle(device->handle);
225 DeleteCriticalSection(&device->lock);
228 if(mmdevice->volume)
229 ISimpleAudioVolume_Release(mmdevice->volume);
230 CoTaskMemFree(mmdevice->dev_id);
231 DeleteCriticalSection(&mmdevice->lock);
234 HeapFree(GetProcessHeap(), 0, g_out_mmdevices);
235 HeapFree(GetProcessHeap(), 0, g_in_mmdevices);
237 HeapFree(GetProcessHeap(), 0, g_device_handles);
238 HeapFree(GetProcessHeap(), 0, g_handle_devices);
240 DeleteCriticalSection(&g_devthread_lock);
243 static inline HWAVE WINMM_MakeHWAVE(UINT mmdevice, BOOL is_out, UINT device)
245 return ULongToHandle((1 << 15) | ((!!is_out) << 14) |
246 (mmdevice << 8) | device);
249 static inline void WINMM_DecomposeHWAVE(HWAVE hwave, UINT *mmdevice_index,
250 BOOL *is_out, UINT *device_index, UINT *junk)
252 ULONG32 l = HandleToULong(hwave);
253 *device_index = l & 0xFF;
254 *mmdevice_index = (l >> 8) & 0x3F;
255 *is_out = (l >> 14) & 0x1;
256 *junk = l >> 15;
259 static void WINMM_InitDevice(WINMM_Device *device)
261 InitializeCriticalSection(&device->lock);
262 device->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM_Device.lock");
265 static inline WINMM_MMDevice *read_map(WINMM_MMDevice **map, UINT index)
267 WINMM_MMDevice *ret;
268 EnterCriticalSection(&g_devthread_lock);
269 ret = map[index];
270 LeaveCriticalSection(&g_devthread_lock);
271 return ret;
274 /* finds the first unused Device, marks it as "open", and returns
275 * a pointer to the device
277 * IMPORTANT: it is the caller's responsibility to release the device's lock
278 * on success
280 static WINMM_Device *WINMM_FindUnusedDevice(WINMM_Device **devices,
281 WINMM_MMDevice *parent, UINT internal_index, BOOL is_out)
283 UINT i;
285 for(i = 0; i < MAX_DEVICES; ++i){
286 WINMM_Device *device = devices[i];
288 if(!device){
289 device = devices[i] = HeapAlloc(GetProcessHeap(),
290 HEAP_ZERO_MEMORY, sizeof(WINMM_Device));
291 if(!device)
292 return NULL;
294 WINMM_InitDevice(device);
295 EnterCriticalSection(&device->lock);
296 }else
297 EnterCriticalSection(&device->lock);
299 if(!device->open){
300 device->handle = WINMM_MakeHWAVE(internal_index, is_out, i);
301 device->parent = parent;
302 device->open = TRUE;
304 return device;
307 LeaveCriticalSection(&device->lock);
310 TRACE("All devices in use: mmdevice: %u\n", internal_index);
312 return NULL;
315 static inline BOOL WINMM_ValidateAndLock(WINMM_Device *device)
317 if(!device)
318 return FALSE;
320 EnterCriticalSection(&device->lock);
322 if(!device->open){
323 LeaveCriticalSection(&device->lock);
324 return FALSE;
327 return TRUE;
330 static WINMM_Device *WINMM_GetDeviceFromHWAVE(HWAVE hwave)
332 WINMM_MMDevice *mmdevice;
333 WINMM_Device *device;
334 UINT mmdevice_index, device_index, junk;
335 BOOL is_out;
337 WINMM_DecomposeHWAVE(hwave, &mmdevice_index, &is_out, &device_index, &junk);
339 if(junk != 0x1)
340 return NULL;
342 if(mmdevice_index == MAPPER_INDEX){
343 EnterCriticalSection(&g_devthread_lock);
344 if(is_out)
345 device = g_out_mapper_devices[device_index];
346 else
347 device = g_in_mapper_devices[device_index];
348 LeaveCriticalSection(&g_devthread_lock);
349 return device;
352 if(mmdevice_index >= (is_out ? g_outmmdevices_count : g_inmmdevices_count))
353 return NULL;
355 if(is_out)
356 mmdevice = &g_out_mmdevices[mmdevice_index];
357 else
358 mmdevice = &g_in_mmdevices[mmdevice_index];
360 EnterCriticalSection(&mmdevice->lock);
362 device = mmdevice->devices[device_index];
364 LeaveCriticalSection(&mmdevice->lock);
366 return device;
369 /* Note: NotifyClient should never be called while holding the device lock
370 * since the client may call wave* functions from within the callback. */
371 static inline void WINMM_NotifyClient(WINMM_CBInfo *info, WORD msg, DWORD_PTR param1,
372 DWORD_PTR param2)
374 DriverCallback(info->callback, info->flags, (HDRVR)info->hwave,
375 msg, info->user, param1, param2);
378 static MMRESULT hr2mmr(HRESULT hr)
380 switch(hr){
381 case S_OK:
382 case AUDCLNT_E_NOT_STOPPED:
383 return MMSYSERR_NOERROR;
384 case AUDCLNT_E_UNSUPPORTED_FORMAT:
385 return WAVERR_BADFORMAT;
386 case AUDCLNT_E_DEVICE_IN_USE:
387 return MMSYSERR_ALLOCATED;
388 case AUDCLNT_E_ENDPOINT_CREATE_FAILED:
389 return MMSYSERR_NOTENABLED;
390 case E_OUTOFMEMORY:
391 return MMSYSERR_NOMEM;
392 case E_POINTER:
393 case E_INVALIDARG:
394 return MMSYSERR_INVALPARAM;
395 case AUDCLNT_E_DEVICE_INVALIDATED: /* DSERR_BUFFERLOST */
396 default:
397 return FAILED(hr) ? MMSYSERR_ERROR : MMSYSERR_NOERROR;
401 static HRESULT WINMM_GetFriendlyName(IMMDevice *device, WCHAR *out,
402 UINT outlen)
404 IPropertyStore *ps;
405 PROPVARIANT var;
406 HRESULT hr;
408 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
409 if(FAILED(hr))
410 return hr;
412 PropVariantInit(&var);
414 hr = IPropertyStore_GetValue(ps,
415 (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &var);
416 if(FAILED(hr)){
417 IPropertyStore_Release(ps);
418 return hr;
421 lstrcpynW(out, var.u.pwszVal, outlen);
423 PropVariantClear(&var);
425 IPropertyStore_Release(ps);
427 return S_OK;
430 static HRESULT WINMM_TestFormat(IAudioClient *client, DWORD rate, DWORD depth,
431 WORD channels)
433 WAVEFORMATEX fmt, *junk;
434 HRESULT hr;
436 fmt.wFormatTag = WAVE_FORMAT_PCM;
437 fmt.nChannels = channels;
438 fmt.nSamplesPerSec = rate;
439 fmt.wBitsPerSample = depth;
440 fmt.nBlockAlign = (channels * depth) / 8;
441 fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
442 fmt.cbSize = 0;
444 hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED,
445 &fmt, &junk);
446 if(SUCCEEDED(hr))
447 CoTaskMemFree(junk);
449 return hr;
452 static struct _TestFormat {
453 DWORD flag;
454 DWORD rate;
455 DWORD depth;
456 WORD channels;
457 } formats_to_test[] = {
458 { WAVE_FORMAT_1M08, 11025, 8, 1 },
459 { WAVE_FORMAT_1M16, 11025, 16, 1 },
460 { WAVE_FORMAT_1S08, 11025, 8, 2 },
461 { WAVE_FORMAT_1S16, 11025, 16, 2 },
462 { WAVE_FORMAT_2M08, 22050, 8, 1 },
463 { WAVE_FORMAT_2M16, 22050, 16, 1 },
464 { WAVE_FORMAT_2S08, 22050, 8, 2 },
465 { WAVE_FORMAT_2S16, 22050, 16, 2 },
466 { WAVE_FORMAT_4M08, 44100, 8, 1 },
467 { WAVE_FORMAT_4M16, 44100, 16, 1 },
468 { WAVE_FORMAT_4S08, 44100, 8, 2 },
469 { WAVE_FORMAT_4S16, 44100, 16, 2 },
470 { WAVE_FORMAT_48M08, 48000, 8, 1 },
471 { WAVE_FORMAT_48M16, 48000, 16, 1 },
472 { WAVE_FORMAT_48S08, 48000, 8, 2 },
473 { WAVE_FORMAT_48S16, 48000, 16, 2 },
474 { WAVE_FORMAT_96M08, 96000, 8, 1 },
475 { WAVE_FORMAT_96M16, 96000, 16, 1 },
476 { WAVE_FORMAT_96S08, 96000, 8, 2 },
477 { WAVE_FORMAT_96S16, 96000, 16, 2 },
481 static DWORD WINMM_GetSupportedFormats(IMMDevice *device)
483 DWORD flags = 0;
484 HRESULT hr;
485 struct _TestFormat *fmt;
486 IAudioClient *client;
488 hr = IMMDevice_Activate(device, &IID_IAudioClient,
489 CLSCTX_INPROC_SERVER, NULL, (void**)&client);
490 if(FAILED(hr))
491 return 0;
493 for(fmt = formats_to_test; fmt->flag; ++fmt){
494 hr = WINMM_TestFormat(client, fmt->rate, fmt->depth, fmt->channels);
495 if(hr == S_OK)
496 flags |= fmt->flag;
499 IAudioClient_Release(client);
501 return flags;
504 static HRESULT WINMM_InitMMDevice(EDataFlow flow, IMMDevice *device,
505 WINMM_MMDevice *dev, UINT index)
507 HRESULT hr;
509 dev->dataflow = flow;
510 if(flow == eRender){
511 dev->out_caps.wMid = 0xFF;
512 dev->out_caps.wPid = 0xFF;
513 dev->out_caps.vDriverVersion = 0x00010001;
514 dev->out_caps.dwFormats = WINMM_GetSupportedFormats(device);
515 dev->out_caps.wReserved1 = 0;
516 dev->out_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
517 WAVECAPS_SAMPLEACCURATE;
518 dev->out_caps.wChannels = 2;
519 dev->out_caps.szPname[0] = '\0';
521 hr = WINMM_GetFriendlyName(device, dev->out_caps.szPname,
522 sizeof(dev->out_caps.szPname) /
523 sizeof(*dev->out_caps.szPname));
524 if(FAILED(hr))
525 return hr;
526 }else{
527 dev->in_caps.wMid = 0xFF;
528 dev->in_caps.wPid = 0xFF;
529 dev->in_caps.vDriverVersion = 0x00010001;
530 dev->in_caps.dwFormats = WINMM_GetSupportedFormats(device);
531 dev->in_caps.wReserved1 = 0;
532 dev->in_caps.wChannels = 2;
533 dev->in_caps.szPname[0] = '\0';
535 hr = WINMM_GetFriendlyName(device, dev->in_caps.szPname,
536 sizeof(dev->in_caps.szPname) /
537 sizeof(*dev->in_caps.szPname));
538 if(FAILED(hr))
539 return hr;
542 hr = IMMDevice_GetId(device, &dev->dev_id);
543 if(FAILED(hr))
544 return hr;
546 CoCreateGuid(&dev->session);
548 dev->index = index;
550 InitializeCriticalSection(&dev->lock);
551 dev->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM_Device.lock");
553 return S_OK;
556 static HRESULT WINMM_EnumDevices(WINMM_MMDevice **devices,
557 WINMM_MMDevice ***map, UINT *devcount, EDataFlow flow,
558 IMMDeviceEnumerator *devenum)
560 IMMDeviceCollection *devcoll;
561 HRESULT hr;
563 hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flow,
564 DEVICE_STATE_ACTIVE, &devcoll);
565 if(FAILED(hr))
566 return hr;
568 hr = IMMDeviceCollection_GetCount(devcoll, devcount);
569 if(FAILED(hr)){
570 IMMDeviceCollection_Release(devcoll);
571 return hr;
574 if(*devcount > 0){
575 UINT n, count = 1;
576 IMMDevice *def_dev = NULL;
578 *devices = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
579 sizeof(WINMM_MMDevice) * (*devcount));
580 if(!*devices){
581 IMMDeviceCollection_Release(devcoll);
582 return E_OUTOFMEMORY;
585 *map = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
586 sizeof(WINMM_MMDevice *) * (*devcount));
587 if(!*map){
588 IMMDeviceCollection_Release(devcoll);
589 HeapFree(GetProcessHeap(), 0, *devices);
590 return E_OUTOFMEMORY;
593 /* make sure that device 0 is the default device */
594 IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum,
595 flow, eConsole, &def_dev);
597 for(n = 0; n < *devcount; ++n){
598 IMMDevice *device;
600 hr = IMMDeviceCollection_Item(devcoll, n, &device);
601 if(SUCCEEDED(hr)){
602 WINMM_InitMMDevice(flow, device, &(*devices)[n], n);
604 if(device == def_dev)
605 (*map)[0] = &(*devices)[n];
606 else{
607 (*map)[count] = &(*devices)[n];
608 ++count;
611 IMMDevice_Release(device);
615 IMMDevice_Release(def_dev);
617 *devcount = count;
620 IMMDeviceCollection_Release(devcoll);
622 return S_OK;
625 static HRESULT WINAPI notif_QueryInterface(IMMNotificationClient *iface,
626 const GUID *riid, void **obj)
628 ERR("Unexpected QueryInterface call: %s\n", wine_dbgstr_guid(riid));
629 return E_NOINTERFACE;
632 static ULONG WINAPI notif_AddRef(IMMNotificationClient *iface)
634 return 2;
637 static ULONG WINAPI notif_Release(IMMNotificationClient *iface)
639 return 1;
642 static HRESULT WINAPI notif_OnDeviceStateChanged(IMMNotificationClient *iface,
643 const WCHAR *device_id, DWORD new_state)
645 TRACE("Ignoring OnDeviceStateChanged callback\n");
646 return S_OK;
649 static HRESULT WINAPI notif_OnDeviceAdded(IMMNotificationClient *iface,
650 const WCHAR *device_id)
652 TRACE("Ignoring OnDeviceAdded callback\n");
653 return S_OK;
656 static HRESULT WINAPI notif_OnDeviceRemoved(IMMNotificationClient *iface,
657 const WCHAR *device_id)
659 TRACE("Ignoring OnDeviceRemoved callback\n");
660 return S_OK;
663 static HRESULT update_mapping(WINMM_MMDevice ***map, UINT count,
664 const WCHAR *default_id)
666 WINMM_MMDevice *prev;
667 UINT i;
669 prev = (*map)[0];
670 for(i = 0; i < count; ++i){
671 WINMM_MMDevice *tmp;
673 if(!lstrcmpW((*map)[i]->dev_id, default_id)){
674 (*map)[0] = (*map)[i];
675 (*map)[i] = prev;
677 return S_OK;
680 tmp = (*map)[i];
681 (*map)[i] = prev;
682 prev = tmp;
685 WARN("Couldn't find new default device! Rearranged map for no reason.\n");
686 (*map)[0] = prev;
688 return S_FALSE;
691 static HRESULT reroute_mapper_device(WINMM_Device *device, BOOL is_out)
693 WINMM_OpenInfo info;
694 BOOL stopped;
695 MMRESULT mr;
696 HRESULT hr;
697 UINT64 clock_freq, clock_pos;
699 TRACE("rerouting device %p\n", device->handle);
701 EnterCriticalSection(&device->lock);
703 if(!device->open || device->acm_handle){
704 /* Windows 7 doesn't re-route ACM devices, so we don't either.
705 * Seems to be because of the data waveXxxPrepareHeader allocates. */
706 LeaveCriticalSection(&device->lock);
707 return S_FALSE;
710 stopped = device->stopped;
712 info.handle = 0;
713 info.req_device = WAVE_MAPPER;
714 info.format = device->orig_fmt;
715 info.callback = device->cb_info.callback;
716 info.cb_user = device->cb_info.user;
717 /* We have to use direct here so that we don't suddenly introduce ACM
718 * into a playing stream that hasn't been Prepared for it */
719 info.flags = (device->cb_info.flags << 16) | WAVE_FORMAT_DIRECT_QUERY;
720 info.reset = FALSE;
722 if(is_out)
723 mr = WOD_Open(&info);
724 else
725 mr = WID_Open(&info);
727 if(mr != MMSYSERR_NOERROR){
728 TRACE("New default device doesn't support this stream: %p\n", device->handle);
729 LeaveCriticalSection(&device->lock);
730 return S_FALSE;
733 hr = IAudioClient_Stop(device->client);
734 if(FAILED(hr))
735 WARN("Stop failed: %08x\n", hr);
737 hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
738 if(FAILED(hr)){
739 WARN("GetFrequency failed: %08x\n", hr);
740 LeaveCriticalSection(&device->lock);
741 return hr;
744 hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
745 if(FAILED(hr)){
746 WARN("GetPosition failed: %08x\n", hr);
747 LeaveCriticalSection(&device->lock);
748 return hr;
751 device->remainder_frames = MulDiv(clock_pos, device->samples_per_sec, clock_freq) - device->last_clock_pos;
753 info.handle = device->handle;
754 info.flags = (device->cb_info.flags << 16) | WAVE_FORMAT_DIRECT;
756 if(is_out){
757 WOD_Close((HWAVEOUT)device->handle);
758 device->parent = read_map(g_out_map, 0);
759 mr = WOD_Open(&info);
760 }else{
761 WID_Close((HWAVEIN)device->handle);
762 device->parent = read_map(g_in_map, 0);
763 mr = WID_Open(&info);
766 if(mr != MMSYSERR_NOERROR){
767 ERR("Opening new default device failed! %u\n", mr);
768 LeaveCriticalSection(&device->lock);
769 return E_FAIL;
772 HeapFree(GetProcessHeap(), 0, info.format);
774 if(!stopped)
775 WINMM_BeginPlaying(device);
777 LeaveCriticalSection(&device->lock);
779 return S_OK;
782 static HRESULT WINAPI notif_OnDefaultDeviceChanged(IMMNotificationClient *iface,
783 EDataFlow flow, ERole role, const WCHAR *device_id)
785 UINT i;
787 TRACE("%u %u %s\n", flow, role, wine_dbgstr_w(device_id));
789 if(role != eConsole)
790 return S_OK;
792 EnterCriticalSection(&g_devthread_lock);
794 if(flow == eRender)
795 update_mapping(&g_out_map, g_outmmdevices_count, device_id);
796 else
797 update_mapping(&g_in_map, g_inmmdevices_count, device_id);
799 for(i = 0; i < MAX_DEVICES && g_out_mapper_devices[i]; ++i)
800 reroute_mapper_device(g_out_mapper_devices[i], TRUE);
802 for(i = 0; i < MAX_DEVICES && g_in_mapper_devices[i]; ++i)
803 reroute_mapper_device(g_in_mapper_devices[i], FALSE);
805 LeaveCriticalSection(&g_devthread_lock);
807 return S_OK;
810 static HRESULT WINAPI notif_OnPropertyValueChanged(IMMNotificationClient *iface,
811 const WCHAR *device_id, const PROPERTYKEY key)
813 TRACE("Ignoring OnPropertyValueChanged callback\n");
814 return S_OK;
817 static IMMNotificationClientVtbl g_notif_vtbl = {
818 notif_QueryInterface,
819 notif_AddRef,
820 notif_Release,
821 notif_OnDeviceStateChanged,
822 notif_OnDeviceAdded,
823 notif_OnDeviceRemoved,
824 notif_OnDefaultDeviceChanged,
825 notif_OnPropertyValueChanged
828 static IMMNotificationClient g_notif = { &g_notif_vtbl };
830 static HRESULT WINMM_InitMMDevices(void)
832 HRESULT hr, init_hr;
833 IMMDeviceEnumerator *devenum = NULL;
835 if(g_outmmdevices_count || g_inmmdevices_count)
836 return S_FALSE;
838 init_hr = CoInitialize(NULL);
840 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
841 CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&devenum);
842 if(FAILED(hr))
843 goto exit;
845 hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(devenum, &g_notif);
846 if(FAILED(hr))
847 WARN("RegisterEndpointNotificationCallback failed: %08x\n", hr);
849 hr = WINMM_EnumDevices(&g_out_mmdevices, &g_out_map, &g_outmmdevices_count,
850 eRender, devenum);
851 if(FAILED(hr)){
852 g_outmmdevices_count = 0;
853 g_inmmdevices_count = 0;
854 goto exit;
857 hr = WINMM_EnumDevices(&g_in_mmdevices, &g_in_map, &g_inmmdevices_count,
858 eCapture, devenum);
859 if(FAILED(hr)){
860 g_inmmdevices_count = 0;
861 goto exit;
864 exit:
865 if(devenum)
866 IMMDeviceEnumerator_Release(devenum);
867 if(SUCCEEDED(init_hr))
868 CoUninitialize();
870 return hr;
873 static inline BOOL WINMM_IsMapper(UINT device)
875 return (device == WAVE_MAPPER || device == (UINT16)WAVE_MAPPER);
878 static MMRESULT WINMM_TryDeviceMapping(WINMM_Device *device, WAVEFORMATEX *fmt,
879 WORD channels, DWORD freq, DWORD bits_per_samp, BOOL is_query, BOOL is_out)
881 WAVEFORMATEX target, *closer_fmt = NULL;
882 HRESULT hr;
883 MMRESULT mr;
885 TRACE("format: %u, channels: %u, sample rate: %u, bit depth: %u\n",
886 WAVE_FORMAT_PCM, channels, freq, bits_per_samp);
888 target.wFormatTag = WAVE_FORMAT_PCM;
889 target.nChannels = channels;
890 target.nSamplesPerSec = freq;
891 target.wBitsPerSample = bits_per_samp;
892 target.nBlockAlign = (target.nChannels * target.wBitsPerSample) / 8;
893 target.nAvgBytesPerSec = target.nSamplesPerSec * target.nBlockAlign;
894 target.cbSize = 0;
896 hr = IAudioClient_IsFormatSupported(device->client,
897 AUDCLNT_SHAREMODE_SHARED, &target, &closer_fmt);
898 if(closer_fmt)
899 CoTaskMemFree(closer_fmt);
900 if(hr != S_OK)
901 return WAVERR_BADFORMAT;
903 /* device supports our target format, so see if MSACM can
904 * do the conversion */
905 if(is_out)
906 mr = acmStreamOpen(&device->acm_handle, NULL, fmt, &target, NULL,
907 0, 0, 0);
908 else
909 mr = acmStreamOpen(&device->acm_handle, NULL, &target, fmt, NULL,
910 0, 0, 0);
911 if(mr != MMSYSERR_NOERROR)
912 return mr;
914 /* yes it can. initialize the audioclient and return success */
915 if(is_query){
916 acmStreamClose(device->acm_handle, 0);
917 device->acm_handle = NULL;
918 return MMSYSERR_NOERROR;
921 hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED,
922 AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
923 AC_BUFLEN, 0, &target, &device->parent->session);
924 if(hr != S_OK){
925 WARN("Initialize failed: %08x\n", hr);
926 acmStreamClose(device->acm_handle, 0);
927 device->acm_handle = NULL;
928 return MMSYSERR_ERROR;
931 device->bytes_per_frame = target.nBlockAlign;
932 device->samples_per_sec = target.nSamplesPerSec;
934 TRACE("Success!\n");
936 return MMSYSERR_NOERROR;
939 static MMRESULT WINMM_MapDevice(WINMM_Device *device, BOOL is_query, BOOL is_out)
941 MMRESULT mr;
942 WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)device->orig_fmt;
944 TRACE("(%p, %u)\n", device, is_out);
946 /* set up the ACM stream */
947 if(device->orig_fmt->wFormatTag != WAVE_FORMAT_PCM &&
948 !(device->orig_fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
949 IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
950 /* convert to PCM format if it's not already */
951 mr = WINMM_TryDeviceMapping(device, device->orig_fmt,
952 device->orig_fmt->nChannels, device->orig_fmt->nSamplesPerSec,
953 16, is_query, is_out);
954 if(mr == MMSYSERR_NOERROR)
955 return mr;
957 mr = WINMM_TryDeviceMapping(device, device->orig_fmt,
958 device->orig_fmt->nChannels, device->orig_fmt->nSamplesPerSec,
959 8, is_query, is_out);
960 if(mr == MMSYSERR_NOERROR)
961 return mr;
962 }else{
963 WORD channels;
965 /* first try just changing bit depth and channels */
966 channels = device->orig_fmt->nChannels;
967 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
968 device->orig_fmt->nSamplesPerSec, 16, is_query, is_out);
969 if(mr == MMSYSERR_NOERROR)
970 return mr;
971 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
972 device->orig_fmt->nSamplesPerSec, 8, is_query, is_out);
973 if(mr == MMSYSERR_NOERROR)
974 return mr;
976 channels = (channels == 2) ? 1 : 2;
977 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
978 device->orig_fmt->nSamplesPerSec, 16, is_query, is_out);
979 if(mr == MMSYSERR_NOERROR)
980 return mr;
981 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
982 device->orig_fmt->nSamplesPerSec, 8, is_query, is_out);
983 if(mr == MMSYSERR_NOERROR)
984 return mr;
986 /* that didn't work, so now try different sample rates */
987 channels = device->orig_fmt->nChannels;
988 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 16, is_query, is_out);
989 if(mr == MMSYSERR_NOERROR)
990 return mr;
991 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 16, is_query, is_out);
992 if(mr == MMSYSERR_NOERROR)
993 return mr;
994 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 16, is_query, is_out);
995 if(mr == MMSYSERR_NOERROR)
996 return mr;
997 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 16, is_query, is_out);
998 if(mr == MMSYSERR_NOERROR)
999 return mr;
1000 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 16, is_query, is_out);
1001 if(mr == MMSYSERR_NOERROR)
1002 return mr;
1004 channels = (channels == 2) ? 1 : 2;
1005 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 16, is_query, is_out);
1006 if(mr == MMSYSERR_NOERROR)
1007 return mr;
1008 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 16, is_query, is_out);
1009 if(mr == MMSYSERR_NOERROR)
1010 return mr;
1011 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 16, is_query, is_out);
1012 if(mr == MMSYSERR_NOERROR)
1013 return mr;
1014 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 16, is_query, is_out);
1015 if(mr == MMSYSERR_NOERROR)
1016 return mr;
1017 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 16, is_query, is_out);
1018 if(mr == MMSYSERR_NOERROR)
1019 return mr;
1021 channels = device->orig_fmt->nChannels;
1022 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 8, is_query, is_out);
1023 if(mr == MMSYSERR_NOERROR)
1024 return mr;
1025 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 8, is_query, is_out);
1026 if(mr == MMSYSERR_NOERROR)
1027 return mr;
1028 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 8, is_query, is_out);
1029 if(mr == MMSYSERR_NOERROR)
1030 return mr;
1031 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 8, is_query, is_out);
1032 if(mr == MMSYSERR_NOERROR)
1033 return mr;
1034 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 8, is_query, is_out);
1035 if(mr == MMSYSERR_NOERROR)
1036 return mr;
1038 channels = (channels == 2) ? 1 : 2;
1039 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 8, is_query, is_out);
1040 if(mr == MMSYSERR_NOERROR)
1041 return mr;
1042 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 8, is_query, is_out);
1043 if(mr == MMSYSERR_NOERROR)
1044 return mr;
1045 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 8, is_query, is_out);
1046 if(mr == MMSYSERR_NOERROR)
1047 return mr;
1048 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 8, is_query, is_out);
1049 if(mr == MMSYSERR_NOERROR)
1050 return mr;
1051 mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 8, is_query, is_out);
1052 if(mr == MMSYSERR_NOERROR)
1053 return mr;
1056 WARN("Unable to find compatible device!\n");
1057 return WAVERR_BADFORMAT;
1060 static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info,
1061 BOOL is_out)
1063 LRESULT ret = MMSYSERR_NOMEM;
1064 HRESULT hr;
1066 hr = IMMDeviceEnumerator_GetDevice(g_devenum, device->parent->dev_id,
1067 &device->device);
1068 if(FAILED(hr)){
1069 WARN("Device %s (%s) unavailable: %08x\n",
1070 wine_dbgstr_w(device->parent->dev_id),
1071 wine_dbgstr_w(device->parent->out_caps.szPname), hr);
1072 ret = MMSYSERR_NODRIVER;
1073 goto error;
1076 /* this is where winexyz.drv opens the audio device */
1077 hr = IMMDevice_Activate(device->device, &IID_IAudioClient,
1078 CLSCTX_INPROC_SERVER, NULL, (void**)&device->client);
1079 if(FAILED(hr)){
1080 WARN("Activate failed: %08x\n", hr);
1081 ret = hr2mmr(hr);
1082 if(ret == MMSYSERR_ERROR)
1083 ret = MMSYSERR_NOTENABLED;
1084 goto error;
1087 if(info->format->wFormatTag == WAVE_FORMAT_PCM){
1088 /* we aren't guaranteed that the struct in lpFormat is a full
1089 * WAVEFORMATEX struct, which IAC::IsFormatSupported requires */
1090 device->orig_fmt = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX));
1091 memcpy(device->orig_fmt, info->format, sizeof(PCMWAVEFORMAT));
1092 device->orig_fmt->cbSize = 0;
1093 if(device->orig_fmt->wBitsPerSample % 8 != 0){
1094 WARN("Fixing bad wBitsPerSample (%u)\n", device->orig_fmt->wBitsPerSample);
1095 device->orig_fmt->wBitsPerSample = (device->orig_fmt->wBitsPerSample + 7) & ~7;
1097 /* winmm ignores broken blockalign and avgbytes */
1098 if(device->orig_fmt->nBlockAlign != device->orig_fmt->nChannels * device->orig_fmt->wBitsPerSample/8){
1099 WARN("Fixing bad nBlockAlign (%u)\n", device->orig_fmt->nBlockAlign);
1100 device->orig_fmt->nBlockAlign = device->orig_fmt->nChannels * device->orig_fmt->wBitsPerSample/8;
1102 if (device->orig_fmt->nAvgBytesPerSec != device->orig_fmt->nSamplesPerSec * device->orig_fmt->nBlockAlign) {
1103 WARN("Fixing bad nAvgBytesPerSec (%u)\n", device->orig_fmt->nAvgBytesPerSec);
1104 device->orig_fmt->nAvgBytesPerSec = device->orig_fmt->nSamplesPerSec * device->orig_fmt->nBlockAlign;
1106 }else{
1107 device->orig_fmt = HeapAlloc(GetProcessHeap(), 0,
1108 sizeof(WAVEFORMATEX) + info->format->cbSize);
1109 memcpy(device->orig_fmt, info->format,
1110 sizeof(WAVEFORMATEX) + info->format->cbSize);
1113 if(info->flags & WAVE_FORMAT_QUERY){
1114 WAVEFORMATEX *closer_fmt = NULL;
1116 hr = IAudioClient_IsFormatSupported(device->client,
1117 AUDCLNT_SHAREMODE_SHARED, device->orig_fmt, &closer_fmt);
1118 if(closer_fmt)
1119 CoTaskMemFree(closer_fmt);
1120 if((hr == S_FALSE || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && !(info->flags & WAVE_FORMAT_DIRECT))
1121 ret = WINMM_MapDevice(device, TRUE, is_out);
1122 else
1123 ret = hr == S_FALSE ? WAVERR_BADFORMAT : hr2mmr(hr);
1124 goto error;
1127 hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED,
1128 AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
1129 AC_BUFLEN, 0, device->orig_fmt, &device->parent->session);
1130 if(FAILED(hr)){
1131 if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT && !(info->flags & WAVE_FORMAT_DIRECT)){
1132 ret = WINMM_MapDevice(device, FALSE, is_out);
1133 if(ret != MMSYSERR_NOERROR || info->flags & WAVE_FORMAT_QUERY)
1134 goto error;
1135 }else{
1136 WARN("Initialize failed: %08x\n", hr);
1137 ret = hr2mmr(hr);
1138 goto error;
1140 }else{
1141 device->bytes_per_frame = device->orig_fmt->nBlockAlign;
1142 device->samples_per_sec = device->orig_fmt->nSamplesPerSec;
1145 hr = IAudioClient_GetService(device->client, &IID_IAudioClock,
1146 (void**)&device->clock);
1147 if(FAILED(hr)){
1148 WARN("GetService failed: %08x\n", hr);
1149 goto error;
1152 if(!device->event){
1153 device->event = CreateEventW(NULL, FALSE, FALSE, NULL);
1154 if(!device->event){
1155 WARN("CreateEvent failed: %08x\n", hr);
1156 goto error;
1159 /* As the devices thread is waiting on g_device_handles, it can
1160 * only be modified from within this same thread. */
1161 if(g_device_handles){
1162 g_device_handles = HeapReAlloc(GetProcessHeap(), 0, g_device_handles,
1163 sizeof(HANDLE) * (g_devhandle_count + 1));
1164 g_handle_devices = HeapReAlloc(GetProcessHeap(), 0, g_handle_devices,
1165 sizeof(WINMM_Device *) * (g_devhandle_count + 1));
1166 }else{
1167 g_device_handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE));
1168 g_handle_devices = HeapAlloc(GetProcessHeap(), 0,
1169 sizeof(WINMM_Device *));
1171 g_device_handles[g_devhandle_count] = device->event;
1172 g_handle_devices[g_devhandle_count] = device;
1173 ++g_devhandle_count;
1176 hr = IAudioClient_SetEventHandle(device->client, device->event);
1177 if(FAILED(hr)){
1178 WARN("SetEventHandle failed: %08x\n", hr);
1179 goto error;
1182 if(info->reset){
1183 device->played_frames = 0;
1184 device->ofs_bytes = 0;
1185 device->loop_counter = 0;
1186 device->first = device->last = device->playing = device->loop_start = NULL;
1189 device->stopped = TRUE;
1190 device->last_clock_pos = 0;
1192 device->cb_info.flags = HIWORD(info->flags & CALLBACK_TYPEMASK);
1193 device->cb_info.callback = info->callback;
1194 device->cb_info.user = info->cb_user;
1195 device->cb_info.hwave = device->handle;
1197 info->handle = device->handle;
1199 return MMSYSERR_NOERROR;
1201 error:
1202 if(device->client){
1203 IAudioClient_Release(device->client);
1204 device->client = NULL;
1206 if(device->device){
1207 IMMDevice_Release(device->device);
1208 device->device = NULL;
1211 return ret;
1214 static LRESULT WOD_Open(WINMM_OpenInfo *info)
1216 WINMM_Device *device;
1217 LRESULT ret = MMSYSERR_ERROR;
1218 HRESULT hr;
1220 if(info->handle != 0){
1221 device = WINMM_GetDeviceFromHWAVE(info->handle);
1222 if(!device){
1223 WARN("Unexpected! Invalid info->handle given: %p\n", info->handle);
1224 return MMSYSERR_ERROR;
1227 EnterCriticalSection(&device->lock);
1229 device->open = TRUE;
1230 }else{
1231 CRITICAL_SECTION *lock;
1232 UINT internal_index;
1233 WINMM_Device **devices;
1234 WINMM_MMDevice *mmdevice;
1236 if(WINMM_IsMapper(info->req_device)){
1237 if (g_outmmdevices_count == 0)
1238 return MMSYSERR_BADDEVICEID;
1239 devices = g_out_mapper_devices;
1240 mmdevice = read_map(g_out_map, 0);
1241 lock = &g_devthread_lock;
1242 internal_index = MAPPER_INDEX;
1243 }else{
1244 if(info->req_device >= g_outmmdevices_count)
1245 return MMSYSERR_BADDEVICEID;
1247 mmdevice = read_map(g_out_map, info->req_device);
1249 if(!mmdevice->out_caps.szPname[0])
1250 return MMSYSERR_NOTENABLED;
1252 devices = mmdevice->devices;
1253 lock = &mmdevice->lock;
1254 internal_index = mmdevice->index;
1257 EnterCriticalSection(lock);
1259 device = WINMM_FindUnusedDevice(devices, mmdevice,
1260 internal_index, TRUE);
1261 if(!device){
1262 LeaveCriticalSection(lock);
1263 return MMSYSERR_ALLOCATED;
1266 LeaveCriticalSection(lock);
1269 ret = WINMM_OpenDevice(device, info, TRUE);
1270 if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
1271 goto error;
1272 ret = MMSYSERR_ERROR;
1274 hr = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
1275 (void**)&device->render);
1276 if(FAILED(hr)){
1277 ERR("GetService failed: %08x\n", hr);
1278 goto error;
1281 hr = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
1282 (void**)&device->volume);
1283 if(FAILED(hr)){
1284 ERR("GetService failed: %08x\n", hr);
1285 goto error;
1288 LeaveCriticalSection(&device->lock);
1290 return MMSYSERR_NOERROR;
1292 error:
1293 if(device->device){
1294 IMMDevice_Release(device->device);
1295 device->device = NULL;
1297 if(device->client){
1298 IAudioClient_Release(device->client);
1299 device->client = NULL;
1301 if(device->render){
1302 IAudioRenderClient_Release(device->render);
1303 device->render = NULL;
1305 if(device->volume){
1306 IAudioStreamVolume_Release(device->volume);
1307 device->volume = NULL;
1309 if(device->clock){
1310 IAudioClock_Release(device->clock);
1311 device->clock = NULL;
1313 device->open = FALSE;
1314 LeaveCriticalSection(&device->lock);
1315 return ret;
1318 static LRESULT WID_Open(WINMM_OpenInfo *info)
1320 WINMM_Device *device, **devices;
1321 WINMM_MMDevice *mmdevice;
1322 UINT internal_index;
1323 CRITICAL_SECTION *lock;
1324 LRESULT ret = MMSYSERR_ERROR;
1325 HRESULT hr;
1327 if(WINMM_IsMapper(info->req_device)){
1328 if (g_inmmdevices_count == 0)
1329 return MMSYSERR_BADDEVICEID;
1330 devices = g_in_mapper_devices;
1331 mmdevice = read_map(g_in_map, 0);
1332 lock = &g_devthread_lock;
1333 internal_index = MAPPER_INDEX;
1334 }else{
1335 if(info->req_device >= g_inmmdevices_count)
1336 return MMSYSERR_BADDEVICEID;
1338 mmdevice = read_map(g_in_map, info->req_device);
1340 if(!mmdevice->in_caps.szPname[0])
1341 return MMSYSERR_NOTENABLED;
1343 devices = mmdevice->devices;
1344 lock = &mmdevice->lock;
1345 internal_index = mmdevice->index;
1348 EnterCriticalSection(lock);
1350 device = WINMM_FindUnusedDevice(devices, mmdevice, internal_index, FALSE);
1351 if(!device){
1352 LeaveCriticalSection(lock);
1353 return MMSYSERR_ALLOCATED;
1356 LeaveCriticalSection(lock);
1358 ret = WINMM_OpenDevice(device, info, FALSE);
1359 if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
1360 goto error;
1361 ret = MMSYSERR_ERROR;
1363 hr = IAudioClient_GetService(device->client, &IID_IAudioCaptureClient,
1364 (void**)&device->capture);
1365 if(FAILED(hr)){
1366 WARN("GetService failed: %08x\n", hr);
1367 goto error;
1370 LeaveCriticalSection(&device->lock);
1372 return MMSYSERR_NOERROR;
1374 error:
1375 if(device->device){
1376 IMMDevice_Release(device->device);
1377 device->device = NULL;
1379 if(device->client){
1380 IAudioClient_Release(device->client);
1381 device->client = NULL;
1383 if(device->capture){
1384 IAudioCaptureClient_Release(device->capture);
1385 device->capture = NULL;
1387 if(device->clock){
1388 IAudioClock_Release(device->clock);
1389 device->clock = NULL;
1391 device->open = FALSE;
1392 LeaveCriticalSection(&device->lock);
1393 return ret;
1396 static HRESULT WINMM_CloseDevice(WINMM_Device *device)
1398 device->open = FALSE;
1400 if(!device->stopped){
1401 IAudioClient_Stop(device->client);
1402 device->stopped = TRUE;
1405 if(device->acm_handle){
1406 acmStreamClose(device->acm_handle, 0);
1407 device->acm_handle = NULL;
1410 IMMDevice_Release(device->device);
1411 device->device = NULL;
1413 IAudioClient_Release(device->client);
1414 device->client = NULL;
1416 IAudioClock_Release(device->clock);
1417 device->clock = NULL;
1419 return S_OK;
1422 static LRESULT WOD_Close(HWAVEOUT hwave)
1424 WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1426 TRACE("(%p)\n", hwave);
1428 if(!WINMM_ValidateAndLock(device))
1429 return MMSYSERR_INVALHANDLE;
1431 WINMM_CloseDevice(device);
1433 IAudioRenderClient_Release(device->render);
1434 device->render = NULL;
1436 IAudioStreamVolume_Release(device->volume);
1437 device->volume = NULL;
1439 LeaveCriticalSection(&device->lock);
1441 return MMSYSERR_NOERROR;
1444 static LRESULT WID_Close(HWAVEIN hwave)
1446 WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
1448 TRACE("(%p)\n", hwave);
1450 if(!WINMM_ValidateAndLock(device))
1451 return MMSYSERR_INVALHANDLE;
1453 WINMM_CloseDevice(device);
1455 IAudioCaptureClient_Release(device->capture);
1456 device->capture = NULL;
1458 LeaveCriticalSection(&device->lock);
1460 return MMSYSERR_NOERROR;
1463 static DWORD WINMM_FixedBufferLen(DWORD length, WINMM_Device *device)
1465 return length - length % device->bytes_per_frame;
1468 static LRESULT WINMM_PrepareHeader(HWAVE hwave, WAVEHDR *header)
1470 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1472 TRACE("(%p, %p)\n", hwave, header);
1474 if(!WINMM_ValidateAndLock(device))
1475 return MMSYSERR_INVALHANDLE;
1477 if(device->render && device->acm_handle){
1478 ACMSTREAMHEADER *ash;
1479 DWORD size;
1480 MMRESULT mr;
1482 mr = acmStreamSize(device->acm_handle, header->dwBufferLength, &size,
1483 ACM_STREAMSIZEF_SOURCE);
1484 if(mr != MMSYSERR_NOERROR){
1485 LeaveCriticalSection(&device->lock);
1486 return mr;
1489 ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + size);
1490 if(!ash){
1491 LeaveCriticalSection(&device->lock);
1492 return MMSYSERR_NOMEM;
1495 ash->cbStruct = sizeof(*ash);
1496 ash->fdwStatus = 0;
1497 ash->dwUser = (DWORD_PTR)header;
1498 ash->pbSrc = (BYTE*)header->lpData;
1499 ash->cbSrcLength = header->dwBufferLength;
1500 ash->dwSrcUser = header->dwUser;
1501 ash->pbDst = (BYTE*)ash + sizeof(ACMSTREAMHEADER);
1502 ash->cbDstLength = size;
1503 ash->dwDstUser = 0;
1505 mr = acmStreamPrepareHeader(device->acm_handle, ash, 0);
1506 if(mr != MMSYSERR_NOERROR){
1507 HeapFree(GetProcessHeap(), 0, ash);
1508 LeaveCriticalSection(&device->lock);
1509 return mr;
1512 header->reserved = (DWORD_PTR)ash;
1515 LeaveCriticalSection(&device->lock);
1517 header->dwFlags |= WHDR_PREPARED;
1518 header->dwFlags &= ~(WHDR_DONE|WHDR_INQUEUE); /* flags cleared since w2k */
1520 return MMSYSERR_NOERROR;
1523 static LRESULT WINMM_UnprepareHeader(HWAVE hwave, WAVEHDR *header)
1525 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1527 TRACE("(%p, %p)\n", hwave, header);
1529 if(!WINMM_ValidateAndLock(device))
1530 return MMSYSERR_INVALHANDLE;
1532 if(device->render && device->acm_handle){
1533 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1535 acmStreamUnprepareHeader(device->acm_handle, ash, 0);
1537 HeapFree(GetProcessHeap(), 0, ash);
1540 LeaveCriticalSection(&device->lock);
1542 header->dwFlags &= ~WHDR_PREPARED;
1544 return MMSYSERR_NOERROR;
1547 static UINT32 WINMM_HeaderLenBytes(WINMM_Device *device, WAVEHDR *header)
1549 if(device->acm_handle){
1550 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
1551 return WINMM_FixedBufferLen(ash->cbDstLengthUsed, device);
1554 return WINMM_FixedBufferLen(header->dwBufferLength, device);
1557 static UINT32 WINMM_HeaderLenFrames(WINMM_Device *device, WAVEHDR *header)
1559 return WINMM_HeaderLenBytes(device, header) / device->bytes_per_frame;
1562 static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device)
1564 HRESULT hr;
1565 WAVEHDR *first = device->first, *queue = first, *last = NULL;
1566 UINT64 clock_freq, clock_pos, clock_frames;
1567 UINT32 nloops, queue_frames = 0;
1569 hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
1570 if(FAILED(hr)){
1571 WARN("GetFrequency failed: %08x\n", hr);
1572 return NULL;
1575 hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
1576 if(FAILED(hr)){
1577 WARN("GetPosition failed: %08x\n", hr);
1578 return NULL;
1581 clock_frames = (clock_pos * device->samples_per_sec) / clock_freq;
1583 nloops = device->loop_counter;
1584 while(queue &&
1585 (queue_frames += WINMM_HeaderLenFrames(device, queue)) <=
1586 clock_frames - device->last_clock_pos + device->remainder_frames){
1587 if(!nloops){
1588 last = queue;
1589 device->last_clock_pos += queue_frames;
1590 device->remainder_frames = 0;
1591 queue_frames = 0;
1592 queue = device->first = queue->lpNext;
1593 }else{
1594 if(queue->dwFlags & WHDR_BEGINLOOP){
1595 if(queue->dwFlags & WHDR_ENDLOOP)
1596 --nloops;
1597 else
1598 queue = queue->lpNext;
1599 }else if(queue->dwFlags & WHDR_ENDLOOP){
1600 queue = device->loop_start;
1601 --nloops;
1606 if(last){
1607 last->lpNext = NULL;
1608 return first;
1609 }else
1610 return NULL;
1613 static void WOD_PushData(WINMM_Device *device)
1615 WINMM_CBInfo cb_info;
1616 HRESULT hr;
1617 UINT32 pad, bufsize, avail_frames, queue_frames, written, ofs;
1618 UINT32 queue_bytes, nloops;
1619 BYTE *data;
1620 WAVEHDR *queue, *first = NULL;
1622 TRACE("(%p)\n", device->handle);
1624 EnterCriticalSection(&device->lock);
1626 if(!device->device)
1627 goto exit;
1629 if(!device->first){
1630 if (device->stopped)
1631 goto exit;
1632 device->stopped = TRUE;
1633 device->last_clock_pos = 0;
1634 IAudioClient_Stop(device->client);
1635 IAudioClient_Reset(device->client);
1636 goto exit;
1639 hr = IAudioClient_GetBufferSize(device->client, &bufsize);
1640 if(FAILED(hr)){
1641 WARN("GetBufferSize failed: %08x\n", hr);
1642 goto exit;
1645 hr = IAudioClient_GetCurrentPadding(device->client, &pad);
1646 if(FAILED(hr)){
1647 WARN("GetCurrentPadding failed: %08x\n", hr);
1648 goto exit;
1651 first = WOD_MarkDoneHeaders(device);
1653 /* determine which is larger between the available buffer size and
1654 * the amount of data left in the queue */
1655 avail_frames = bufsize - pad;
1657 queue = device->playing;
1658 ofs = device->ofs_bytes;
1659 queue_frames = 0;
1660 nloops = 0;
1661 while(queue && queue_frames < avail_frames){
1662 queue_bytes = WINMM_HeaderLenBytes(device, queue);
1663 queue_frames += (queue_bytes - ofs) / device->bytes_per_frame;
1664 ofs = 0;
1666 if(queue->dwFlags & WHDR_ENDLOOP && nloops < device->loop_counter){
1667 queue = device->loop_start;
1668 ++nloops;
1669 }else
1670 queue = queue->lpNext;
1673 if(queue_frames < avail_frames)
1674 avail_frames = queue_frames;
1675 if(avail_frames == 0)
1676 goto exit;
1678 hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
1679 if(FAILED(hr)){
1680 WARN("GetBuffer failed: %08x\n", hr);
1681 goto exit;
1684 written = 0;
1685 while(device->playing && written < avail_frames){
1686 UINT32 copy_frames, copy_bytes;
1687 BYTE *queue_data;
1689 queue = device->playing;
1691 queue_bytes = WINMM_HeaderLenBytes(device, queue);
1692 if(device->acm_handle)
1693 queue_data = ((ACMSTREAMHEADER*)queue->reserved)->pbDst;
1694 else
1695 queue_data = (BYTE*)queue->lpData;
1697 queue_frames = (queue_bytes - device->ofs_bytes) /
1698 device->bytes_per_frame;
1700 copy_frames = queue_frames < (avail_frames - written) ?
1701 queue_frames : avail_frames - written;
1702 copy_bytes = copy_frames * device->bytes_per_frame;
1704 memcpy(data, queue_data + device->ofs_bytes, copy_bytes);
1706 data += copy_bytes;
1707 written += copy_frames;
1708 device->ofs_bytes += copy_bytes;
1710 if(device->ofs_bytes >= queue_bytes){
1711 device->ofs_bytes = 0;
1713 if(!(queue->dwFlags & (WHDR_BEGINLOOP | WHDR_ENDLOOP)))
1714 device->playing = queue->lpNext;
1715 else{
1716 if(queue->dwFlags & WHDR_BEGINLOOP){
1717 device->loop_start = device->playing;
1718 device->playing = queue->lpNext;
1719 device->loop_counter = queue->dwLoops;
1721 if(queue->dwFlags & WHDR_ENDLOOP){
1722 --device->loop_counter;
1723 if(device->loop_counter)
1724 device->playing = device->loop_start;
1725 else
1726 device->loop_start = device->playing = queue->lpNext;
1732 hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames, 0);
1733 if(FAILED(hr)){
1734 WARN("ReleaseBuffer failed: %08x\n", hr);
1735 goto exit;
1738 if(device->orig_fmt->nSamplesPerSec != device->samples_per_sec)
1739 device->played_frames += MulDiv(avail_frames, device->orig_fmt->nSamplesPerSec, device->samples_per_sec);
1740 else
1741 device->played_frames += avail_frames;
1743 exit:
1744 cb_info = device->cb_info;
1746 LeaveCriticalSection(&device->lock);
1748 while(first){
1749 WAVEHDR *next = first->lpNext;
1750 first->dwFlags &= ~WHDR_INQUEUE;
1751 first->dwFlags |= WHDR_DONE;
1752 WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
1753 first = next;
1757 static void WID_PullACMData(WINMM_Device *device)
1759 UINT32 packet, packet_bytes;
1760 DWORD flags;
1761 BYTE *data;
1762 WAVEHDR *queue;
1763 HRESULT hr;
1764 MMRESULT mr;
1766 if(device->acm_hdr.cbDstLength == 0){
1767 hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
1768 &flags, NULL, NULL);
1769 if(hr != S_OK){
1770 if(FAILED(hr))
1771 WARN("GetBuffer failed: %08x\n", hr);
1772 return;
1775 acmStreamSize(device->acm_handle, packet * device->bytes_per_frame,
1776 &packet_bytes, ACM_STREAMSIZEF_SOURCE);
1778 device->acm_offs = 0;
1780 device->acm_hdr.cbStruct = sizeof(device->acm_hdr);
1781 device->acm_hdr.fdwStatus = 0;
1782 device->acm_hdr.dwUser = 0;
1783 device->acm_hdr.pbSrc = data;
1784 device->acm_hdr.cbSrcLength = packet * device->bytes_per_frame;
1785 device->acm_hdr.cbSrcLengthUsed = 0;
1786 device->acm_hdr.dwSrcUser = 0;
1787 device->acm_hdr.pbDst = HeapAlloc(GetProcessHeap(), 0, packet_bytes);
1788 device->acm_hdr.cbDstLength = packet_bytes;
1789 device->acm_hdr.cbDstLengthUsed = 0;
1790 device->acm_hdr.dwDstUser = 0;
1792 mr = acmStreamPrepareHeader(device->acm_handle, &device->acm_hdr, 0);
1793 if(mr != MMSYSERR_NOERROR){
1794 WARN("acmStreamPrepareHeader failed: %d\n", mr);
1795 return;
1798 mr = acmStreamConvert(device->acm_handle, &device->acm_hdr, 0);
1799 if(mr != MMSYSERR_NOERROR){
1800 WARN("acmStreamConvert failed: %d\n", mr);
1801 return;
1804 hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet);
1805 if(FAILED(hr))
1806 WARN("ReleaseBuffer failed: %08x\n", hr);
1808 device->played_frames += packet;
1811 queue = device->first;
1812 while(queue){
1813 UINT32 to_copy_bytes;
1815 to_copy_bytes = min(WINMM_FixedBufferLen(queue->dwBufferLength, device) - queue->dwBytesRecorded,
1816 WINMM_FixedBufferLen(device->acm_hdr.cbDstLengthUsed, device) - device->acm_offs);
1818 memcpy(queue->lpData + queue->dwBytesRecorded,
1819 device->acm_hdr.pbDst + device->acm_offs, to_copy_bytes);
1821 queue->dwBytesRecorded += to_copy_bytes;
1822 device->acm_offs += to_copy_bytes;
1824 if(queue->dwBufferLength - queue->dwBytesRecorded <
1825 device->bytes_per_frame){
1826 queue->dwFlags &= ~WHDR_INQUEUE;
1827 queue->dwFlags |= WHDR_DONE;
1828 device->first = queue = queue->lpNext;
1831 if(device->acm_offs >= WINMM_FixedBufferLen(device->acm_hdr.cbDstLengthUsed, device)){
1832 acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1833 HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1834 device->acm_hdr.cbDstLength = 0;
1835 device->acm_hdr.cbDstLengthUsed = 0;
1837 /* done with this ACM Header, so try to pull more data */
1838 WID_PullACMData(device);
1839 return;
1843 /* out of WAVEHDRs to write into, so toss the rest of this packet */
1844 acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
1845 HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
1846 device->acm_hdr.cbDstLength = 0;
1847 device->acm_hdr.cbDstLengthUsed = 0;
1850 static void WID_PullData(WINMM_Device *device)
1852 WINMM_CBInfo cb_info;
1853 WAVEHDR *queue, *first = NULL, *last = NULL;
1854 HRESULT hr;
1856 TRACE("(%p)\n", device->handle);
1858 EnterCriticalSection(&device->lock);
1860 if(!device->device || !device->first)
1861 goto exit;
1863 first = device->first;
1865 if(device->acm_handle){
1866 WID_PullACMData(device);
1867 goto exit;
1870 while(device->first){
1871 BYTE *data;
1872 UINT32 packet_len, packet;
1873 DWORD flags;
1875 hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet_len,
1876 &flags, NULL, NULL);
1877 if(hr != S_OK){
1878 if(FAILED(hr))
1879 WARN("GetBuffer failed: %08x\n", hr);
1880 else /* AUDCLNT_S_BUFFER_EMPTY success code */
1881 IAudioCaptureClient_ReleaseBuffer(device->capture, 0);
1882 goto exit;
1885 packet = packet_len;
1886 queue = device->first;
1887 while(queue && packet > 0){
1888 UINT32 to_copy_bytes;
1890 to_copy_bytes = min(packet * device->bytes_per_frame,
1891 WINMM_FixedBufferLen(queue->dwBufferLength, device) - queue->dwBytesRecorded);
1893 memcpy(queue->lpData + queue->dwBytesRecorded,
1894 data + (packet_len - packet) * device->bytes_per_frame,
1895 to_copy_bytes);
1897 queue->dwBytesRecorded += to_copy_bytes;
1899 if(queue->dwBufferLength - queue->dwBytesRecorded <
1900 device->bytes_per_frame){
1901 last = queue;
1902 device->first = queue = queue->lpNext;
1905 packet -= to_copy_bytes / device->bytes_per_frame;
1908 hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet_len);
1909 if(FAILED(hr))
1910 WARN("ReleaseBuffer failed: %08x\n", hr);
1912 if(packet > 0)
1913 WARN("losing %u frames\n", packet);
1914 device->played_frames += packet_len - packet;
1917 exit:
1918 cb_info = device->cb_info;
1920 LeaveCriticalSection(&device->lock);
1922 if(last){
1923 last->lpNext = NULL;
1924 while(first){
1925 WAVEHDR *next = first->lpNext;
1926 first->dwFlags &= ~WHDR_INQUEUE;
1927 first->dwFlags |= WHDR_DONE;
1928 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
1929 first = next;
1934 static MMRESULT WINMM_BeginPlaying(WINMM_Device *device)
1936 HRESULT hr;
1938 TRACE("(%p)\n", device->handle);
1940 if(device->render)
1941 /* prebuffer data before starting */
1942 WOD_PushData(device);
1944 if(device->stopped){
1945 device->stopped = FALSE;
1947 hr = IAudioClient_Start(device->client);
1948 if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){
1949 device->stopped = TRUE;
1950 WARN("Start failed: %08x\n", hr);
1951 return MMSYSERR_ERROR;
1955 return MMSYSERR_NOERROR;
1958 static LRESULT WINMM_Pause(HWAVE hwave)
1960 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1961 HRESULT hr;
1963 TRACE("(%p)\n", hwave);
1965 if(!WINMM_ValidateAndLock(device))
1966 return MMSYSERR_INVALHANDLE;
1968 hr = IAudioClient_Stop(device->client);
1969 if(FAILED(hr)){
1970 LeaveCriticalSection(&device->lock);
1971 WARN("Stop failed: %08x\n", hr);
1972 return MMSYSERR_ERROR;
1975 device->stopped = FALSE;
1977 LeaveCriticalSection(&device->lock);
1979 return MMSYSERR_NOERROR;
1982 static LRESULT WINMM_Reset(HWAVE hwave)
1984 WINMM_CBInfo cb_info;
1985 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
1986 BOOL is_out;
1987 WAVEHDR *first;
1988 HRESULT hr;
1990 TRACE("(%p)\n", hwave);
1992 if(!WINMM_ValidateAndLock(device))
1993 return MMSYSERR_INVALHANDLE;
1995 hr = IAudioClient_Stop(device->client);
1996 if(FAILED(hr)){
1997 LeaveCriticalSection(&device->lock);
1998 WARN("Stop failed: %08x\n", hr);
1999 return MMSYSERR_ERROR;
2001 device->stopped = TRUE;
2003 first = device->first;
2004 device->first = device->last = device->playing = NULL;
2005 device->ofs_bytes = 0;
2006 device->played_frames = 0;
2007 device->loop_counter = 0;
2008 device->last_clock_pos = 0;
2009 IAudioClient_Reset(device->client);
2011 cb_info = device->cb_info;
2012 is_out = device->render != NULL;
2014 LeaveCriticalSection(&device->lock);
2016 while(first){
2017 WAVEHDR *next = first->lpNext;
2018 first->dwFlags &= ~WHDR_INQUEUE;
2019 first->dwFlags |= WHDR_DONE;
2020 if(is_out)
2021 WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
2022 else
2023 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
2024 first = next;
2027 return MMSYSERR_NOERROR;
2030 static MMRESULT WINMM_FramesToMMTime(MMTIME *time, UINT32 played_frames,
2031 UINT32 sample_rate, UINT32 bytes_per_sec)
2033 switch(time->wType){
2034 case TIME_SAMPLES:
2035 time->u.sample = played_frames;
2036 return MMSYSERR_NOERROR;
2037 case TIME_MS:
2038 time->u.ms = (UINT64)played_frames * 1000 / sample_rate;
2039 return MMSYSERR_NOERROR;
2040 case TIME_SMPTE:
2041 time->u.smpte.fps = 30;
2042 played_frames += sample_rate / time->u.smpte.fps - 1; /* round up */
2043 time->u.smpte.frame = (played_frames % sample_rate) * time->u.smpte.fps / sample_rate;
2044 played_frames /= sample_rate; /* yields seconds */
2045 time->u.smpte.sec = played_frames % 60;
2046 played_frames /= 60;
2047 time->u.smpte.min = played_frames % 60;
2048 time->u.smpte.hour= played_frames / 60;
2049 return MMSYSERR_NOERROR;
2050 default:
2051 time->wType = TIME_BYTES;
2052 /* fall through */
2053 case TIME_BYTES:
2054 time->u.cb = MulDiv(played_frames, bytes_per_sec, sample_rate);
2055 return MMSYSERR_NOERROR;
2059 static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time)
2061 WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
2062 UINT32 played_frames, sample_rate, bytes_per_sec;
2064 TRACE("(%p, %p)\n", hwave, time);
2066 if(!WINMM_ValidateAndLock(device))
2067 return MMSYSERR_INVALHANDLE;
2069 played_frames = device->played_frames;
2070 sample_rate = device->orig_fmt->nSamplesPerSec;
2071 bytes_per_sec = device->orig_fmt->nAvgBytesPerSec;
2073 LeaveCriticalSection(&device->lock);
2075 return WINMM_FramesToMMTime(time, played_frames, sample_rate, bytes_per_sec);
2078 static WINMM_MMDevice *WINMM_GetMixerMMDevice(HMIXEROBJ hmix, DWORD flags,
2079 UINT *mmdev_index)
2081 UINT mmdev, dev, junk, *out;
2082 BOOL is_out;
2084 if(!mmdev_index)
2085 out = &mmdev;
2086 else
2087 out = mmdev_index;
2089 switch(flags & 0xF0000000){
2090 case MIXER_OBJECTF_MIXER: /* == 0 */
2091 *out = HandleToULong(hmix);
2092 if(*out < g_outmmdevices_count)
2093 return read_map(g_out_map, *out);
2094 if(*out - g_outmmdevices_count < g_inmmdevices_count){
2095 *out -= g_outmmdevices_count;
2096 return read_map(g_in_map, *out);
2098 /* fall through -- if it's not a valid mixer device, then
2099 * it could be a valid mixer handle. windows seems to do
2100 * this as well. */
2101 case MIXER_OBJECTF_HMIXER:
2102 case MIXER_OBJECTF_HWAVEOUT:
2103 case MIXER_OBJECTF_HWAVEIN:
2104 WINMM_DecomposeHWAVE((HWAVE)hmix, out, &is_out, &dev, &junk);
2105 if(junk != 0x1 || (is_out && *out >= g_outmmdevices_count) ||
2106 (!is_out && *out >= g_inmmdevices_count))
2107 return NULL;
2108 if(is_out)
2109 return read_map(g_out_map, *out);
2110 return read_map(g_in_map, *out);
2111 case MIXER_OBJECTF_WAVEOUT:
2112 *out = HandleToULong(hmix);
2113 if(*out < g_outmmdevices_count)
2114 return read_map(g_out_map, *out);
2115 return NULL;
2116 case MIXER_OBJECTF_WAVEIN:
2117 *out = HandleToULong(hmix);
2118 if(*out < g_inmmdevices_count)
2119 return read_map(g_in_map, *out);
2120 return NULL;
2123 return NULL;
2126 static MMRESULT WINMM_SetupMMDeviceVolume(WINMM_MMDevice *mmdevice)
2128 IAudioSessionManager *sesman;
2129 IMMDevice *device;
2130 HRESULT hr;
2132 hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id, &device);
2133 if(FAILED(hr)){
2134 WARN("Device %s (%s) unavailable: %08x\n",
2135 wine_dbgstr_w(mmdevice->dev_id),
2136 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
2137 return MMSYSERR_ERROR;
2140 hr = IMMDevice_Activate(device, &IID_IAudioSessionManager,
2141 CLSCTX_INPROC_SERVER, NULL, (void**)&sesman);
2142 if(FAILED(hr)){
2143 WARN("Activate failed: %08x\n", hr);
2144 IMMDevice_Release(device);
2145 return MMSYSERR_ERROR;
2148 IMMDevice_Release(device);
2150 hr = IAudioSessionManager_GetSimpleAudioVolume(sesman, &mmdevice->session,
2151 FALSE, &mmdevice->volume);
2152 IAudioSessionManager_Release(sesman);
2153 if(FAILED(hr)){
2154 WARN("GetSimpleAudioVolume failed: %08x\n", hr);
2155 return MMSYSERR_ERROR;
2158 return MMSYSERR_NOERROR;
2161 static LRESULT MXD_GetControlDetails(WINMM_ControlDetails *details)
2163 WINMM_MMDevice *mmdevice;
2164 MIXERCONTROLDETAILS *control = details->details;
2165 HRESULT hr;
2167 TRACE("(%p)\n", details->hmix);
2169 mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
2170 if(!mmdevice)
2171 return MMSYSERR_INVALHANDLE;
2173 EnterCriticalSection(&mmdevice->lock);
2175 if(!mmdevice->volume){
2176 MMRESULT mr;
2178 mr = WINMM_SetupMMDeviceVolume(mmdevice);
2179 if(mr != MMSYSERR_NOERROR){
2180 LeaveCriticalSection(&mmdevice->lock);
2181 return mr;
2185 if(control->dwControlID == 0){
2186 float vol;
2187 MIXERCONTROLDETAILS_UNSIGNED *udet;
2189 if(!control->paDetails ||
2190 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
2191 LeaveCriticalSection(&mmdevice->lock);
2192 return MMSYSERR_INVALPARAM;
2195 hr = ISimpleAudioVolume_GetMasterVolume(mmdevice->volume, &vol);
2196 if(FAILED(hr)){
2197 WARN("GetMasterVolume failed: %08x\n", hr);
2198 LeaveCriticalSection(&mmdevice->lock);
2199 return MMSYSERR_ERROR;
2202 udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
2203 udet->dwValue = vol * ((unsigned int)0xFFFF);
2204 }else if(control->dwControlID == 1){
2205 BOOL mute;
2206 MIXERCONTROLDETAILS_BOOLEAN *bdet;
2208 if(!control->paDetails ||
2209 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
2210 LeaveCriticalSection(&mmdevice->lock);
2211 return MMSYSERR_INVALPARAM;
2214 hr = ISimpleAudioVolume_GetMute(mmdevice->volume, &mute);
2215 if(FAILED(hr)){
2216 WARN("GetMute failed: %08x\n", hr);
2217 LeaveCriticalSection(&mmdevice->lock);
2218 return MMSYSERR_ERROR;
2221 bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
2222 bdet->fValue = mute;
2223 }else if(control->dwControlID == 2 || control->dwControlID == 3){
2224 FIXME("What should the sw-side mixer controls map to?\n");
2225 }else{
2226 LeaveCriticalSection(&mmdevice->lock);
2227 return MIXERR_INVALCONTROL;
2230 LeaveCriticalSection(&mmdevice->lock);
2232 return MMSYSERR_NOERROR;
2235 static LRESULT MXD_SetControlDetails(WINMM_ControlDetails *details)
2237 WINMM_MMDevice *mmdevice;
2238 MIXERCONTROLDETAILS *control = details->details;
2239 HRESULT hr;
2241 TRACE("(%p)\n", details->hmix);
2243 mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
2244 if(!mmdevice)
2245 return MMSYSERR_INVALHANDLE;
2247 EnterCriticalSection(&mmdevice->lock);
2249 if(!mmdevice->volume){
2250 MMRESULT mr;
2252 mr = WINMM_SetupMMDeviceVolume(mmdevice);
2253 if(mr != MMSYSERR_NOERROR){
2254 LeaveCriticalSection(&mmdevice->lock);
2255 return mr;
2259 if(control->dwControlID == 0){
2260 float vol;
2261 MIXERCONTROLDETAILS_UNSIGNED *udet;
2263 if(!control->paDetails ||
2264 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
2265 LeaveCriticalSection(&mmdevice->lock);
2266 return MMSYSERR_INVALPARAM;
2269 udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
2271 if(udet->dwValue > 65535){
2272 LeaveCriticalSection(&mmdevice->lock);
2273 return MMSYSERR_INVALPARAM;
2276 vol = udet->dwValue / 65535.f;
2278 hr = ISimpleAudioVolume_SetMasterVolume(mmdevice->volume, vol, NULL);
2279 if(FAILED(hr)){
2280 WARN("SetMasterVolume failed: %08x\n", hr);
2281 LeaveCriticalSection(&mmdevice->lock);
2282 return MMSYSERR_ERROR;
2284 }else if(control->dwControlID == 1){
2285 BOOL mute;
2286 MIXERCONTROLDETAILS_BOOLEAN *bdet;
2288 if(!control->paDetails ||
2289 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
2290 LeaveCriticalSection(&mmdevice->lock);
2291 return MMSYSERR_INVALPARAM;
2294 bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
2295 mute = bdet->fValue;
2297 hr = ISimpleAudioVolume_SetMute(mmdevice->volume, mute, NULL);
2298 if(FAILED(hr)){
2299 WARN("SetMute failed: %08x\n", hr);
2300 LeaveCriticalSection(&mmdevice->lock);
2301 return MMSYSERR_ERROR;
2303 }else if(control->dwControlID == 2 || control->dwControlID == 3){
2304 FIXME("What should the sw-side mixer controls map to?\n");
2305 }else{
2306 LeaveCriticalSection(&mmdevice->lock);
2307 return MIXERR_INVALCONTROL;
2310 LeaveCriticalSection(&mmdevice->lock);
2312 return MMSYSERR_NOERROR;
2315 static LRESULT DRV_QueryDeviceInterface(WINMM_QueryInterfaceInfo *info)
2317 WINMM_MMDevice *mmdevice;
2318 IMMDevice *device;
2319 IPropertyStore *ps;
2320 PROPVARIANT pv;
2321 DWORD len_bytes;
2322 HRESULT hr;
2324 static const PROPERTYKEY deviceinterface_key = {
2325 {0x233164c8, 0x1b2c, 0x4c7d, {0xbc, 0x68, 0xb6, 0x71, 0x68, 0x7a, 0x25, 0x67}}, 1
2328 if(WINMM_IsMapper(info->index)){
2329 if(info->str){
2330 if(*info->len_bytes < sizeof(WCHAR))
2331 return MMSYSERR_INVALPARAM;
2332 *info->str = 0;
2333 }else
2334 *info->len_bytes = sizeof(WCHAR);
2335 return MMSYSERR_NOERROR;
2338 if(info->is_out){
2339 if(info->index >= g_outmmdevices_count)
2340 return MMSYSERR_INVALHANDLE;
2342 mmdevice = &g_out_mmdevices[info->index];
2343 }else{
2344 if(info->index >= g_inmmdevices_count)
2345 return MMSYSERR_INVALHANDLE;
2347 mmdevice = &g_in_mmdevices[info->index];
2350 hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id,
2351 &device);
2352 if(FAILED(hr)){
2353 WARN("Device %s unavailable: %08x\n", wine_dbgstr_w(mmdevice->dev_id), hr);
2354 return MMSYSERR_ERROR;
2357 hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
2358 if(FAILED(hr)){
2359 WARN("OpenPropertyStore failed: %08x\n", hr);
2360 IMMDevice_Release(device);
2361 return MMSYSERR_ERROR;
2364 PropVariantInit(&pv);
2365 hr = IPropertyStore_GetValue(ps, &deviceinterface_key, &pv);
2366 if(FAILED(hr)){
2367 WARN("GetValue failed: %08x\n", hr);
2368 IPropertyStore_Release(ps);
2369 IMMDevice_Release(device);
2370 return MMSYSERR_ERROR;
2372 if(pv.vt != VT_LPWSTR){
2373 WARN("Got unexpected property type: %u\n", pv.vt);
2374 PropVariantClear(&pv);
2375 IPropertyStore_Release(ps);
2376 IMMDevice_Release(device);
2377 return MMSYSERR_ERROR;
2380 len_bytes = (lstrlenW(pv.u.pwszVal) + 1) * sizeof(WCHAR);
2382 if(info->str){
2383 if(len_bytes > *info->len_bytes){
2384 PropVariantClear(&pv);
2385 IPropertyStore_Release(ps);
2386 IMMDevice_Release(device);
2387 return MMSYSERR_INVALPARAM;
2390 memcpy(info->str, pv.u.pwszVal, len_bytes);
2391 }else
2392 *info->len_bytes = len_bytes;
2394 PropVariantClear(&pv);
2395 IPropertyStore_Release(ps);
2396 IMMDevice_Release(device);
2398 return MMSYSERR_NOERROR;
2401 static LRESULT CALLBACK WINMM_DevicesMsgProc(HWND hwnd, UINT msg, WPARAM wparam,
2402 LPARAM lparam)
2404 switch(msg){
2405 case WODM_OPEN:
2406 return WOD_Open((WINMM_OpenInfo*)wparam);
2407 case WODM_CLOSE:
2408 return WOD_Close((HWAVEOUT)wparam);
2409 case WIDM_OPEN:
2410 return WID_Open((WINMM_OpenInfo*)wparam);
2411 case WIDM_CLOSE:
2412 return WID_Close((HWAVEIN)wparam);
2413 case MXDM_GETCONTROLDETAILS:
2414 return MXD_GetControlDetails((WINMM_ControlDetails*)wparam);
2415 case MXDM_SETCONTROLDETAILS:
2416 return MXD_SetControlDetails((WINMM_ControlDetails*)wparam);
2417 case DRV_QUERYDEVICEINTERFACESIZE:
2418 case DRV_QUERYDEVICEINTERFACE:
2419 return DRV_QueryDeviceInterface((WINMM_QueryInterfaceInfo*)wparam);
2421 return DefWindowProcW(hwnd, msg, wparam, lparam);
2424 static BOOL WINMM_DevicesThreadDone(void)
2426 UINT i;
2428 EnterCriticalSection(&g_devthread_lock);
2430 if(g_devthread_token > 0){
2431 LeaveCriticalSection(&g_devthread_lock);
2432 return FALSE;
2435 for(i = 0; i < g_devhandle_count; ++i){
2436 if(g_handle_devices[i]->open){
2437 LeaveCriticalSection(&g_devthread_lock);
2438 return FALSE;
2442 DestroyWindow(g_devices_hwnd);
2443 g_devices_hwnd = NULL;
2444 IMMDeviceEnumerator_Release(g_devenum);
2445 g_devenum = NULL;
2446 CoUninitialize();
2448 LeaveCriticalSection(&g_devthread_lock);
2450 return TRUE;
2453 static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
2455 HANDLE evt = arg;
2456 HRESULT hr;
2457 static const WCHAR messageW[] = {'M','e','s','s','a','g','e',0};
2459 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
2460 if(FAILED(hr)){
2461 WARN("CoInitializeEx failed: %08x\n", hr);
2462 FreeLibraryAndExitThread(g_devthread_module, 1);
2465 hr = WINMM_InitMMDevices();
2466 if(FAILED(hr)){
2467 CoUninitialize();
2468 FreeLibraryAndExitThread(g_devthread_module, 1);
2471 hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
2472 CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&g_devenum);
2473 if(FAILED(hr)){
2474 WARN("CoCreateInstance failed: %08x\n", hr);
2475 CoUninitialize();
2476 FreeLibraryAndExitThread(g_devthread_module, 1);
2479 g_devices_hwnd = CreateWindowW(messageW, NULL, 0, 0, 0, 0, 0,
2480 HWND_MESSAGE, NULL, NULL, NULL);
2481 if(!g_devices_hwnd){
2482 WARN("CreateWindow failed: %d\n", GetLastError());
2483 CoUninitialize();
2484 FreeLibraryAndExitThread(g_devthread_module, 1);
2487 SetWindowLongPtrW(g_devices_hwnd, GWLP_WNDPROC,
2488 (LONG_PTR)WINMM_DevicesMsgProc);
2490 /* inform caller that the thread is ready to process messages */
2491 SetEvent(evt);
2492 evt = NULL; /* do not use after this point */
2494 while(1){
2495 DWORD wait;
2496 wait = MsgWaitForMultipleObjects(g_devhandle_count, g_device_handles,
2497 FALSE, INFINITE, QS_ALLINPUT);
2498 if(wait == g_devhandle_count + WAIT_OBJECT_0){
2499 MSG msg;
2500 if(PeekMessageW(&msg, g_devices_hwnd, 0, 0, PM_REMOVE))
2501 WARN("Unexpected message: 0x%x\n", msg.message);
2502 if(!g_devices_hwnd)
2503 break;
2504 }else if(wait < g_devhandle_count + WAIT_OBJECT_0){
2505 WINMM_Device *device = g_handle_devices[wait - WAIT_OBJECT_0];
2506 if(device->render)
2507 WOD_PushData(device);
2508 else
2509 WID_PullData(device);
2510 }else
2511 WARN("Unexpected MsgWait result 0x%x, GLE: %d\n", wait,
2512 GetLastError());
2513 if(WINMM_DevicesThreadDone()){
2514 TRACE("Quitting devices thread\n");
2515 FreeLibraryAndExitThread(g_devthread_module, 0);
2519 FreeLibraryAndExitThread(g_devthread_module, 0);
2522 /* on success, increments g_devthread_token to prevent
2523 * device thread shutdown. caller must decrement. */
2524 static BOOL WINMM_StartDevicesThread(void)
2526 HANDLE events[2];
2527 DWORD wait;
2529 EnterCriticalSection(&g_devthread_lock);
2531 if(g_devices_hwnd){
2532 wait = WaitForSingleObject(g_devices_thread, 0);
2533 if(wait == WAIT_TIMEOUT){
2534 /* thread still running */
2535 InterlockedIncrement(&g_devthread_token);
2536 LeaveCriticalSection(&g_devthread_lock);
2537 return TRUE;
2539 if(wait != WAIT_OBJECT_0){
2540 /* error */
2541 LeaveCriticalSection(&g_devthread_lock);
2542 return FALSE;
2544 TRACE("Devices thread left dangling message window?\n");
2545 g_devices_hwnd = NULL;
2546 CloseHandle(g_devices_thread);
2547 g_devices_thread = NULL;
2548 }else if(g_devices_thread){
2549 WaitForSingleObject(g_devices_thread, INFINITE);
2550 CloseHandle(g_devices_thread);
2551 g_devices_thread = NULL;
2554 TRACE("Starting up devices thread\n");
2556 /* The devices thread holds a reference to the winmm module
2557 * to prevent it from unloading while it's running. */
2558 GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
2559 (const WCHAR *)WINMM_StartDevicesThread, &g_devthread_module);
2561 events[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
2563 g_devices_thread = CreateThread(NULL, 0, WINMM_DevicesThreadProc,
2564 events[0], 0, NULL);
2565 if(!g_devices_thread){
2566 LeaveCriticalSection(&g_devthread_lock);
2567 CloseHandle(events[0]);
2568 FreeLibrary(g_devthread_module);
2569 return FALSE;
2572 events[1] = g_devices_thread;
2573 wait = WaitForMultipleObjects(2, events, FALSE, INFINITE);
2574 CloseHandle(events[0]);
2575 if(wait != WAIT_OBJECT_0){
2576 if(wait == 1 + WAIT_OBJECT_0){
2577 CloseHandle(g_devices_thread);
2578 g_devices_thread = NULL;
2579 g_devices_hwnd = NULL;
2581 LeaveCriticalSection(&g_devthread_lock);
2582 return FALSE;
2585 InterlockedIncrement(&g_devthread_token);
2587 LeaveCriticalSection(&g_devthread_lock);
2589 return TRUE;
2592 /**************************************************************************
2593 * waveOutGetNumDevs [WINMM.@]
2595 UINT WINAPI waveOutGetNumDevs(void)
2597 HRESULT hr = WINMM_InitMMDevices();
2598 if(FAILED(hr))
2599 return 0;
2601 TRACE("count: %u\n", g_outmmdevices_count);
2603 return g_outmmdevices_count;
2606 /**************************************************************************
2607 * waveOutGetDevCapsA [WINMM.@]
2609 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
2610 UINT uSize)
2612 WAVEOUTCAPSW wocW;
2613 UINT ret;
2615 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2617 if(!lpCaps)
2618 return MMSYSERR_INVALPARAM;
2620 ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
2622 if (ret == MMSYSERR_NOERROR) {
2623 WAVEOUTCAPSA wocA;
2624 wocA.wMid = wocW.wMid;
2625 wocA.wPid = wocW.wPid;
2626 wocA.vDriverVersion = wocW.vDriverVersion;
2627 WideCharToMultiByte( CP_ACP, 0, wocW.szPname, -1, wocA.szPname,
2628 sizeof(wocA.szPname), NULL, NULL );
2629 wocA.dwFormats = wocW.dwFormats;
2630 wocA.wChannels = wocW.wChannels;
2631 wocA.dwSupport = wocW.dwSupport;
2632 wocA.wReserved1 = wocW.wReserved1;
2633 memcpy(lpCaps, &wocA, min(uSize, sizeof(wocA)));
2635 return ret;
2638 /**************************************************************************
2639 * waveOutGetDevCapsW [WINMM.@]
2641 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
2642 UINT uSize)
2644 WAVEOUTCAPSW mapper_caps, *caps;
2645 HRESULT hr;
2647 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
2649 hr = WINMM_InitMMDevices();
2650 if(FAILED(hr))
2651 return MMSYSERR_NODRIVER;
2653 if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
2655 if(WINMM_IsMapper(uDeviceID)){
2656 mapper_caps.wMid = 0xFF;
2657 mapper_caps.wPid = 0xFF;
2658 mapper_caps.vDriverVersion = 0x00010001;
2659 mapper_caps.dwFormats = 0xFFFFFFFF;
2660 mapper_caps.wReserved1 = 0;
2661 mapper_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
2662 WAVECAPS_SAMPLEACCURATE;
2663 mapper_caps.wChannels = 2;
2664 LoadStringW(hWinMM32Instance, IDS_MAPPER_NAME, mapper_caps.szPname, MAXPNAMELEN);
2666 caps = &mapper_caps;
2667 }else{
2668 if(uDeviceID >= g_outmmdevices_count)
2669 return MMSYSERR_BADDEVICEID;
2671 caps = &read_map(g_out_map, uDeviceID)->out_caps;
2674 memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
2676 return MMSYSERR_NOERROR;
2679 /**************************************************************************
2680 * waveOutGetErrorTextA [WINMM.@]
2681 * waveInGetErrorTextA [WINMM.@]
2683 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
2685 UINT ret;
2687 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2688 else if (uSize == 0) ret = MMSYSERR_NOERROR;
2689 else
2691 LPWSTR xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
2692 if (!xstr) ret = MMSYSERR_NOMEM;
2693 else
2695 ret = waveOutGetErrorTextW(uError, xstr, uSize);
2696 if (ret == MMSYSERR_NOERROR)
2697 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
2698 HeapFree(GetProcessHeap(), 0, xstr);
2701 return ret;
2704 /**************************************************************************
2705 * waveOutGetErrorTextW [WINMM.@]
2706 * waveInGetErrorTextW [WINMM.@]
2708 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
2710 UINT ret = MMSYSERR_BADERRNUM;
2712 if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
2713 else if (uSize == 0) ret = MMSYSERR_NOERROR;
2714 else if (
2715 /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
2716 * a warning for the test was always true */
2717 (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
2718 (uError >= WAVERR_BASE && uError <= WAVERR_LASTERROR)) {
2719 if (LoadStringW(hWinMM32Instance,
2720 uError, lpText, uSize) > 0) {
2721 ret = MMSYSERR_NOERROR;
2724 return ret;
2727 /**************************************************************************
2728 * waveOutOpen [WINMM.@]
2729 * All the args/structs have the same layout as the win16 equivalents
2731 MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
2732 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
2733 DWORD_PTR dwInstance, DWORD dwFlags)
2735 LRESULT res;
2736 WINMM_OpenInfo info;
2737 WINMM_CBInfo cb_info;
2739 TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut, uDeviceID, lpFormat,
2740 dwCallback, dwInstance, dwFlags);
2742 if(!lphWaveOut && !(dwFlags & WAVE_FORMAT_QUERY))
2743 return MMSYSERR_INVALPARAM;
2745 res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
2746 if(res != MMSYSERR_NOERROR)
2747 return res;
2749 if(!WINMM_StartDevicesThread())
2750 return MMSYSERR_NODRIVER;
2752 info.handle = 0;
2753 info.format = (WAVEFORMATEX*)lpFormat;
2754 info.callback = dwCallback;
2755 info.cb_user = dwInstance;
2756 info.req_device = uDeviceID;
2757 info.flags = dwFlags;
2758 info.reset = TRUE;
2760 res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0);
2761 InterlockedDecrement(&g_devthread_token);
2762 if(res != MMSYSERR_NOERROR || (dwFlags & WAVE_FORMAT_QUERY))
2763 return res;
2765 if(lphWaveOut)
2766 *lphWaveOut = (HWAVEOUT)info.handle;
2768 cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
2769 cb_info.callback = dwCallback;
2770 cb_info.user = dwInstance;
2771 cb_info.hwave = info.handle;
2773 WINMM_NotifyClient(&cb_info, WOM_OPEN, 0, 0);
2775 return res;
2778 /**************************************************************************
2779 * waveOutClose [WINMM.@]
2781 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
2783 UINT res;
2784 WINMM_Device *device;
2785 WINMM_CBInfo cb_info;
2787 TRACE("(%p)\n", hWaveOut);
2789 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2791 if(!WINMM_ValidateAndLock(device))
2792 return MMSYSERR_INVALHANDLE;
2794 cb_info = device->cb_info;
2796 LeaveCriticalSection(&device->lock);
2798 res = SendMessageW(g_devices_hwnd, WODM_CLOSE, (WPARAM)hWaveOut, 0);
2800 if(res == MMSYSERR_NOERROR)
2801 WINMM_NotifyClient(&cb_info, WOM_CLOSE, 0, 0);
2803 return res;
2806 /**************************************************************************
2807 * waveOutPrepareHeader [WINMM.@]
2809 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
2810 WAVEHDR* lpWaveOutHdr, UINT uSize)
2812 TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2814 if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2815 return MMSYSERR_INVALPARAM;
2817 if(lpWaveOutHdr->dwFlags & WHDR_PREPARED)
2818 return MMSYSERR_NOERROR;
2820 return WINMM_PrepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2823 /**************************************************************************
2824 * waveOutUnprepareHeader [WINMM.@]
2826 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
2827 LPWAVEHDR lpWaveOutHdr, UINT uSize)
2829 TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
2831 if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
2832 return MMSYSERR_INVALPARAM;
2834 if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
2835 return WAVERR_STILLPLAYING;
2837 if(!(lpWaveOutHdr->dwFlags & WHDR_PREPARED))
2838 return MMSYSERR_NOERROR;
2840 return WINMM_UnprepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
2843 /**************************************************************************
2844 * waveOutWrite [WINMM.@]
2846 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, WAVEHDR *header, UINT uSize)
2848 WINMM_Device *device;
2849 MMRESULT mr;
2851 TRACE("(%p, %p, %u)\n", hWaveOut, header, uSize);
2853 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2855 if(!WINMM_ValidateAndLock(device))
2856 return MMSYSERR_INVALHANDLE;
2858 if(!header->lpData || !(header->dwFlags & WHDR_PREPARED)){
2859 LeaveCriticalSection(&device->lock);
2860 return WAVERR_UNPREPARED;
2863 if(header->dwFlags & WHDR_INQUEUE){
2864 LeaveCriticalSection(&device->lock);
2865 return WAVERR_STILLPLAYING;
2868 TRACE("dwBufferLength: %u\n", header->dwBufferLength);
2870 if(device->acm_handle){
2871 ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
2873 ash->cbSrcLength = header->dwBufferLength;
2874 mr = acmStreamConvert(device->acm_handle, ash, 0);
2875 if(mr != MMSYSERR_NOERROR){
2876 LeaveCriticalSection(&device->lock);
2877 return mr;
2881 if(device->first){
2882 device->last->lpNext = header;
2883 device->last = header;
2884 if(!device->playing)
2885 device->playing = header;
2886 }else{
2887 device->playing = device->first = device->last = header;
2888 if(header->dwFlags & WHDR_BEGINLOOP){
2889 device->loop_counter = header->dwLoops;
2890 device->loop_start = header;
2894 header->lpNext = NULL;
2895 header->dwFlags &= ~WHDR_DONE;
2896 header->dwFlags |= WHDR_INQUEUE;
2898 mr = WINMM_BeginPlaying(device);
2900 LeaveCriticalSection(&device->lock);
2902 return mr;
2905 /**************************************************************************
2906 * waveOutBreakLoop [WINMM.@]
2908 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
2910 WINMM_Device *device;
2912 TRACE("(%p)\n", hWaveOut);
2914 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2916 if(!WINMM_ValidateAndLock(device))
2917 return MMSYSERR_INVALHANDLE;
2919 device->loop_counter = 0;
2921 LeaveCriticalSection(&device->lock);
2923 return MMSYSERR_NOERROR;
2926 /**************************************************************************
2927 * waveOutPause [WINMM.@]
2929 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
2931 TRACE("(%p)\n", hWaveOut);
2933 return WINMM_Pause((HWAVE)hWaveOut);
2936 /**************************************************************************
2937 * waveOutReset [WINMM.@]
2939 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
2941 TRACE("(%p)\n", hWaveOut);
2943 return WINMM_Reset((HWAVE)hWaveOut);
2946 /**************************************************************************
2947 * waveOutRestart [WINMM.@]
2949 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
2951 WINMM_Device *device;
2952 MMRESULT mr;
2954 TRACE("(%p)\n", hWaveOut);
2956 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
2958 if(!WINMM_ValidateAndLock(device))
2959 return MMSYSERR_INVALHANDLE;
2961 device->stopped = TRUE;
2963 mr = WINMM_BeginPlaying(device);
2965 LeaveCriticalSection(&device->lock);
2967 return mr;
2970 /**************************************************************************
2971 * waveOutGetPosition [WINMM.@]
2973 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
2974 UINT uSize)
2976 TRACE("(%p, %p, %u)\n", hWaveOut, lpTime, uSize);
2978 if(!uSize || !lpTime)
2979 return MMSYSERR_INVALPARAM;
2981 if(uSize < sizeof(MMTIME))
2982 return MMSYSERR_ERROR;
2984 return WINMM_GetPosition((HWAVE)hWaveOut, lpTime);
2987 /**************************************************************************
2988 * waveOutGetPitch [WINMM.@]
2990 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
2992 TRACE("(%p, %p)\n", hWaveOut, lpdw);
2993 return MMSYSERR_NOTSUPPORTED;
2996 /**************************************************************************
2997 * waveOutSetPitch [WINMM.@]
2999 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
3001 TRACE("(%p, %08x)\n", hWaveOut, dw);
3003 return MMSYSERR_NOTSUPPORTED;
3006 /**************************************************************************
3007 * waveOutGetPlaybackRate [WINMM.@]
3009 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
3011 TRACE("(%p, %p)\n", hWaveOut, lpdw);
3013 return MMSYSERR_NOTSUPPORTED;
3016 /**************************************************************************
3017 * waveOutSetPlaybackRate [WINMM.@]
3019 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
3021 TRACE("(%p, %08x)\n", hWaveOut, dw);
3023 return MMSYSERR_NOTSUPPORTED;
3026 /**************************************************************************
3027 * waveOutGetVolume [WINMM.@]
3029 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, DWORD *out)
3031 WINMM_Device *device;
3032 UINT32 channels;
3033 float *vols;
3034 HRESULT hr;
3036 TRACE("(%p, %p)\n", hWaveOut, out);
3038 if(!out)
3039 return MMSYSERR_INVALPARAM;
3041 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
3043 if(!WINMM_ValidateAndLock(device))
3044 return MMSYSERR_INVALHANDLE;
3046 hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
3047 if(FAILED(hr)){
3048 LeaveCriticalSection(&device->lock);
3049 WARN("GetChannelCount failed: %08x\n", hr);
3050 return MMSYSERR_ERROR;
3053 vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
3054 if(!vols){
3055 LeaveCriticalSection(&device->lock);
3056 return MMSYSERR_NOMEM;
3059 hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
3060 if(FAILED(hr)){
3061 LeaveCriticalSection(&device->lock);
3062 HeapFree(GetProcessHeap(), 0, vols);
3063 WARN("GetAllVolumes failed: %08x\n", hr);
3064 return MMSYSERR_ERROR;
3067 LeaveCriticalSection(&device->lock);
3069 *out = ((UINT16)(vols[0] * (DWORD)0xFFFF));
3070 if(channels > 1)
3071 *out |= ((UINT16)(vols[1] * (DWORD)0xFFFF)) << 16;
3073 HeapFree(GetProcessHeap(), 0, vols);
3075 return MMSYSERR_NOERROR;
3078 /**************************************************************************
3079 * waveOutSetVolume [WINMM.@]
3081 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD in)
3083 WINMM_Device *device;
3084 UINT32 channels;
3085 float *vols;
3086 HRESULT hr;
3088 TRACE("(%p, %08x)\n", hWaveOut, in);
3090 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
3092 if(!WINMM_ValidateAndLock(device))
3093 return MMSYSERR_INVALHANDLE;
3095 hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
3096 if(FAILED(hr)){
3097 LeaveCriticalSection(&device->lock);
3098 WARN("GetChannelCount failed: %08x\n", hr);
3099 return MMSYSERR_ERROR;
3102 vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
3103 if(!vols){
3104 LeaveCriticalSection(&device->lock);
3105 return MMSYSERR_NOMEM;
3108 hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
3109 if(FAILED(hr)){
3110 LeaveCriticalSection(&device->lock);
3111 HeapFree(GetProcessHeap(), 0, vols);
3112 WARN("GetAllVolumes failed: %08x\n", hr);
3113 return MMSYSERR_ERROR;
3116 vols[0] = (float)((DWORD)(in & 0xFFFF) / (float)0xFFFF);
3117 if(channels > 1)
3118 vols[1] = (float)((DWORD)(in >> 16) / (float)0xFFFF);
3120 hr = IAudioStreamVolume_SetAllVolumes(device->volume, channels, vols);
3121 if(FAILED(hr)){
3122 LeaveCriticalSection(&device->lock);
3123 HeapFree(GetProcessHeap(), 0, vols);
3124 WARN("SetAllVolumes failed: %08x\n", hr);
3125 return MMSYSERR_ERROR;
3128 LeaveCriticalSection(&device->lock);
3130 HeapFree(GetProcessHeap(), 0, vols);
3132 return MMSYSERR_NOERROR;
3135 /**************************************************************************
3136 * waveOutGetID [WINMM.@]
3138 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
3140 WINMM_Device *device;
3141 UINT dev, junk;
3142 BOOL is_out;
3144 TRACE("(%p, %p)\n", hWaveOut, lpuDeviceID);
3146 if(!lpuDeviceID)
3147 return MMSYSERR_INVALPARAM;
3149 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
3150 if(!WINMM_ValidateAndLock(device))
3151 return MMSYSERR_INVALHANDLE;
3153 LeaveCriticalSection(&device->lock);
3155 WINMM_DecomposeHWAVE((HWAVE)hWaveOut, lpuDeviceID, &is_out, &dev, &junk);
3157 return MMSYSERR_NOERROR;
3160 static UINT WINMM_QueryInstanceIDSize(UINT device, DWORD_PTR *len, BOOL is_out)
3162 UINT count;
3163 WINMM_MMDevice **devices;
3165 TRACE("(%u, %p, %d)\n", device, len, is_out);
3167 if(is_out){
3168 count = g_outmmdevices_count;
3169 devices = g_out_map;
3170 }else{
3171 count = g_inmmdevices_count;
3172 devices = g_in_map;
3175 if(device >= count)
3176 return MMSYSERR_INVALHANDLE;
3178 EnterCriticalSection(&g_devthread_lock);
3179 *len = (lstrlenW(devices[device]->dev_id) + 1) * sizeof(WCHAR);
3180 LeaveCriticalSection(&g_devthread_lock);
3182 return MMSYSERR_NOERROR;
3185 static UINT WINMM_QueryInstanceID(UINT device, WCHAR *str, DWORD_PTR len,
3186 BOOL is_out)
3188 UINT count, id_len;
3189 WINMM_MMDevice **devices;
3191 TRACE("(%u, %p, %d)\n", device, str, is_out);
3193 if(is_out){
3194 count = g_outmmdevices_count;
3195 devices = g_out_map;
3196 }else{
3197 count = g_inmmdevices_count;
3198 devices = g_in_map;
3201 if(device >= count)
3202 return MMSYSERR_INVALHANDLE;
3204 EnterCriticalSection(&g_devthread_lock);
3205 id_len = (lstrlenW(devices[device]->dev_id) + 1) * sizeof(WCHAR);
3206 if(len < id_len){
3207 LeaveCriticalSection(&g_devthread_lock);
3208 return MMSYSERR_ERROR;
3211 memcpy(str, devices[device]->dev_id, id_len);
3212 LeaveCriticalSection(&g_devthread_lock);
3214 return MMSYSERR_NOERROR;
3217 static UINT get_device_interface(UINT msg, BOOL is_out, UINT index, WCHAR *out, ULONG *out_len)
3219 WINMM_QueryInterfaceInfo info;
3220 UINT ret;
3222 if(!WINMM_StartDevicesThread())
3223 return MMSYSERR_NODRIVER;
3225 info.is_out = is_out;
3226 info.index = index;
3227 info.str = out;
3228 info.len_bytes = out_len;
3230 ret = SendMessageW(g_devices_hwnd, msg, (DWORD_PTR)&info, 0);
3231 InterlockedDecrement(&g_devthread_token);
3232 return ret;
3235 /**************************************************************************
3236 * waveOutMessage [WINMM.@]
3238 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
3239 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3241 TRACE("(%p, %u, %lx, %lx)\n", hWaveOut, uMessage, dwParam1, dwParam2);
3243 switch(uMessage){
3244 case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
3245 return WINMM_QueryInstanceIDSize(HandleToULong(hWaveOut),
3246 (DWORD_PTR*)dwParam1, TRUE);
3247 case DRV_QUERYFUNCTIONINSTANCEID:
3248 return WINMM_QueryInstanceID(HandleToULong(hWaveOut), (WCHAR*)dwParam1, dwParam2, TRUE);
3249 case DRV_QUERYDEVICEINTERFACESIZE:
3250 return get_device_interface(DRV_QUERYDEVICEINTERFACESIZE, TRUE, HandleToULong(hWaveOut),
3251 NULL, (ULONG*)dwParam1);
3252 case DRV_QUERYDEVICEINTERFACE:
3254 ULONG size = dwParam2;
3255 return get_device_interface(DRV_QUERYDEVICEINTERFACE, TRUE, HandleToULong(hWaveOut),
3256 (WCHAR*)dwParam1, &size);
3258 case DRV_QUERYMAPPABLE:
3259 return MMSYSERR_NOERROR;
3260 case DRVM_MAPPER_PREFERRED_GET:
3261 if(dwParam1) {
3262 if(g_outmmdevices_count > 0)
3263 /* Device 0 is always the default device */
3264 *(DWORD *)dwParam1 = 0;
3265 else
3266 *(DWORD *)dwParam1 = -1;
3269 if(dwParam2)
3270 /* Status flags */
3271 *(DWORD *)dwParam2 = 0;
3273 return MMSYSERR_NOERROR;
3276 TRACE("Message not supported: %u\n", uMessage);
3278 return MMSYSERR_NOTSUPPORTED;
3281 /**************************************************************************
3282 * waveInGetNumDevs [WINMM.@]
3284 UINT WINAPI waveInGetNumDevs(void)
3286 HRESULT hr = WINMM_InitMMDevices();
3287 if(FAILED(hr))
3288 return 0;
3290 TRACE("count: %u\n", g_inmmdevices_count);
3292 return g_inmmdevices_count;
3295 /**************************************************************************
3296 * waveInGetDevCapsW [WINMM.@]
3298 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
3300 WAVEINCAPSW mapper_caps, *caps;
3301 HRESULT hr;
3303 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3305 hr = WINMM_InitMMDevices();
3306 if(FAILED(hr))
3307 return MMSYSERR_NODRIVER;
3309 if(!lpCaps)
3310 return MMSYSERR_INVALPARAM;
3312 if(WINMM_IsMapper(uDeviceID)){
3313 mapper_caps.wMid = 0xFF;
3314 mapper_caps.wPid = 0xFF;
3315 mapper_caps.vDriverVersion = 0x00010001;
3316 mapper_caps.dwFormats = 0xFFFFFFFF;
3317 mapper_caps.wReserved1 = 0;
3318 mapper_caps.wChannels = 2;
3319 LoadStringW(hWinMM32Instance, IDS_MAPPER_NAME, mapper_caps.szPname, MAXPNAMELEN);
3321 caps = &mapper_caps;
3322 }else{
3323 if(uDeviceID >= g_inmmdevices_count)
3324 return MMSYSERR_BADDEVICEID;
3326 caps = &read_map(g_in_map, uDeviceID)->in_caps;
3329 memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
3331 return MMSYSERR_NOERROR;
3334 /**************************************************************************
3335 * waveInGetDevCapsA [WINMM.@]
3337 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
3339 UINT ret;
3340 WAVEINCAPSW wicW;
3342 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3344 if(!lpCaps)
3345 return MMSYSERR_INVALPARAM;
3347 ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
3349 if (ret == MMSYSERR_NOERROR) {
3350 WAVEINCAPSA wicA;
3351 wicA.wMid = wicW.wMid;
3352 wicA.wPid = wicW.wPid;
3353 wicA.vDriverVersion = wicW.vDriverVersion;
3354 WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
3355 sizeof(wicA.szPname), NULL, NULL );
3356 wicA.dwFormats = wicW.dwFormats;
3357 wicA.wChannels = wicW.wChannels;
3358 wicA.wReserved1 = wicW.wReserved1;
3359 memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
3361 return ret;
3364 /**************************************************************************
3365 * waveInOpen [WINMM.@]
3367 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
3368 LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
3369 DWORD_PTR dwInstance, DWORD dwFlags)
3371 LRESULT res;
3372 WINMM_OpenInfo info;
3373 WINMM_CBInfo cb_info;
3375 TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
3376 dwCallback, dwInstance, dwFlags);
3378 if(!lphWaveIn && !(dwFlags & WAVE_FORMAT_QUERY))
3379 return MMSYSERR_INVALPARAM;
3381 res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
3382 if(res != MMSYSERR_NOERROR)
3383 return res;
3385 if(!WINMM_StartDevicesThread())
3386 return MMSYSERR_NODRIVER;
3388 info.handle = 0;
3389 info.format = (WAVEFORMATEX*)lpFormat;
3390 info.callback = dwCallback;
3391 info.cb_user = dwInstance;
3392 info.req_device = uDeviceID;
3393 info.flags = dwFlags;
3394 info.reset = TRUE;
3396 res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
3397 InterlockedDecrement(&g_devthread_token);
3398 if(res != MMSYSERR_NOERROR || (dwFlags & WAVE_FORMAT_QUERY))
3399 return res;
3401 if(lphWaveIn)
3402 *lphWaveIn = (HWAVEIN)info.handle;
3404 cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
3405 cb_info.callback = dwCallback;
3406 cb_info.user = dwInstance;
3407 cb_info.hwave = info.handle;
3409 WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
3411 return res;
3414 /**************************************************************************
3415 * waveInClose [WINMM.@]
3417 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
3419 WINMM_Device *device;
3420 WINMM_CBInfo cb_info;
3421 UINT res;
3423 TRACE("(%p)\n", hWaveIn);
3425 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3427 if(!WINMM_ValidateAndLock(device))
3428 return MMSYSERR_INVALHANDLE;
3430 cb_info = device->cb_info;
3432 LeaveCriticalSection(&device->lock);
3434 res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
3436 if(res == MMSYSERR_NOERROR)
3437 WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
3439 return res;
3442 /**************************************************************************
3443 * waveInPrepareHeader [WINMM.@]
3445 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
3446 UINT uSize)
3448 TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
3450 if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
3451 return MMSYSERR_INVALPARAM;
3453 if(lpWaveInHdr->dwFlags & WHDR_PREPARED)
3454 return MMSYSERR_NOERROR;
3456 return WINMM_PrepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
3459 /**************************************************************************
3460 * waveInUnprepareHeader [WINMM.@]
3462 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
3463 UINT uSize)
3465 TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
3467 if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
3468 return MMSYSERR_INVALPARAM;
3470 if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
3471 return WAVERR_STILLPLAYING;
3473 if(!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
3474 return MMSYSERR_NOERROR;
3476 return WINMM_UnprepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
3479 /**************************************************************************
3480 * waveInAddBuffer [WINMM.@]
3482 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn, WAVEHDR *header, UINT uSize)
3484 WINMM_Device *device;
3486 TRACE("(%p, %p, %u)\n", hWaveIn, header, uSize);
3488 if(!header || uSize < sizeof(WAVEHDR))
3489 return MMSYSERR_INVALPARAM;
3491 if(!(header->dwFlags & WHDR_PREPARED))
3492 return WAVERR_UNPREPARED;
3494 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3496 if(!WINMM_ValidateAndLock(device))
3497 return MMSYSERR_INVALHANDLE;
3499 if(!device->first)
3500 device->first = device->last = header;
3501 else{
3502 device->last->lpNext = header;
3503 device->last = header;
3506 header->dwBytesRecorded = 0;
3507 header->lpNext = NULL;
3508 header->dwFlags &= ~WHDR_DONE;
3509 header->dwFlags |= WHDR_INQUEUE;
3511 LeaveCriticalSection(&device->lock);
3513 return MMSYSERR_NOERROR;
3516 /**************************************************************************
3517 * waveInReset [WINMM.@]
3519 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
3521 TRACE("(%p)\n", hWaveIn);
3523 return WINMM_Reset((HWAVE)hWaveIn);
3526 /**************************************************************************
3527 * waveInStart [WINMM.@]
3529 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
3531 WINMM_Device *device;
3532 MMRESULT mr;
3534 TRACE("(%p)\n", hWaveIn);
3536 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3538 if(!WINMM_ValidateAndLock(device))
3539 return MMSYSERR_INVALHANDLE;
3541 mr = WINMM_BeginPlaying(device);
3543 LeaveCriticalSection(&device->lock);
3545 return mr;
3548 /**************************************************************************
3549 * waveInStop [WINMM.@]
3551 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
3553 WINMM_CBInfo cb_info;
3554 WINMM_Device *device;
3555 WAVEHDR *buf;
3556 HRESULT hr;
3558 TRACE("(%p)\n", hWaveIn);
3560 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3562 if(!WINMM_ValidateAndLock(device))
3563 return MMSYSERR_INVALHANDLE;
3565 hr = WINMM_Pause((HWAVE)hWaveIn);
3566 if(FAILED(hr)){
3567 LeaveCriticalSection(&device->lock);
3568 return MMSYSERR_ERROR;
3570 device->stopped = TRUE;
3572 buf = device->first;
3573 if(buf && buf->dwBytesRecorded > 0){
3574 device->first = buf->lpNext;
3575 }else
3576 buf = NULL;
3578 cb_info = device->cb_info;
3580 LeaveCriticalSection(&device->lock);
3582 if(buf){
3583 buf->dwFlags &= ~WHDR_INQUEUE;
3584 buf->dwFlags |= WHDR_DONE;
3585 WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
3588 return MMSYSERR_NOERROR;
3591 /**************************************************************************
3592 * waveInGetPosition [WINMM.@]
3594 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
3595 UINT uSize)
3597 TRACE("(%p, %p, %u)\n", hWaveIn, lpTime, uSize);
3599 if(!uSize || !lpTime)
3600 return MMSYSERR_INVALPARAM;
3602 if(uSize < sizeof(MMTIME))
3603 return MMSYSERR_ERROR;
3605 return WINMM_GetPosition((HWAVE)hWaveIn, lpTime);
3608 /**************************************************************************
3609 * waveInGetID [WINMM.@]
3611 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
3613 UINT dev, junk;
3614 BOOL is_out;
3615 WINMM_Device *device;
3617 TRACE("(%p, %p)\n", hWaveIn, lpuDeviceID);
3619 if(!lpuDeviceID)
3620 return MMSYSERR_INVALPARAM;
3622 device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
3623 if(!WINMM_ValidateAndLock(device))
3624 return MMSYSERR_INVALHANDLE;
3626 LeaveCriticalSection(&device->lock);
3628 WINMM_DecomposeHWAVE((HWAVE)hWaveIn, lpuDeviceID, &is_out, &dev, &junk);
3630 return MMSYSERR_NOERROR;
3633 /**************************************************************************
3634 * waveInMessage [WINMM.@]
3636 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
3637 DWORD_PTR dwParam1, DWORD_PTR dwParam2)
3639 TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
3641 switch(uMessage){
3642 case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
3643 return WINMM_QueryInstanceIDSize(HandleToULong(hWaveIn),
3644 (DWORD_PTR*)dwParam1, FALSE);
3645 case DRV_QUERYFUNCTIONINSTANCEID:
3646 return WINMM_QueryInstanceID(HandleToULong(hWaveIn), (WCHAR*)dwParam1, dwParam2, FALSE);
3647 case DRV_QUERYDEVICEINTERFACESIZE:
3648 return get_device_interface(DRV_QUERYDEVICEINTERFACESIZE, FALSE, HandleToULong(hWaveIn),
3649 NULL, (ULONG*)dwParam1);
3650 case DRV_QUERYDEVICEINTERFACE:
3652 ULONG size = dwParam2;
3653 return get_device_interface(DRV_QUERYDEVICEINTERFACE, FALSE, HandleToULong(hWaveIn),
3654 (WCHAR*)dwParam1, &size);
3656 case DRV_QUERYMAPPABLE:
3657 return MMSYSERR_NOERROR;
3658 case DRVM_MAPPER_PREFERRED_GET:
3659 if(dwParam1) {
3660 if(g_inmmdevices_count > 0)
3661 /* Device 0 is always the default device */
3662 *(DWORD *)dwParam1 = 0;
3663 else
3664 *(DWORD *)dwParam1 = -1;
3667 if(dwParam2)
3668 /* Status flags */
3669 *(DWORD *)dwParam2 = 0;
3671 return MMSYSERR_NOERROR;
3674 TRACE("Message not supported: %u\n", uMessage);
3676 return MMSYSERR_NOTSUPPORTED;
3679 UINT WINAPI mixerGetNumDevs(void)
3681 HRESULT hr;
3683 TRACE("\n");
3685 hr = WINMM_InitMMDevices();
3686 if(FAILED(hr))
3687 return 0;
3689 return g_outmmdevices_count + g_inmmdevices_count;
3692 /**************************************************************************
3693 * mixerGetDevCapsA [WINMM.@]
3695 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
3697 MIXERCAPSW micW;
3698 UINT ret;
3700 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3702 if(!lpCaps)
3703 return MMSYSERR_INVALPARAM;
3705 ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
3707 if (ret == MMSYSERR_NOERROR) {
3708 MIXERCAPSA micA;
3709 micA.wMid = micW.wMid;
3710 micA.wPid = micW.wPid;
3711 micA.vDriverVersion = micW.vDriverVersion;
3712 WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
3713 sizeof(micA.szPname), NULL, NULL );
3714 micA.fdwSupport = micW.fdwSupport;
3715 micA.cDestinations = micW.cDestinations;
3716 memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
3718 return ret;
3721 /**************************************************************************
3722 * mixerGetDevCapsW [WINMM.@]
3724 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
3726 WINMM_MMDevice *mmdevice;
3727 MIXERCAPSW caps;
3728 HRESULT hr;
3730 TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
3732 hr = WINMM_InitMMDevices();
3733 if(FAILED(hr))
3734 return MMSYSERR_NODRIVER;
3736 if(!lpCaps)
3737 return MMSYSERR_INVALPARAM;
3739 if(!uSize)
3740 return MMSYSERR_NOERROR;
3742 if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3743 mmdevice = WINMM_GetMixerMMDevice((HMIXEROBJ)uDeviceID,
3744 MIXER_OBJECTF_MIXER, NULL);
3745 else if(uDeviceID < g_outmmdevices_count)
3746 mmdevice = read_map(g_out_map, uDeviceID);
3747 else
3748 mmdevice = read_map(g_in_map, uDeviceID - g_outmmdevices_count);
3750 if(!mmdevice)
3751 return MMSYSERR_BADDEVICEID;
3753 if(mmdevice->dataflow == eRender)
3754 memcpy(caps.szPname, mmdevice->out_caps.szPname, sizeof(caps.szPname));
3755 else
3756 memcpy(caps.szPname, mmdevice->in_caps.szPname, sizeof(caps.szPname));
3758 caps.wMid = 0xFF;
3759 caps.wPid = 0xFF;
3760 caps.vDriverVersion = 0x00010001;
3761 caps.fdwSupport = 0;
3762 caps.cDestinations = 1;
3764 memcpy(lpCaps, &caps, uSize);
3766 return MMSYSERR_NOERROR;
3769 /**************************************************************************
3770 * mixerOpen [WINMM.@]
3772 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
3773 DWORD_PTR dwInstance, DWORD fdwOpen)
3775 WINMM_MMDevice *mmdevice;
3776 MMRESULT mr;
3777 HRESULT hr;
3779 TRACE("(%p, %d, %lx, %lx, %x)\n", lphMix, uDeviceID, dwCallback,
3780 dwInstance, fdwOpen);
3782 hr = WINMM_InitMMDevices();
3783 if(FAILED(hr))
3784 return MMSYSERR_NODRIVER;
3786 if(!lphMix)
3787 return MMSYSERR_INVALPARAM;
3789 mr = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
3790 if(mr != MMSYSERR_NOERROR)
3791 return mr;
3793 if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
3794 return MMSYSERR_BADDEVICEID;
3796 if(uDeviceID < g_outmmdevices_count){
3797 mmdevice = read_map(g_out_map, uDeviceID);
3798 *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID, TRUE,
3799 mmdevice->mixer_count);
3800 }else{
3801 mmdevice = read_map(g_in_map, uDeviceID - g_outmmdevices_count);
3802 *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID - g_outmmdevices_count,
3803 FALSE, mmdevice->mixer_count);
3806 ++mmdevice->mixer_count;
3808 return MMSYSERR_NOERROR;
3811 /**************************************************************************
3812 * mixerClose [WINMM.@]
3814 UINT WINAPI mixerClose(HMIXER hMix)
3816 TRACE("(%p)\n", hMix);
3818 return MMSYSERR_NOERROR;
3821 /**************************************************************************
3822 * mixerGetID [WINMM.@]
3824 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
3826 WINMM_MMDevice *mmdevice;
3827 HRESULT hr;
3829 TRACE("(%p, %p, %x)\n", hmix, lpid, fdwID);
3831 hr = WINMM_InitMMDevices();
3832 if(FAILED(hr))
3833 return MMSYSERR_NODRIVER;
3835 if(!lpid)
3836 return MMSYSERR_INVALPARAM;
3838 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwID, lpid);
3839 if(!mmdevice)
3840 return MMSYSERR_INVALHANDLE;
3842 if(mmdevice->in_caps.szPname[0] != '\0')
3843 *lpid += g_outmmdevices_count;
3845 return MMSYSERR_NOERROR;
3848 /**************************************************************************
3849 * mixerGetControlDetailsW [WINMM.@]
3851 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
3852 DWORD fdwDetails)
3854 WINMM_ControlDetails details;
3856 TRACE("(%p, %p, %x)\n", hmix, lpmcdW, fdwDetails);
3858 if(!WINMM_StartDevicesThread())
3859 return MMSYSERR_NODRIVER;
3861 if(!lpmcdW || !lpmcdW->paDetails)
3862 return MMSYSERR_INVALPARAM;
3864 TRACE("dwControlID: %u\n", lpmcdW->dwControlID);
3866 details.hmix = hmix;
3867 details.details = lpmcdW;
3868 details.flags = fdwDetails;
3870 return SendMessageW(g_devices_hwnd, MXDM_GETCONTROLDETAILS,
3871 (DWORD_PTR)&details, 0);
3874 /**************************************************************************
3875 * mixerGetControlDetailsA [WINMM.@]
3877 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
3878 DWORD fdwDetails)
3880 UINT ret = MMSYSERR_NOTSUPPORTED;
3882 TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
3884 if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
3885 return MMSYSERR_INVALPARAM;
3887 switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
3888 case MIXER_GETCONTROLDETAILSF_VALUE:
3889 /* can safely use A structure as it is, no string inside */
3890 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3891 break;
3892 case MIXER_GETCONTROLDETAILSF_LISTTEXT:
3894 MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = lpmcdA->paDetails;
3895 MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
3896 int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3897 unsigned int i;
3899 if (lpmcdA->u.cMultipleItems != 0) {
3900 size *= lpmcdA->u.cMultipleItems;
3902 pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
3903 lpmcdA->paDetails = pDetailsW;
3904 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
3905 /* set up lpmcd->paDetails */
3906 ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
3907 /* copy from lpmcd->paDetails back to paDetailsW; */
3908 if (ret == MMSYSERR_NOERROR) {
3909 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
3910 pDetailsA->dwParam1 = pDetailsW->dwParam1;
3911 pDetailsA->dwParam2 = pDetailsW->dwParam2;
3912 WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
3913 pDetailsA->szName,
3914 sizeof(pDetailsA->szName), NULL, NULL );
3915 pDetailsA++;
3916 pDetailsW++;
3918 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3919 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
3921 HeapFree(GetProcessHeap(), 0, pDetailsW);
3922 lpmcdA->paDetails = pDetailsA;
3923 lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
3925 break;
3926 default:
3927 WARN("Unsupported fdwDetails=0x%08x\n", fdwDetails);
3930 return ret;
3933 /**************************************************************************
3934 * mixerGetLineControlsA [WINMM.@]
3936 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
3937 DWORD fdwControls)
3939 MIXERLINECONTROLSW mlcW;
3940 DWORD ret;
3941 unsigned int i;
3943 TRACE("(%p, %p, %x)\n", hmix, lpmlcA, fdwControls);
3945 if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
3946 lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
3947 return MMSYSERR_INVALPARAM;
3949 mlcW.cbStruct = sizeof(mlcW);
3950 mlcW.dwLineID = lpmlcA->dwLineID;
3951 mlcW.u.dwControlID = lpmlcA->u.dwControlID;
3952 mlcW.u.dwControlType = lpmlcA->u.dwControlType;
3954 /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
3955 the control count is assumed to be 1 - This is relied upon by a game,
3956 "Dynomite Deluze" */
3957 if (MIXER_GETLINECONTROLSF_ONEBYTYPE == (fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK)) {
3958 mlcW.cControls = 1;
3959 } else {
3960 mlcW.cControls = lpmlcA->cControls;
3962 mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
3963 mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
3964 mlcW.cControls * mlcW.cbmxctrl);
3966 ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
3968 if (ret == MMSYSERR_NOERROR) {
3969 lpmlcA->dwLineID = mlcW.dwLineID;
3970 lpmlcA->u.dwControlID = mlcW.u.dwControlID;
3971 lpmlcA->u.dwControlType = mlcW.u.dwControlType;
3973 for (i = 0; i < mlcW.cControls; i++) {
3974 lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
3975 lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
3976 lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
3977 lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
3978 lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
3979 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
3980 lpmlcA->pamxctrl[i].szShortName,
3981 sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
3982 WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
3983 lpmlcA->pamxctrl[i].szName,
3984 sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
3985 /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
3986 * sizeof(mlcW.pamxctrl[i].Bounds) */
3987 memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
3988 sizeof(mlcW.pamxctrl[i].Bounds));
3989 /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
3990 * sizeof(mlcW.pamxctrl[i].Metrics) */
3991 memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
3992 sizeof(mlcW.pamxctrl[i].Metrics));
3996 HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
3998 return ret;
4001 static UINT WINMM_GetVolumeLineControl(WINMM_MMDevice *mmdevice, DWORD line,
4002 MIXERCONTROLW *ctl, DWORD flags)
4004 ctl->dwControlID = (line == 0xFFFF0000) ? 0 : 2;
4005 ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
4006 ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
4007 ctl->cMultipleItems = 0;
4008 LoadStringW(hWinMM32Instance, IDS_VOLUME, ctl->szShortName, MIXER_SHORT_NAME_CHARS);
4009 LoadStringW(hWinMM32Instance, IDS_VOLUME, ctl->szName, MIXER_LONG_NAME_CHARS);
4010 ctl->Bounds.s1.dwMinimum = 0;
4011 ctl->Bounds.s1.dwMaximum = 0xFFFF;
4012 ctl->Metrics.cSteps = 192;
4014 return MMSYSERR_NOERROR;
4017 static UINT WINMM_GetMuteLineControl(WINMM_MMDevice *mmdevice, DWORD line,
4018 MIXERCONTROLW *ctl, DWORD flags)
4020 ctl->dwControlID = (line == 0xFFFF0000) ? 1 : 3;
4021 ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
4022 ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
4023 ctl->cMultipleItems = 0;
4024 LoadStringW(hWinMM32Instance, IDS_MUTE, ctl->szShortName, MIXER_SHORT_NAME_CHARS);
4025 LoadStringW(hWinMM32Instance, IDS_MUTE, ctl->szName, MIXER_LONG_NAME_CHARS);
4026 ctl->Bounds.s1.dwMinimum = 0;
4027 ctl->Bounds.s1.dwMaximum = 1;
4028 ctl->Metrics.cSteps = 0;
4030 return MMSYSERR_NOERROR;
4033 /**************************************************************************
4034 * mixerGetLineControlsW [WINMM.@]
4036 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
4037 DWORD fdwControls)
4039 WINMM_MMDevice *mmdevice;
4040 HRESULT hr;
4042 TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
4044 hr = WINMM_InitMMDevices();
4045 if(FAILED(hr))
4046 return MMSYSERR_NODRIVER;
4048 if(fdwControls & ~(MIXER_GETLINECONTROLSF_ALL |
4049 MIXER_GETLINECONTROLSF_ONEBYID |
4050 MIXER_GETLINECONTROLSF_ONEBYTYPE |
4051 MIXER_OBJECTF_HMIXER |
4052 MIXER_OBJECTF_MIXER)){
4053 WARN("Unknown GetLineControls flag: %x\n", fdwControls);
4054 return MMSYSERR_INVALFLAG;
4057 if(!lpmlcW || lpmlcW->cbStruct < sizeof(*lpmlcW) || !lpmlcW->pamxctrl)
4058 return MMSYSERR_INVALPARAM;
4060 TRACE("dwLineID: %u\n", lpmlcW->dwLineID);
4061 TRACE("dwControl: %x\n", lpmlcW->u.dwControlID);
4062 TRACE("cControls: %u\n", lpmlcW->cControls);
4064 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwControls, NULL);
4065 if(!mmdevice)
4066 return MMSYSERR_INVALHANDLE;
4068 switch(fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK){
4069 case MIXER_GETLINECONTROLSF_ALL:
4070 if(lpmlcW->cControls != 2)
4071 return MMSYSERR_INVALPARAM;
4072 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
4073 return MMSYSERR_INVALPARAM;
4074 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
4075 return MIXERR_INVALLINE;
4076 WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
4077 &lpmlcW->pamxctrl[0], fdwControls);
4078 WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
4079 &lpmlcW->pamxctrl[1], fdwControls);
4080 return MMSYSERR_NOERROR;
4081 case MIXER_GETLINECONTROLSF_ONEBYID:
4082 if(lpmlcW->cControls != 1)
4083 return MMSYSERR_INVALPARAM;
4084 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
4085 return MMSYSERR_INVALPARAM;
4086 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
4087 return MIXERR_INVALLINE;
4088 if(lpmlcW->u.dwControlID == 0)
4089 return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
4090 lpmlcW->pamxctrl, fdwControls);
4091 if(lpmlcW->u.dwControlID == 1)
4092 return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
4093 lpmlcW->pamxctrl, fdwControls);
4094 return MMSYSERR_NOTSUPPORTED;
4095 case MIXER_GETLINECONTROLSF_ONEBYTYPE:
4096 if(lpmlcW->cControls != 1)
4097 return MMSYSERR_INVALPARAM;
4098 if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
4099 return MMSYSERR_INVALPARAM;
4100 if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
4101 return MIXERR_INVALLINE;
4102 if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
4103 return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
4104 lpmlcW->pamxctrl, fdwControls);
4105 if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
4106 return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
4107 lpmlcW->pamxctrl, fdwControls);
4108 return MMSYSERR_NOTSUPPORTED;
4111 return MMSYSERR_NOTSUPPORTED;
4114 static UINT WINMM_GetSourceLineInfo(WINMM_MMDevice *mmdevice, UINT mmdev_index,
4115 MIXERLINEW *info, DWORD flags)
4117 BOOL is_out = TRUE;
4118 if(mmdevice->in_caps.szPname[0] != '\0')
4119 is_out = FALSE;
4121 if(info->dwSource != 0)
4122 return MIXERR_INVALLINE;
4124 info->dwDestination = 0;
4125 info->dwLineID = 0;
4126 info->fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
4127 info->cConnections = 0;
4128 info->cControls = 2;
4129 /* volume & mute always affect all channels, so claim 1 channel */
4130 info->cChannels = 1;
4131 info->Target.dwDeviceID = mmdev_index;
4132 info->Target.wMid = ~0;
4133 info->Target.wPid = ~0;
4134 info->Target.vDriverVersion = 0;
4136 LoadStringW(hWinMM32Instance, IDS_VOLUME, info->szShortName, MIXER_SHORT_NAME_CHARS);
4137 LoadStringW(hWinMM32Instance, IDS_MASTER_VOLUME, info->szName, MIXER_LONG_NAME_CHARS);
4139 if(is_out){
4140 info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
4141 info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
4142 memcpy(info->Target.szPname, mmdevice->out_caps.szPname,
4143 sizeof(info->Target.szPname));
4144 }else{
4145 info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
4146 info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
4147 info->Target.szPname[0] = '\0';
4150 return MMSYSERR_NOERROR;
4153 static UINT WINMM_GetDestinationLineInfo(WINMM_MMDevice *mmdevice,
4154 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
4156 BOOL is_out = TRUE;
4157 if(mmdevice->in_caps.szPname[0] != '\0')
4158 is_out = FALSE;
4160 if(info->dwDestination != 0)
4161 return MIXERR_INVALLINE;
4163 info->dwSource = 0xFFFFFFFF;
4164 info->dwLineID = 0xFFFF0000;
4165 info->fdwLine = MIXERLINE_LINEF_ACTIVE;
4166 info->cConnections = 1;
4167 info->cControls = 2;
4169 LoadStringW(hWinMM32Instance, IDS_VOLUME, info->szShortName, MIXER_SHORT_NAME_CHARS);
4170 LoadStringW(hWinMM32Instance, IDS_MASTER_VOLUME, info->szName, MIXER_LONG_NAME_CHARS);
4172 info->Target.dwDeviceID = mmdev_index;
4173 info->Target.wMid = ~0;
4174 info->Target.wPid = ~0;
4175 info->Target.vDriverVersion = 0;
4177 if(is_out){
4178 info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
4179 info->cChannels = mmdevice->out_caps.wChannels;
4180 info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
4181 info->Target.szPname[0] = '\0';
4182 }else{
4183 info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
4184 info->cChannels = mmdevice->in_caps.wChannels;
4185 info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
4186 memcpy(info->Target.szPname, mmdevice->in_caps.szPname,
4187 sizeof(info->Target.szPname));
4190 return MMSYSERR_NOERROR;
4193 static UINT WINMM_GetComponentTypeLineInfo(WINMM_MMDevice *mmdevice,
4194 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
4196 BOOL is_out = TRUE;
4197 if(mmdevice->in_caps.szPname[0] != '\0')
4198 is_out = FALSE;
4200 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN){
4201 if(is_out)
4202 return MIXERR_INVALLINE;
4203 info->dwDestination = 0;
4204 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
4207 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS){
4208 if(!is_out)
4209 return MIXERR_INVALLINE;
4210 info->dwDestination = 0;
4211 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
4214 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE){
4215 if(is_out)
4216 return MIXERR_INVALLINE;
4217 info->dwSource = 0;
4218 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
4221 if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
4222 if(!is_out)
4223 return MIXERR_INVALLINE;
4224 info->dwSource = 0;
4225 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
4228 TRACE("Returning INVALLINE on this component type: %u\n",
4229 info->dwComponentType);
4231 return MIXERR_INVALLINE;
4234 static UINT WINMM_GetLineIDLineInfo(WINMM_MMDevice *mmdevice,
4235 UINT mmdev_index, MIXERLINEW *info, DWORD flags)
4237 if(info->dwLineID == 0xFFFF0000){
4238 info->dwDestination = 0;
4239 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
4242 if(info->dwLineID == 0){
4243 info->dwSource = 0;
4244 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
4247 TRACE("Returning INVALLINE on this dwLineID: %u\n", info->dwLineID);
4248 return MIXERR_INVALLINE;
4251 /**************************************************************************
4252 * mixerGetLineInfoW [WINMM.@]
4254 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
4256 UINT mmdev_index;
4257 WINMM_MMDevice *mmdevice;
4258 HRESULT hr;
4260 TRACE("(%p, %p, %x)\n", hmix, lpmliW, fdwInfo);
4262 hr = WINMM_InitMMDevices();
4263 if(FAILED(hr))
4264 return MMSYSERR_NODRIVER;
4266 if(!lpmliW || lpmliW->cbStruct < sizeof(MIXERLINEW))
4267 return MMSYSERR_INVALPARAM;
4269 TRACE("dwDestination: %u\n", lpmliW->dwDestination);
4270 TRACE("dwSource: %u\n", lpmliW->dwSource);
4271 TRACE("dwLineID: %u\n", lpmliW->dwLineID);
4272 TRACE("fdwLine: 0x%x\n", lpmliW->fdwLine);
4273 TRACE("dwComponentType: 0x%x\n", lpmliW->dwComponentType);
4275 if(fdwInfo & ~(MIXER_GETLINEINFOF_COMPONENTTYPE |
4276 MIXER_GETLINEINFOF_DESTINATION |
4277 MIXER_GETLINEINFOF_LINEID |
4278 MIXER_GETLINEINFOF_SOURCE |
4279 MIXER_GETLINEINFOF_TARGETTYPE |
4280 MIXER_OBJECTF_HMIXER |
4281 MIXER_OBJECTF_MIXER)){
4282 WARN("Unknown GetLineInfo flag: %x\n", fdwInfo);
4283 return MMSYSERR_INVALFLAG;
4286 mmdevice = WINMM_GetMixerMMDevice(hmix, fdwInfo, &mmdev_index);
4287 if(!mmdevice)
4288 return MMSYSERR_INVALHANDLE;
4290 switch(fdwInfo & MIXER_GETLINEINFOF_QUERYMASK){
4291 case MIXER_GETLINEINFOF_DESTINATION:
4292 return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, lpmliW,
4293 fdwInfo);
4294 case MIXER_GETLINEINFOF_SOURCE:
4295 return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
4296 case MIXER_GETLINEINFOF_COMPONENTTYPE:
4297 return WINMM_GetComponentTypeLineInfo(mmdevice, mmdev_index, lpmliW,
4298 fdwInfo);
4299 case MIXER_GETLINEINFOF_LINEID:
4300 return WINMM_GetLineIDLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
4301 case MIXER_GETLINEINFOF_TARGETTYPE:
4302 FIXME("TARGETTYPE flag not implemented!\n");
4303 return MIXERR_INVALLINE;
4306 TRACE("Returning INVALFLAG on these flags: %x\n", fdwInfo);
4307 return MMSYSERR_INVALFLAG;
4310 /**************************************************************************
4311 * mixerGetLineInfoA [WINMM.@]
4313 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
4314 DWORD fdwInfo)
4316 MIXERLINEW mliW;
4317 UINT ret;
4319 TRACE("(%p, %p, %x)\n", hmix, lpmliA, fdwInfo);
4321 if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
4322 return MMSYSERR_INVALPARAM;
4324 mliW.cbStruct = sizeof(mliW);
4325 switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
4326 case MIXER_GETLINEINFOF_COMPONENTTYPE:
4327 mliW.dwComponentType = lpmliA->dwComponentType;
4328 break;
4329 case MIXER_GETLINEINFOF_DESTINATION:
4330 mliW.dwDestination = lpmliA->dwDestination;
4331 break;
4332 case MIXER_GETLINEINFOF_LINEID:
4333 mliW.dwLineID = lpmliA->dwLineID;
4334 break;
4335 case MIXER_GETLINEINFOF_SOURCE:
4336 mliW.dwDestination = lpmliA->dwDestination;
4337 mliW.dwSource = lpmliA->dwSource;
4338 break;
4339 case MIXER_GETLINEINFOF_TARGETTYPE:
4340 mliW.Target.dwType = lpmliA->Target.dwType;
4341 mliW.Target.wMid = lpmliA->Target.wMid;
4342 mliW.Target.wPid = lpmliA->Target.wPid;
4343 mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
4344 MultiByteToWideChar( CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, sizeof(mliW.Target.szPname)/sizeof(WCHAR));
4345 break;
4346 default:
4347 WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
4348 return MMSYSERR_INVALFLAG;
4351 ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
4353 if(ret == MMSYSERR_NOERROR)
4355 lpmliA->dwDestination = mliW.dwDestination;
4356 lpmliA->dwSource = mliW.dwSource;
4357 lpmliA->dwLineID = mliW.dwLineID;
4358 lpmliA->fdwLine = mliW.fdwLine;
4359 lpmliA->dwUser = mliW.dwUser;
4360 lpmliA->dwComponentType = mliW.dwComponentType;
4361 lpmliA->cChannels = mliW.cChannels;
4362 lpmliA->cConnections = mliW.cConnections;
4363 lpmliA->cControls = mliW.cControls;
4364 WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
4365 sizeof(lpmliA->szShortName), NULL, NULL);
4366 WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
4367 sizeof(lpmliA->szName), NULL, NULL );
4368 lpmliA->Target.dwType = mliW.Target.dwType;
4369 lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
4370 lpmliA->Target.wMid = mliW.Target.wMid;
4371 lpmliA->Target.wPid = mliW.Target.wPid;
4372 lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
4373 WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
4374 sizeof(lpmliA->Target.szPname), NULL, NULL );
4376 return ret;
4379 /**************************************************************************
4380 * mixerSetControlDetails [WINMM.@]
4382 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
4383 DWORD fdwDetails)
4385 WINMM_ControlDetails details;
4386 UINT ret;
4388 TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
4390 if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
4391 MIXER_SETCONTROLDETAILSF_CUSTOM)
4392 return MMSYSERR_NOTSUPPORTED;
4394 if(!lpmcd)
4395 return MMSYSERR_INVALPARAM;
4397 if(!WINMM_StartDevicesThread())
4398 return MMSYSERR_NODRIVER;
4400 TRACE("dwControlID: %u\n", lpmcd->dwControlID);
4402 details.hmix = hmix;
4403 details.details = lpmcd;
4404 details.flags = fdwDetails;
4406 ret = SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
4407 (DWORD_PTR)&details, 0);
4408 InterlockedDecrement(&g_devthread_token);
4409 return ret;
4412 /**************************************************************************
4413 * mixerMessage [WINMM.@]
4415 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
4417 TRACE("(%p, %d, %lx, %lx)\n", hmix, uMsg, dwParam1, dwParam2);
4419 return MMSYSERR_NOTSUPPORTED;