"Fossies" - the Fresh Open Source Software Archive

Member "wine-6.0.1/dlls/winmm/waveform.c" (7 Jun 2021, 133314 Bytes) of package /linux/misc/wine-6.0.1.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "waveform.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 6.0_vs_6.0.1.

    1 /*
    2  * Copyright 1993      Martin Ayotte
    3  *           1998-2002 Eric Pouech
    4  *           2011 Andrew Eikum for CodeWeavers
    5  *
    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.
   10  *
   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.
   15  *
   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
   19  */
   20 
   21 #include <stdio.h>
   22 #include <stdarg.h>
   23 #include <string.h>
   24 
   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"
   37 
   38 #include "winemm.h"
   39 #include "resource.h"
   40 
   41 #include "ole2.h"
   42 #include "initguid.h"
   43 #include "devpkey.h"
   44 #include "mmdeviceapi.h"
   45 #include "audioclient.h"
   46 #include "audiopolicy.h"
   47 
   48 #include "wine/debug.h"
   49 
   50 WINE_DEFAULT_DEBUG_CHANNEL(winmm);
   51 
   52 /* HWAVE (and HMIXER) format:
   53  *
   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
   60  *
   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.
   64  */
   65 
   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
   70 
   71 typedef struct _WINMM_CBInfo {
   72     DWORD_PTR callback;
   73     DWORD_PTR user;
   74     DWORD flags;
   75     HWAVE hwave;
   76 } WINMM_CBInfo;
   77 
   78 struct _WINMM_MMDevice;
   79 typedef struct _WINMM_MMDevice WINMM_MMDevice;
   80 
   81 typedef struct _WINMM_Device {
   82     WINMM_CBInfo cb_info;
   83 
   84     HWAVE handle;
   85 
   86     BOOL open;
   87 
   88     IMMDevice *device;
   89     IAudioClient *client;
   90     IAudioRenderClient *render;
   91     IAudioCaptureClient *capture;
   92     IAudioClock *clock;
   93     IAudioStreamVolume *volume;
   94 
   95     WAVEFORMATEX *orig_fmt;
   96     HACMSTREAM acm_handle;
   97     ACMSTREAMHEADER acm_hdr;
   98     UINT32 acm_offs;
   99 
  100     WAVEHDR *first, *last, *playing, *loop_start;
  101 
  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 */
  106 
  107     /* stored in frames of sample rate, *not* AC::GetFrequency */
  108     UINT64 last_clock_pos;
  109 
  110     HANDLE event;
  111     CRITICAL_SECTION lock;
  112 
  113     WINMM_MMDevice *parent;
  114 } WINMM_Device;
  115 
  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;
  121 
  122     ISimpleAudioVolume *volume;
  123 
  124     GUID session;
  125 
  126     UINT index;
  127 
  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;
  131 
  132     CRITICAL_SECTION lock;
  133 
  134     WINMM_Device *devices[MAX_DEVICES];
  135 };
  136 
  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];
  141 
  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];
  146 
  147 static IMMDeviceEnumerator *g_devenum;
  148 
  149 static CRITICAL_SECTION g_devthread_lock;
  150 static CRITICAL_SECTION_DEBUG g_devthread_lock_debug =
  151 {
  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") }
  155 };
  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;
  161 
  162 static UINT g_devhandle_count;
  163 static HANDLE *g_device_handles;
  164 static WINMM_Device **g_handle_devices;
  165 
  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;
  175 
  176 typedef struct _WINMM_ControlDetails {
  177     HMIXEROBJ hmix;
  178     MIXERCONTROLDETAILS *details;
  179     DWORD flags;
  180 } WINMM_ControlDetails;
  181 
  182 typedef struct _WINMM_QueryInterfaceInfo {
  183     BOOL is_out;
  184     UINT index;
  185     WCHAR *str;
  186     UINT *len_bytes;
  187 } WINMM_QueryInterfaceInfo;
  188 
  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);
  194 
  195 void WINMM_DeleteWaveform(void)
  196 {
  197     UINT i, j;
  198 
  199     if(g_devices_thread)
  200         CloseHandle(g_devices_thread);
  201 
  202     for(i = 0; i < g_outmmdevices_count; ++i){
  203         WINMM_MMDevice *mmdevice = &g_out_mmdevices[i];
  204 
  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             device->lock.DebugInfo->Spare[0] = 0;
  210             DeleteCriticalSection(&device->lock);
  211         }
  212 
  213         if(mmdevice->volume)
  214             ISimpleAudioVolume_Release(mmdevice->volume);
  215         CoTaskMemFree(mmdevice->dev_id);
  216         mmdevice->lock.DebugInfo->Spare[0] = 0;
  217         DeleteCriticalSection(&mmdevice->lock);
  218     }
  219 
  220     for(i = 0; i < g_inmmdevices_count; ++i){
  221         WINMM_MMDevice *mmdevice = &g_in_mmdevices[i];
  222 
  223         for(j = 0; j < MAX_DEVICES && mmdevice->devices[j]; ++j){
  224             WINMM_Device *device = mmdevice->devices[j];
  225             if(device->handle)
  226                 CloseHandle(device->handle);
  227             device->lock.DebugInfo->Spare[0] = 0;
  228             DeleteCriticalSection(&device->lock);
  229         }
  230 
  231         if(mmdevice->volume)
  232             ISimpleAudioVolume_Release(mmdevice->volume);
  233         CoTaskMemFree(mmdevice->dev_id);
  234         mmdevice->lock.DebugInfo->Spare[0] = 0;
  235         DeleteCriticalSection(&mmdevice->lock);
  236     }
  237 
  238     HeapFree(GetProcessHeap(), 0, g_out_mmdevices);
  239     HeapFree(GetProcessHeap(), 0, g_in_mmdevices);
  240 
  241     HeapFree(GetProcessHeap(), 0, g_device_handles);
  242     HeapFree(GetProcessHeap(), 0, g_handle_devices);
  243 
  244     DeleteCriticalSection(&g_devthread_lock);
  245 }
  246 
  247 static inline HWAVE WINMM_MakeHWAVE(UINT mmdevice, BOOL is_out, UINT device)
  248 {
  249     return ULongToHandle((1 << 15) | ((!!is_out) << 14) |
  250             (mmdevice << 8) | device);
  251 }
  252 
  253 static inline void WINMM_DecomposeHWAVE(HWAVE hwave, UINT *mmdevice_index,
  254         BOOL *is_out, UINT *device_index, UINT *junk)
  255 {
  256     ULONG32 l = HandleToULong(hwave);
  257     *device_index = l & 0xFF;
  258     *mmdevice_index = (l >> 8) & 0x3F;
  259     *is_out = (l >> 14) & 0x1;
  260     *junk = l >> 15;
  261 }
  262 
  263 static void WINMM_InitDevice(WINMM_Device *device)
  264 {
  265     InitializeCriticalSection(&device->lock);
  266     device->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM_Device.lock");
  267 }
  268 
  269 static inline WINMM_MMDevice *read_map(WINMM_MMDevice **map, UINT index)
  270 {
  271     WINMM_MMDevice *ret;
  272     EnterCriticalSection(&g_devthread_lock);
  273     ret = map[index];
  274     LeaveCriticalSection(&g_devthread_lock);
  275     return ret;
  276 }
  277 
  278 /* finds the first unused Device, marks it as "open", and returns
  279  * a pointer to the device
  280  *
  281  * IMPORTANT: it is the caller's responsibility to release the device's lock
  282  * on success
  283  */
  284 static WINMM_Device *WINMM_FindUnusedDevice(WINMM_Device **devices,
  285         WINMM_MMDevice *parent, UINT internal_index, BOOL is_out)
  286 {
  287     UINT i;
  288 
  289     for(i = 0; i < MAX_DEVICES; ++i){
  290         WINMM_Device *device = devices[i];
  291 
  292         if(!device){
  293             device = devices[i] = HeapAlloc(GetProcessHeap(),
  294                     HEAP_ZERO_MEMORY, sizeof(WINMM_Device));
  295             if(!device)
  296                 return NULL;
  297 
  298             WINMM_InitDevice(device);
  299             EnterCriticalSection(&device->lock);
  300         }else
  301             EnterCriticalSection(&device->lock);
  302 
  303         if(!device->open){
  304             device->handle = WINMM_MakeHWAVE(internal_index, is_out, i);
  305             device->parent = parent;
  306             device->open = TRUE;
  307 
  308             return device;
  309         }
  310 
  311         LeaveCriticalSection(&device->lock);
  312     }
  313 
  314     TRACE("All devices in use: mmdevice: %u\n", internal_index);
  315 
  316     return NULL;
  317 }
  318 
  319 static inline BOOL WINMM_ValidateAndLock(WINMM_Device *device)
  320 {
  321     if(!device)
  322         return FALSE;
  323 
  324     EnterCriticalSection(&device->lock);
  325 
  326     if(!device->open){
  327         LeaveCriticalSection(&device->lock);
  328         return FALSE;
  329     }
  330 
  331     return TRUE;
  332 }
  333 
  334 static WINMM_Device *WINMM_GetDeviceFromHWAVE(HWAVE hwave)
  335 {
  336     WINMM_MMDevice *mmdevice;
  337     WINMM_Device *device;
  338     UINT mmdevice_index, device_index, junk;
  339     BOOL is_out;
  340 
  341     WINMM_DecomposeHWAVE(hwave, &mmdevice_index, &is_out, &device_index, &junk);
  342 
  343     if(junk != 0x1)
  344         return NULL;
  345 
  346     if(mmdevice_index == MAPPER_INDEX){
  347         EnterCriticalSection(&g_devthread_lock);
  348         if(is_out)
  349             device = g_out_mapper_devices[device_index];
  350         else
  351             device = g_in_mapper_devices[device_index];
  352         LeaveCriticalSection(&g_devthread_lock);
  353         return device;
  354     }
  355 
  356     if(mmdevice_index >= (is_out ? g_outmmdevices_count : g_inmmdevices_count))
  357         return NULL;
  358 
  359     if(is_out)
  360         mmdevice = &g_out_mmdevices[mmdevice_index];
  361     else
  362         mmdevice = &g_in_mmdevices[mmdevice_index];
  363 
  364     EnterCriticalSection(&mmdevice->lock);
  365 
  366     device = mmdevice->devices[device_index];
  367 
  368     LeaveCriticalSection(&mmdevice->lock);
  369 
  370     return device;
  371 }
  372 
  373 /* Note: NotifyClient should never be called while holding the device lock
  374  * since the client may call wave* functions from within the callback. */
  375 static inline void WINMM_NotifyClient(WINMM_CBInfo *info, WORD msg, DWORD_PTR param1,
  376         DWORD_PTR param2)
  377 {
  378     DriverCallback(info->callback, info->flags, (HDRVR)info->hwave,
  379         msg, info->user, param1, param2);
  380 }
  381 
  382 static MMRESULT hr2mmr(HRESULT hr)
  383 {
  384     switch(hr){
  385     case S_OK:
  386     case AUDCLNT_E_NOT_STOPPED:
  387         return MMSYSERR_NOERROR;
  388     case AUDCLNT_E_UNSUPPORTED_FORMAT:
  389         return WAVERR_BADFORMAT;
  390     case AUDCLNT_E_DEVICE_IN_USE:
  391         return MMSYSERR_ALLOCATED;
  392     case AUDCLNT_E_ENDPOINT_CREATE_FAILED:
  393         return MMSYSERR_NOTENABLED;
  394     case E_OUTOFMEMORY:
  395         return MMSYSERR_NOMEM;
  396     case E_POINTER:
  397     case E_INVALIDARG:
  398         return MMSYSERR_INVALPARAM;
  399     case AUDCLNT_E_DEVICE_INVALIDATED: /* DSERR_BUFFERLOST */
  400     default:
  401         return FAILED(hr) ? MMSYSERR_ERROR : MMSYSERR_NOERROR;
  402     }
  403 }
  404 
  405 static HRESULT WINMM_GetFriendlyName(IMMDevice *device, WCHAR *out,
  406         UINT outlen)
  407 {
  408     IPropertyStore *ps;
  409     PROPVARIANT var;
  410     HRESULT hr;
  411 
  412     hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
  413     if(FAILED(hr))
  414         return hr;
  415 
  416     PropVariantInit(&var);
  417 
  418     hr = IPropertyStore_GetValue(ps,
  419             (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &var);
  420     if(FAILED(hr)){
  421         IPropertyStore_Release(ps);
  422         return hr;
  423     }
  424 
  425     lstrcpynW(out, var.u.pwszVal, outlen);
  426 
  427     PropVariantClear(&var);
  428 
  429     IPropertyStore_Release(ps);
  430 
  431     return S_OK;
  432 }
  433 
  434 static HRESULT WINMM_TestFormat(IAudioClient *client, DWORD rate, DWORD depth,
  435         WORD channels)
  436 {
  437     WAVEFORMATEX fmt, *junk;
  438     HRESULT hr;
  439 
  440     fmt.wFormatTag = WAVE_FORMAT_PCM;
  441     fmt.nChannels = channels;
  442     fmt.nSamplesPerSec = rate;
  443     fmt.wBitsPerSample = depth;
  444     fmt.nBlockAlign = (channels * depth) / 8;
  445     fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign;
  446     fmt.cbSize = 0;
  447 
  448     hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED,
  449             &fmt, &junk);
  450     if(SUCCEEDED(hr))
  451         CoTaskMemFree(junk);
  452 
  453     return hr;
  454 }
  455 
  456 static struct _TestFormat {
  457     DWORD flag;
  458     DWORD rate;
  459     DWORD depth;
  460     WORD channels;
  461 } formats_to_test[] = {
  462     { WAVE_FORMAT_1M08, 11025, 8, 1 },
  463     { WAVE_FORMAT_1M16, 11025, 16, 1 },
  464     { WAVE_FORMAT_1S08, 11025, 8, 2 },
  465     { WAVE_FORMAT_1S16, 11025, 16, 2 },
  466     { WAVE_FORMAT_2M08, 22050, 8, 1 },
  467     { WAVE_FORMAT_2M16, 22050, 16, 1 },
  468     { WAVE_FORMAT_2S08, 22050, 8, 2 },
  469     { WAVE_FORMAT_2S16, 22050, 16, 2 },
  470     { WAVE_FORMAT_4M08, 44100, 8, 1 },
  471     { WAVE_FORMAT_4M16, 44100, 16, 1 },
  472     { WAVE_FORMAT_4S08, 44100, 8, 2 },
  473     { WAVE_FORMAT_4S16, 44100, 16, 2 },
  474     { WAVE_FORMAT_48M08, 48000, 8, 1 },
  475     { WAVE_FORMAT_48M16, 48000, 16, 1 },
  476     { WAVE_FORMAT_48S08, 48000, 8, 2 },
  477     { WAVE_FORMAT_48S16, 48000, 16, 2 },
  478     { WAVE_FORMAT_96M08, 96000, 8, 1 },
  479     { WAVE_FORMAT_96M16, 96000, 16, 1 },
  480     { WAVE_FORMAT_96S08, 96000, 8, 2 },
  481     { WAVE_FORMAT_96S16, 96000, 16, 2 },
  482     {0}
  483 };
  484 
  485 static DWORD WINMM_GetSupportedFormats(IMMDevice *device)
  486 {
  487     DWORD flags = 0;
  488     HRESULT hr;
  489     struct _TestFormat *fmt;
  490     IAudioClient *client;
  491 
  492     hr = IMMDevice_Activate(device, &IID_IAudioClient,
  493             CLSCTX_INPROC_SERVER, NULL, (void**)&client);
  494     if(FAILED(hr))
  495         return 0;
  496 
  497     for(fmt = formats_to_test; fmt->flag; ++fmt){
  498         hr = WINMM_TestFormat(client, fmt->rate, fmt->depth, fmt->channels);
  499         if(hr == S_OK)
  500             flags |= fmt->flag;
  501     }
  502 
  503     IAudioClient_Release(client);
  504 
  505     return flags;
  506 }
  507 
  508 static HRESULT WINMM_InitMMDevice(EDataFlow flow, IMMDevice *device,
  509         WINMM_MMDevice *dev, UINT index)
  510 {
  511     HRESULT hr;
  512 
  513     dev->dataflow = flow;
  514     if(flow == eRender){
  515         dev->out_caps.wMid = 0xFF;
  516         dev->out_caps.wPid = 0xFF;
  517         dev->out_caps.vDriverVersion = 0x00010001;
  518         dev->out_caps.dwFormats = WINMM_GetSupportedFormats(device);
  519         dev->out_caps.wReserved1 = 0;
  520         dev->out_caps.dwSupport = WAVECAPS_LRVOLUME | WAVECAPS_VOLUME |
  521             WAVECAPS_SAMPLEACCURATE;
  522         dev->out_caps.wChannels = 2;
  523         dev->out_caps.szPname[0] = '\0';
  524 
  525         hr = WINMM_GetFriendlyName(device, dev->out_caps.szPname,
  526                 sizeof(dev->out_caps.szPname) /
  527                 sizeof(*dev->out_caps.szPname));
  528         if(FAILED(hr))
  529             return hr;
  530     }else{
  531         dev->in_caps.wMid = 0xFF;
  532         dev->in_caps.wPid = 0xFF;
  533         dev->in_caps.vDriverVersion = 0x00010001;
  534         dev->in_caps.dwFormats = WINMM_GetSupportedFormats(device);
  535         dev->in_caps.wReserved1 = 0;
  536         dev->in_caps.wChannels = 2;
  537         dev->in_caps.szPname[0] = '\0';
  538 
  539         hr = WINMM_GetFriendlyName(device, dev->in_caps.szPname,
  540                 sizeof(dev->in_caps.szPname) /
  541                 sizeof(*dev->in_caps.szPname));
  542         if(FAILED(hr))
  543             return hr;
  544     }
  545 
  546     hr = IMMDevice_GetId(device, &dev->dev_id);
  547     if(FAILED(hr))
  548         return hr;
  549 
  550     CoCreateGuid(&dev->session);
  551 
  552     dev->index = index;
  553 
  554     InitializeCriticalSection(&dev->lock);
  555     dev->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": WINMM_Device.lock");
  556 
  557     return S_OK;
  558 }
  559 
  560 static HRESULT WINMM_EnumDevices(WINMM_MMDevice **devices,
  561         WINMM_MMDevice ***map, UINT *devcount, EDataFlow flow,
  562         IMMDeviceEnumerator *devenum)
  563 {
  564     IMMDeviceCollection *devcoll;
  565     HRESULT hr;
  566 
  567     hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flow,
  568             DEVICE_STATE_ACTIVE, &devcoll);
  569     if(FAILED(hr))
  570         return hr;
  571 
  572     hr = IMMDeviceCollection_GetCount(devcoll, devcount);
  573     if(FAILED(hr)){
  574         IMMDeviceCollection_Release(devcoll);
  575         return hr;
  576     }
  577 
  578     if(*devcount > 0){
  579         UINT n, count = 1;
  580         IMMDevice *def_dev = NULL;
  581 
  582         *devices = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
  583                 sizeof(WINMM_MMDevice) * (*devcount));
  584         if(!*devices){
  585             IMMDeviceCollection_Release(devcoll);
  586             return E_OUTOFMEMORY;
  587         }
  588 
  589         *map = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
  590                 sizeof(WINMM_MMDevice *) * (*devcount));
  591         if(!*map){
  592             IMMDeviceCollection_Release(devcoll);
  593             HeapFree(GetProcessHeap(), 0, *devices);
  594             return E_OUTOFMEMORY;
  595         }
  596 
  597         /* make sure that device 0 is the default device */
  598         IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum,
  599                 flow, eConsole, &def_dev);
  600 
  601         for(n = 0; n < *devcount; ++n){
  602             IMMDevice *device;
  603 
  604             hr = IMMDeviceCollection_Item(devcoll, n, &device);
  605             if(SUCCEEDED(hr)){
  606                 WINMM_InitMMDevice(flow, device, &(*devices)[n], n);
  607 
  608                 if(device == def_dev)
  609                     (*map)[0] = &(*devices)[n];
  610                 else{
  611                     (*map)[count] = &(*devices)[n];
  612                     ++count;
  613                 }
  614 
  615                 IMMDevice_Release(device);
  616             }
  617         }
  618 
  619         IMMDevice_Release(def_dev);
  620 
  621         *devcount = count;
  622     }
  623 
  624     IMMDeviceCollection_Release(devcoll);
  625 
  626     return S_OK;
  627 }
  628 
  629 static HRESULT WINAPI notif_QueryInterface(IMMNotificationClient *iface,
  630         const GUID *riid, void **obj)
  631 {
  632     ERR("Unexpected QueryInterface call: %s\n", wine_dbgstr_guid(riid));
  633     return E_NOINTERFACE;
  634 }
  635 
  636 static ULONG WINAPI notif_AddRef(IMMNotificationClient *iface)
  637 {
  638     return 2;
  639 }
  640 
  641 static ULONG WINAPI notif_Release(IMMNotificationClient *iface)
  642 {
  643     return 1;
  644 }
  645 
  646 static HRESULT WINAPI notif_OnDeviceStateChanged(IMMNotificationClient *iface,
  647         const WCHAR *device_id, DWORD new_state)
  648 {
  649     TRACE("Ignoring OnDeviceStateChanged callback\n");
  650     return S_OK;
  651 }
  652 
  653 static HRESULT WINAPI notif_OnDeviceAdded(IMMNotificationClient *iface,
  654         const WCHAR *device_id)
  655 {
  656     TRACE("Ignoring OnDeviceAdded callback\n");
  657     return S_OK;
  658 }
  659 
  660 static HRESULT WINAPI notif_OnDeviceRemoved(IMMNotificationClient *iface,
  661         const WCHAR *device_id)
  662 {
  663     TRACE("Ignoring OnDeviceRemoved callback\n");
  664     return S_OK;
  665 }
  666 
  667 static HRESULT update_mapping(WINMM_MMDevice ***map, UINT count,
  668         const WCHAR *default_id)
  669 {
  670     WINMM_MMDevice *prev;
  671     UINT i;
  672 
  673     prev = (*map)[0];
  674     for(i = 0; i < count; ++i){
  675         WINMM_MMDevice *tmp;
  676 
  677         if(!wcscmp((*map)[i]->dev_id, default_id)){
  678             (*map)[0] = (*map)[i];
  679             (*map)[i] = prev;
  680 
  681             return S_OK;
  682         }
  683 
  684         tmp = (*map)[i];
  685         (*map)[i] = prev;
  686         prev = tmp;
  687     }
  688 
  689     WARN("Couldn't find new default device! Rearranged map for no reason.\n");
  690     (*map)[0] = prev;
  691 
  692     return S_FALSE;
  693 }
  694 
  695 static HRESULT reroute_mapper_device(WINMM_Device *device, BOOL is_out)
  696 {
  697     WINMM_OpenInfo info;
  698     BOOL stopped;
  699     MMRESULT mr;
  700     HRESULT hr;
  701     UINT64 clock_freq, clock_pos;
  702 
  703     TRACE("rerouting device %p\n", device->handle);
  704 
  705     EnterCriticalSection(&device->lock);
  706 
  707     if(!device->open || device->acm_handle){
  708         /* Windows 7 doesn't re-route ACM devices, so we don't either.
  709          * Seems to be because of the data waveXxxPrepareHeader allocates. */
  710         LeaveCriticalSection(&device->lock);
  711         return S_FALSE;
  712     }
  713 
  714     stopped = device->stopped;
  715 
  716     info.handle = 0;
  717     info.req_device = WAVE_MAPPER;
  718     info.format = device->orig_fmt;
  719     info.callback = device->cb_info.callback;
  720     info.cb_user = device->cb_info.user;
  721     /* We have to use direct here so that we don't suddenly introduce ACM
  722      * into a playing stream that hasn't been Prepared for it */
  723     info.flags = (device->cb_info.flags << 16) | WAVE_FORMAT_DIRECT_QUERY;
  724     info.reset = FALSE;
  725 
  726     if(is_out)
  727         mr = WOD_Open(&info);
  728     else
  729         mr = WID_Open(&info);
  730 
  731     if(mr != MMSYSERR_NOERROR){
  732         TRACE("New default device doesn't support this stream: %p\n", device->handle);
  733         LeaveCriticalSection(&device->lock);
  734         return S_FALSE;
  735     }
  736 
  737     hr = IAudioClient_Stop(device->client);
  738     if(FAILED(hr))
  739         WARN("Stop failed: %08x\n", hr);
  740 
  741     hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
  742     if(FAILED(hr)){
  743         WARN("GetFrequency failed: %08x\n", hr);
  744         LeaveCriticalSection(&device->lock);
  745         return hr;
  746     }
  747 
  748     hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
  749     if(FAILED(hr)){
  750         WARN("GetPosition failed: %08x\n", hr);
  751         LeaveCriticalSection(&device->lock);
  752         return hr;
  753     }
  754 
  755     device->remainder_frames = MulDiv(clock_pos, device->samples_per_sec, clock_freq) - device->last_clock_pos;
  756 
  757     info.handle = device->handle;
  758     info.flags = (device->cb_info.flags << 16) | WAVE_FORMAT_DIRECT;
  759 
  760     if(is_out){
  761         WOD_Close((HWAVEOUT)device->handle);
  762         device->parent = read_map(g_out_map, 0);
  763         mr = WOD_Open(&info);
  764     }else{
  765         WID_Close((HWAVEIN)device->handle);
  766         device->parent = read_map(g_in_map, 0);
  767         mr = WID_Open(&info);
  768     }
  769 
  770     if(mr != MMSYSERR_NOERROR){
  771         ERR("Opening new default device failed! %u\n", mr);
  772         LeaveCriticalSection(&device->lock);
  773         return E_FAIL;
  774     }
  775 
  776     HeapFree(GetProcessHeap(), 0, info.format);
  777 
  778     if(!stopped)
  779         WINMM_BeginPlaying(device);
  780 
  781     LeaveCriticalSection(&device->lock);
  782 
  783     return S_OK;
  784 }
  785 
  786 static HRESULT WINAPI notif_OnDefaultDeviceChanged(IMMNotificationClient *iface,
  787         EDataFlow flow, ERole role, const WCHAR *device_id)
  788 {
  789     UINT i;
  790 
  791     TRACE("%u %u %s\n", flow, role, wine_dbgstr_w(device_id));
  792 
  793     if(role != eConsole)
  794         return S_OK;
  795 
  796     EnterCriticalSection(&g_devthread_lock);
  797 
  798     if(flow == eRender)
  799         update_mapping(&g_out_map, g_outmmdevices_count, device_id);
  800     else
  801         update_mapping(&g_in_map, g_inmmdevices_count, device_id);
  802 
  803     for(i = 0; i < MAX_DEVICES && g_out_mapper_devices[i]; ++i)
  804         reroute_mapper_device(g_out_mapper_devices[i], TRUE);
  805 
  806     for(i = 0; i < MAX_DEVICES && g_in_mapper_devices[i]; ++i)
  807         reroute_mapper_device(g_in_mapper_devices[i], FALSE);
  808 
  809     LeaveCriticalSection(&g_devthread_lock);
  810 
  811     return S_OK;
  812 }
  813 
  814 static HRESULT WINAPI notif_OnPropertyValueChanged(IMMNotificationClient *iface,
  815         const WCHAR *device_id, const PROPERTYKEY key)
  816 {
  817     TRACE("Ignoring OnPropertyValueChanged callback\n");
  818     return S_OK;
  819 }
  820 
  821 static IMMNotificationClientVtbl g_notif_vtbl = {
  822     notif_QueryInterface,
  823     notif_AddRef,
  824     notif_Release,
  825     notif_OnDeviceStateChanged,
  826     notif_OnDeviceAdded,
  827     notif_OnDeviceRemoved,
  828     notif_OnDefaultDeviceChanged,
  829     notif_OnPropertyValueChanged
  830 };
  831 
  832 static IMMNotificationClient g_notif = { &g_notif_vtbl };
  833 
  834 static HRESULT WINMM_InitMMDevices(void)
  835 {
  836     HRESULT hr, init_hr;
  837     IMMDeviceEnumerator *devenum = NULL;
  838 
  839     if(g_outmmdevices_count || g_inmmdevices_count)
  840         return S_FALSE;
  841 
  842     init_hr = CoInitialize(NULL);
  843 
  844     hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
  845             CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&devenum);
  846     if(FAILED(hr))
  847         goto exit;
  848 
  849     hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(devenum, &g_notif);
  850     if(FAILED(hr))
  851         WARN("RegisterEndpointNotificationCallback failed: %08x\n", hr);
  852 
  853     hr = WINMM_EnumDevices(&g_out_mmdevices, &g_out_map, &g_outmmdevices_count,
  854             eRender, devenum);
  855     if(FAILED(hr)){
  856         g_outmmdevices_count = 0;
  857         g_inmmdevices_count = 0;
  858         goto exit;
  859     }
  860 
  861     hr = WINMM_EnumDevices(&g_in_mmdevices, &g_in_map, &g_inmmdevices_count,
  862             eCapture, devenum);
  863     if(FAILED(hr)){
  864         g_inmmdevices_count = 0;
  865         goto exit;
  866     }
  867 
  868 exit:
  869     if(devenum)
  870         IMMDeviceEnumerator_Release(devenum);
  871     if(SUCCEEDED(init_hr))
  872         CoUninitialize();
  873 
  874     return hr;
  875 }
  876 
  877 static inline BOOL WINMM_IsMapper(UINT device)
  878 {
  879     return (device == WAVE_MAPPER || device == (UINT16)WAVE_MAPPER);
  880 }
  881 
  882 static MMRESULT WINMM_TryDeviceMapping(WINMM_Device *device, WAVEFORMATEX *fmt,
  883         WORD channels, DWORD freq, DWORD bits_per_samp, BOOL is_query, BOOL is_out)
  884 {
  885     WAVEFORMATEX target, *closer_fmt = NULL;
  886     HRESULT hr;
  887     MMRESULT mr;
  888 
  889     TRACE("format: %u, channels: %u, sample rate: %u, bit depth: %u\n",
  890             WAVE_FORMAT_PCM, channels, freq, bits_per_samp);
  891 
  892     target.wFormatTag = WAVE_FORMAT_PCM;
  893     target.nChannels = channels;
  894     target.nSamplesPerSec = freq;
  895     target.wBitsPerSample = bits_per_samp;
  896     target.nBlockAlign = (target.nChannels * target.wBitsPerSample) / 8;
  897     target.nAvgBytesPerSec = target.nSamplesPerSec * target.nBlockAlign;
  898     target.cbSize = 0;
  899 
  900     hr = IAudioClient_IsFormatSupported(device->client,
  901             AUDCLNT_SHAREMODE_SHARED, &target, &closer_fmt);
  902     CoTaskMemFree(closer_fmt);
  903     if(hr != S_OK)
  904         return WAVERR_BADFORMAT;
  905 
  906     /* device supports our target format, so see if MSACM can
  907      * do the conversion */
  908     if(is_out)
  909         mr = acmStreamOpen(&device->acm_handle, NULL, fmt, &target, NULL,
  910                 0, 0, 0);
  911     else
  912         mr = acmStreamOpen(&device->acm_handle, NULL, &target, fmt, NULL,
  913                 0, 0, 0);
  914     if(mr != MMSYSERR_NOERROR)
  915         return mr;
  916 
  917     /* yes it can. initialize the audioclient and return success */
  918     if(is_query){
  919         acmStreamClose(device->acm_handle, 0);
  920         device->acm_handle = NULL;
  921         return MMSYSERR_NOERROR;
  922     }
  923 
  924     hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED,
  925             AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
  926             AC_BUFLEN, 0, &target, &device->parent->session);
  927     if(hr != S_OK){
  928         WARN("Initialize failed: %08x\n", hr);
  929         acmStreamClose(device->acm_handle, 0);
  930         device->acm_handle = NULL;
  931         return MMSYSERR_ERROR;
  932     }
  933 
  934     device->bytes_per_frame = target.nBlockAlign;
  935     device->samples_per_sec = target.nSamplesPerSec;
  936 
  937     TRACE("Success!\n");
  938 
  939     return MMSYSERR_NOERROR;
  940 }
  941 
  942 static MMRESULT WINMM_MapDevice(WINMM_Device *device, BOOL is_query, BOOL is_out)
  943 {
  944     MMRESULT mr;
  945     WAVEFORMATEXTENSIBLE *fmtex = (WAVEFORMATEXTENSIBLE*)device->orig_fmt;
  946 
  947     TRACE("(%p, %u)\n", device, is_out);
  948 
  949     /* set up the ACM stream */
  950     if(device->orig_fmt->wFormatTag != WAVE_FORMAT_PCM &&
  951             !(device->orig_fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
  952               IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM))){
  953         /* convert to PCM format if it's not already */
  954         mr = WINMM_TryDeviceMapping(device, device->orig_fmt,
  955                 device->orig_fmt->nChannels, device->orig_fmt->nSamplesPerSec,
  956                 16, is_query, is_out);
  957         if(mr == MMSYSERR_NOERROR)
  958             return mr;
  959 
  960         mr = WINMM_TryDeviceMapping(device, device->orig_fmt,
  961                 device->orig_fmt->nChannels, device->orig_fmt->nSamplesPerSec,
  962                 8, is_query, is_out);
  963         if(mr == MMSYSERR_NOERROR)
  964             return mr;
  965     }else{
  966         WORD channels;
  967 
  968         /* first try just changing bit depth and channels */
  969         channels = device->orig_fmt->nChannels;
  970         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
  971                 device->orig_fmt->nSamplesPerSec, 16, is_query, is_out);
  972         if(mr == MMSYSERR_NOERROR)
  973             return mr;
  974         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
  975                 device->orig_fmt->nSamplesPerSec, 8, is_query, is_out);
  976         if(mr == MMSYSERR_NOERROR)
  977             return mr;
  978 
  979         channels = (channels == 2) ? 1 : 2;
  980         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
  981                 device->orig_fmt->nSamplesPerSec, 16, is_query, is_out);
  982         if(mr == MMSYSERR_NOERROR)
  983             return mr;
  984         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels,
  985                 device->orig_fmt->nSamplesPerSec, 8, is_query, is_out);
  986         if(mr == MMSYSERR_NOERROR)
  987             return mr;
  988 
  989         /* that didn't work, so now try different sample rates */
  990         channels = device->orig_fmt->nChannels;
  991         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 16, is_query, is_out);
  992         if(mr == MMSYSERR_NOERROR)
  993             return mr;
  994         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 16, is_query, is_out);
  995         if(mr == MMSYSERR_NOERROR)
  996             return mr;
  997         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 16, is_query, is_out);
  998         if(mr == MMSYSERR_NOERROR)
  999             return mr;
 1000         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 16, is_query, is_out);
 1001         if(mr == MMSYSERR_NOERROR)
 1002             return mr;
 1003         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 16, is_query, is_out);
 1004         if(mr == MMSYSERR_NOERROR)
 1005             return mr;
 1006 
 1007         channels = (channels == 2) ? 1 : 2;
 1008         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 16, is_query, is_out);
 1009         if(mr == MMSYSERR_NOERROR)
 1010             return mr;
 1011         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 16, is_query, is_out);
 1012         if(mr == MMSYSERR_NOERROR)
 1013             return mr;
 1014         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 16, is_query, is_out);
 1015         if(mr == MMSYSERR_NOERROR)
 1016             return mr;
 1017         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 16, is_query, is_out);
 1018         if(mr == MMSYSERR_NOERROR)
 1019             return mr;
 1020         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 16, is_query, is_out);
 1021         if(mr == MMSYSERR_NOERROR)
 1022             return mr;
 1023 
 1024         channels = device->orig_fmt->nChannels;
 1025         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 8, is_query, is_out);
 1026         if(mr == MMSYSERR_NOERROR)
 1027             return mr;
 1028         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 8, is_query, is_out);
 1029         if(mr == MMSYSERR_NOERROR)
 1030             return mr;
 1031         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 8, is_query, is_out);
 1032         if(mr == MMSYSERR_NOERROR)
 1033             return mr;
 1034         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 8, is_query, is_out);
 1035         if(mr == MMSYSERR_NOERROR)
 1036             return mr;
 1037         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 8, is_query, is_out);
 1038         if(mr == MMSYSERR_NOERROR)
 1039             return mr;
 1040 
 1041         channels = (channels == 2) ? 1 : 2;
 1042         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 96000, 8, is_query, is_out);
 1043         if(mr == MMSYSERR_NOERROR)
 1044             return mr;
 1045         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 48000, 8, is_query, is_out);
 1046         if(mr == MMSYSERR_NOERROR)
 1047             return mr;
 1048         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 44100, 8, is_query, is_out);
 1049         if(mr == MMSYSERR_NOERROR)
 1050             return mr;
 1051         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 22050, 8, is_query, is_out);
 1052         if(mr == MMSYSERR_NOERROR)
 1053             return mr;
 1054         mr = WINMM_TryDeviceMapping(device, device->orig_fmt, channels, 11025, 8, is_query, is_out);
 1055         if(mr == MMSYSERR_NOERROR)
 1056             return mr;
 1057     }
 1058 
 1059     WARN("Unable to find compatible device!\n");
 1060     return WAVERR_BADFORMAT;
 1061 }
 1062 
 1063 static LRESULT WINMM_OpenDevice(WINMM_Device *device, WINMM_OpenInfo *info,
 1064         BOOL is_out)
 1065 {
 1066     LRESULT ret = MMSYSERR_NOMEM;
 1067     HRESULT hr;
 1068 
 1069     hr = IMMDeviceEnumerator_GetDevice(g_devenum, device->parent->dev_id,
 1070             &device->device);
 1071     if(FAILED(hr)){
 1072         WARN("Device %s (%s) unavailable: %08x\n",
 1073                 wine_dbgstr_w(device->parent->dev_id),
 1074                 wine_dbgstr_w(device->parent->out_caps.szPname), hr);
 1075         ret = MMSYSERR_NODRIVER;
 1076         goto error;
 1077     }
 1078 
 1079     /* this is where winexyz.drv opens the audio device */
 1080     hr = IMMDevice_Activate(device->device, &IID_IAudioClient,
 1081             CLSCTX_INPROC_SERVER, NULL, (void**)&device->client);
 1082     if(FAILED(hr)){
 1083         WARN("Activate failed: %08x\n", hr);
 1084         ret = hr2mmr(hr);
 1085         if(ret == MMSYSERR_ERROR)
 1086             ret = MMSYSERR_NOTENABLED;
 1087         goto error;
 1088     }
 1089 
 1090     if(info->format->wFormatTag == WAVE_FORMAT_PCM){
 1091 
 1092         if (info->format->nSamplesPerSec == 0)
 1093         {
 1094             ret = MMSYSERR_INVALPARAM;
 1095             goto error;
 1096         }
 1097 
 1098         /* we aren't guaranteed that the struct in lpFormat is a full
 1099          * WAVEFORMATEX struct, which IAC::IsFormatSupported requires */
 1100         device->orig_fmt = HeapAlloc(GetProcessHeap(), 0, sizeof(WAVEFORMATEX));
 1101         memcpy(device->orig_fmt, info->format, sizeof(PCMWAVEFORMAT));
 1102         device->orig_fmt->cbSize = 0;
 1103         if(device->orig_fmt->wBitsPerSample % 8 != 0){
 1104             WARN("Fixing bad wBitsPerSample (%u)\n", device->orig_fmt->wBitsPerSample);
 1105             device->orig_fmt->wBitsPerSample = (device->orig_fmt->wBitsPerSample + 7) & ~7;
 1106         }
 1107         /* winmm ignores broken blockalign and avgbytes */
 1108         if(device->orig_fmt->nBlockAlign != device->orig_fmt->nChannels * device->orig_fmt->wBitsPerSample/8){
 1109             WARN("Fixing bad nBlockAlign (%u)\n", device->orig_fmt->nBlockAlign);
 1110             device->orig_fmt->nBlockAlign  = device->orig_fmt->nChannels * device->orig_fmt->wBitsPerSample/8;
 1111         }
 1112         if (device->orig_fmt->nAvgBytesPerSec != device->orig_fmt->nSamplesPerSec * device->orig_fmt->nBlockAlign) {
 1113             WARN("Fixing bad nAvgBytesPerSec (%u)\n", device->orig_fmt->nAvgBytesPerSec);
 1114             device->orig_fmt->nAvgBytesPerSec  = device->orig_fmt->nSamplesPerSec * device->orig_fmt->nBlockAlign;
 1115         }
 1116     }else{
 1117         device->orig_fmt = HeapAlloc(GetProcessHeap(), 0,
 1118                 sizeof(WAVEFORMATEX) + info->format->cbSize);
 1119         memcpy(device->orig_fmt, info->format,
 1120                 sizeof(WAVEFORMATEX) + info->format->cbSize);
 1121     }
 1122 
 1123     if(info->flags & WAVE_FORMAT_QUERY){
 1124         WAVEFORMATEX *closer_fmt = NULL;
 1125 
 1126         hr = IAudioClient_IsFormatSupported(device->client,
 1127                 AUDCLNT_SHAREMODE_SHARED, device->orig_fmt, &closer_fmt);
 1128         CoTaskMemFree(closer_fmt);
 1129         if((hr == S_FALSE || hr == AUDCLNT_E_UNSUPPORTED_FORMAT) && !(info->flags & WAVE_FORMAT_DIRECT))
 1130             ret = WINMM_MapDevice(device, TRUE, is_out);
 1131         else
 1132             ret = hr == S_FALSE ? WAVERR_BADFORMAT : hr2mmr(hr);
 1133         goto error;
 1134     }
 1135 
 1136     hr = IAudioClient_Initialize(device->client, AUDCLNT_SHAREMODE_SHARED,
 1137             AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
 1138             AC_BUFLEN, 0, device->orig_fmt, &device->parent->session);
 1139     if(FAILED(hr)){
 1140         if(hr == AUDCLNT_E_UNSUPPORTED_FORMAT && !(info->flags & WAVE_FORMAT_DIRECT)){
 1141             ret = WINMM_MapDevice(device, FALSE, is_out);
 1142             if(ret != MMSYSERR_NOERROR || info->flags & WAVE_FORMAT_QUERY)
 1143                 goto error;
 1144         }else{
 1145             WARN("Initialize failed: %08x\n", hr);
 1146             ret = hr2mmr(hr);
 1147             goto error;
 1148         }
 1149     }else{
 1150         device->bytes_per_frame = device->orig_fmt->nBlockAlign;
 1151         device->samples_per_sec = device->orig_fmt->nSamplesPerSec;
 1152     }
 1153 
 1154     hr = IAudioClient_GetService(device->client, &IID_IAudioClock,
 1155             (void**)&device->clock);
 1156     if(FAILED(hr)){
 1157         WARN("GetService failed: %08x\n", hr);
 1158         goto error;
 1159     }
 1160 
 1161     if(!device->event){
 1162         device->event = CreateEventW(NULL, FALSE, FALSE, NULL);
 1163         if(!device->event){
 1164             WARN("CreateEvent failed: %08x\n", hr);
 1165             goto error;
 1166         }
 1167 
 1168         /* As the devices thread is waiting on g_device_handles, it can
 1169          * only be modified from within this same thread. */
 1170         if(g_device_handles){
 1171             g_device_handles = HeapReAlloc(GetProcessHeap(), 0, g_device_handles,
 1172                     sizeof(HANDLE) * (g_devhandle_count + 1));
 1173             g_handle_devices = HeapReAlloc(GetProcessHeap(), 0, g_handle_devices,
 1174                     sizeof(WINMM_Device *) * (g_devhandle_count + 1));
 1175         }else{
 1176             g_device_handles = HeapAlloc(GetProcessHeap(), 0, sizeof(HANDLE));
 1177             g_handle_devices = HeapAlloc(GetProcessHeap(), 0,
 1178                     sizeof(WINMM_Device *));
 1179         }
 1180         g_device_handles[g_devhandle_count] = device->event;
 1181         g_handle_devices[g_devhandle_count] = device;
 1182         ++g_devhandle_count;
 1183     }
 1184 
 1185     hr = IAudioClient_SetEventHandle(device->client, device->event);
 1186     if(FAILED(hr)){
 1187         WARN("SetEventHandle failed: %08x\n", hr);
 1188         goto error;
 1189     }
 1190 
 1191     if(info->reset){
 1192         device->played_frames = 0;
 1193         device->ofs_bytes = 0;
 1194         device->loop_counter = 0;
 1195         device->first = device->last = device->playing = device->loop_start = NULL;
 1196     }
 1197 
 1198     device->stopped = TRUE;
 1199     device->last_clock_pos = 0;
 1200 
 1201     device->cb_info.flags = HIWORD(info->flags & CALLBACK_TYPEMASK);
 1202     device->cb_info.callback = info->callback;
 1203     device->cb_info.user = info->cb_user;
 1204     device->cb_info.hwave = device->handle;
 1205 
 1206     info->handle = device->handle;
 1207 
 1208     return MMSYSERR_NOERROR;
 1209 
 1210 error:
 1211     if(device->client){
 1212         IAudioClient_Release(device->client);
 1213         device->client = NULL;
 1214     }
 1215     if(device->device){
 1216         IMMDevice_Release(device->device);
 1217         device->device = NULL;
 1218     }
 1219 
 1220     return ret;
 1221 }
 1222 
 1223 static LRESULT WOD_Open(WINMM_OpenInfo *info)
 1224 {
 1225     WINMM_Device *device;
 1226     LRESULT ret = MMSYSERR_ERROR;
 1227     HRESULT hr;
 1228 
 1229     if(info->handle != 0){
 1230         device = WINMM_GetDeviceFromHWAVE(info->handle);
 1231         if(!device){
 1232             WARN("Unexpected! Invalid info->handle given: %p\n", info->handle);
 1233             return MMSYSERR_ERROR;
 1234         }
 1235 
 1236         EnterCriticalSection(&device->lock);
 1237 
 1238         device->open = TRUE;
 1239     }else{
 1240         CRITICAL_SECTION *lock;
 1241         UINT internal_index;
 1242         WINMM_Device **devices;
 1243         WINMM_MMDevice *mmdevice;
 1244 
 1245         if(WINMM_IsMapper(info->req_device)){
 1246             if (g_outmmdevices_count == 0)
 1247                 return MMSYSERR_BADDEVICEID;
 1248             devices = g_out_mapper_devices;
 1249             mmdevice = read_map(g_out_map, 0);
 1250             lock = &g_devthread_lock;
 1251             internal_index = MAPPER_INDEX;
 1252         }else{
 1253             if(info->req_device >= g_outmmdevices_count)
 1254                 return MMSYSERR_BADDEVICEID;
 1255 
 1256             mmdevice = read_map(g_out_map, info->req_device);
 1257 
 1258             if(!mmdevice->out_caps.szPname[0])
 1259                 return MMSYSERR_NOTENABLED;
 1260 
 1261             devices = mmdevice->devices;
 1262             lock = &mmdevice->lock;
 1263             internal_index = mmdevice->index;
 1264         }
 1265 
 1266         EnterCriticalSection(lock);
 1267 
 1268         device = WINMM_FindUnusedDevice(devices, mmdevice,
 1269                 internal_index, TRUE);
 1270         if(!device){
 1271             LeaveCriticalSection(lock);
 1272             return MMSYSERR_ALLOCATED;
 1273         }
 1274 
 1275         LeaveCriticalSection(lock);
 1276     }
 1277 
 1278     ret = WINMM_OpenDevice(device, info, TRUE);
 1279     if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
 1280         goto error;
 1281     ret = MMSYSERR_ERROR;
 1282 
 1283     hr = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
 1284             (void**)&device->render);
 1285     if(FAILED(hr)){
 1286         ERR("GetService failed: %08x\n", hr);
 1287         goto error;
 1288     }
 1289 
 1290     hr = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume,
 1291             (void**)&device->volume);
 1292     if(FAILED(hr)){
 1293         ERR("GetService failed: %08x\n", hr);
 1294         goto error;
 1295     }
 1296 
 1297     LeaveCriticalSection(&device->lock);
 1298 
 1299     return MMSYSERR_NOERROR;
 1300 
 1301 error:
 1302     if(device->device){
 1303         IMMDevice_Release(device->device);
 1304         device->device = NULL;
 1305     }
 1306     if(device->client){
 1307         IAudioClient_Release(device->client);
 1308         device->client = NULL;
 1309     }
 1310     if(device->render){
 1311         IAudioRenderClient_Release(device->render);
 1312         device->render = NULL;
 1313     }
 1314     if(device->volume){
 1315         IAudioStreamVolume_Release(device->volume);
 1316         device->volume = NULL;
 1317     }
 1318     if(device->clock){
 1319         IAudioClock_Release(device->clock);
 1320         device->clock = NULL;
 1321     }
 1322     device->open = FALSE;
 1323     LeaveCriticalSection(&device->lock);
 1324     return ret;
 1325 }
 1326 
 1327 static LRESULT WID_Open(WINMM_OpenInfo *info)
 1328 {
 1329     WINMM_Device *device, **devices;
 1330     WINMM_MMDevice *mmdevice;
 1331     UINT internal_index;
 1332     CRITICAL_SECTION *lock;
 1333     LRESULT ret = MMSYSERR_ERROR;
 1334     HRESULT hr;
 1335 
 1336     if(WINMM_IsMapper(info->req_device)){
 1337         if (g_inmmdevices_count == 0)
 1338             return MMSYSERR_BADDEVICEID;
 1339         devices = g_in_mapper_devices;
 1340         mmdevice = read_map(g_in_map, 0);
 1341         lock = &g_devthread_lock;
 1342         internal_index = MAPPER_INDEX;
 1343     }else{
 1344         if(info->req_device >= g_inmmdevices_count)
 1345             return MMSYSERR_BADDEVICEID;
 1346 
 1347         mmdevice = read_map(g_in_map, info->req_device);
 1348 
 1349         if(!mmdevice->in_caps.szPname[0])
 1350             return MMSYSERR_NOTENABLED;
 1351 
 1352         devices = mmdevice->devices;
 1353         lock = &mmdevice->lock;
 1354         internal_index = mmdevice->index;
 1355     }
 1356 
 1357     EnterCriticalSection(lock);
 1358 
 1359     device = WINMM_FindUnusedDevice(devices, mmdevice, internal_index, FALSE);
 1360     if(!device){
 1361         LeaveCriticalSection(lock);
 1362         return MMSYSERR_ALLOCATED;
 1363     }
 1364 
 1365     LeaveCriticalSection(lock);
 1366 
 1367     ret = WINMM_OpenDevice(device, info, FALSE);
 1368     if((info->flags & WAVE_FORMAT_QUERY) || ret != MMSYSERR_NOERROR)
 1369         goto error;
 1370     ret = MMSYSERR_ERROR;
 1371 
 1372     hr = IAudioClient_GetService(device->client, &IID_IAudioCaptureClient,
 1373             (void**)&device->capture);
 1374     if(FAILED(hr)){
 1375         WARN("GetService failed: %08x\n", hr);
 1376         goto error;
 1377     }
 1378 
 1379     LeaveCriticalSection(&device->lock);
 1380 
 1381     return MMSYSERR_NOERROR;
 1382 
 1383 error:
 1384     if(device->device){
 1385         IMMDevice_Release(device->device);
 1386         device->device = NULL;
 1387     }
 1388     if(device->client){
 1389         IAudioClient_Release(device->client);
 1390         device->client = NULL;
 1391     }
 1392     if(device->capture){
 1393         IAudioCaptureClient_Release(device->capture);
 1394         device->capture = NULL;
 1395     }
 1396     if(device->clock){
 1397         IAudioClock_Release(device->clock);
 1398         device->clock = NULL;
 1399     }
 1400     device->open = FALSE;
 1401     LeaveCriticalSection(&device->lock);
 1402     return ret;
 1403 }
 1404 
 1405 static HRESULT WINMM_CloseDevice(WINMM_Device *device)
 1406 {
 1407     device->open = FALSE;
 1408 
 1409     if(!device->stopped){
 1410         IAudioClient_Stop(device->client);
 1411         device->stopped = TRUE;
 1412     }
 1413 
 1414     if(device->acm_handle){
 1415         acmStreamClose(device->acm_handle, 0);
 1416         device->acm_handle = NULL;
 1417     }
 1418 
 1419     IMMDevice_Release(device->device);
 1420     device->device = NULL;
 1421 
 1422     IAudioClient_Release(device->client);
 1423     device->client = NULL;
 1424 
 1425     IAudioClock_Release(device->clock);
 1426     device->clock = NULL;
 1427 
 1428     HeapFree(GetProcessHeap(), 0, device->orig_fmt);
 1429 
 1430     return S_OK;
 1431 }
 1432 
 1433 static LRESULT WOD_Close(HWAVEOUT hwave)
 1434 {
 1435     WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
 1436 
 1437     TRACE("(%p)\n", hwave);
 1438 
 1439     if(!WINMM_ValidateAndLock(device))
 1440         return MMSYSERR_INVALHANDLE;
 1441 
 1442     WINMM_CloseDevice(device);
 1443 
 1444     IAudioRenderClient_Release(device->render);
 1445     device->render = NULL;
 1446 
 1447     IAudioStreamVolume_Release(device->volume);
 1448     device->volume = NULL;
 1449 
 1450     LeaveCriticalSection(&device->lock);
 1451 
 1452     return MMSYSERR_NOERROR;
 1453 }
 1454 
 1455 static LRESULT WID_Close(HWAVEIN hwave)
 1456 {
 1457     WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)hwave);
 1458 
 1459     TRACE("(%p)\n", hwave);
 1460 
 1461     if(!WINMM_ValidateAndLock(device))
 1462         return MMSYSERR_INVALHANDLE;
 1463 
 1464     WINMM_CloseDevice(device);
 1465 
 1466     IAudioCaptureClient_Release(device->capture);
 1467     device->capture = NULL;
 1468 
 1469     LeaveCriticalSection(&device->lock);
 1470 
 1471     return MMSYSERR_NOERROR;
 1472 }
 1473 
 1474 static DWORD WINMM_FixedBufferLen(DWORD length, WINMM_Device *device)
 1475 {
 1476     return length - length % device->bytes_per_frame;
 1477 }
 1478 
 1479 static LRESULT WINMM_PrepareHeader(HWAVE hwave, WAVEHDR *header)
 1480 {
 1481     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
 1482 
 1483     TRACE("(%p, %p)\n", hwave, header);
 1484 
 1485     if(!WINMM_ValidateAndLock(device))
 1486         return MMSYSERR_INVALHANDLE;
 1487 
 1488     if(device->render && device->acm_handle){
 1489         ACMSTREAMHEADER *ash;
 1490         DWORD size;
 1491         MMRESULT mr;
 1492 
 1493         mr = acmStreamSize(device->acm_handle, header->dwBufferLength, &size,
 1494                 ACM_STREAMSIZEF_SOURCE);
 1495         if(mr != MMSYSERR_NOERROR){
 1496             LeaveCriticalSection(&device->lock);
 1497             return mr;
 1498         }
 1499 
 1500         ash = HeapAlloc(GetProcessHeap(), 0, sizeof(ACMSTREAMHEADER) + size);
 1501         if(!ash){
 1502             LeaveCriticalSection(&device->lock);
 1503             return MMSYSERR_NOMEM;
 1504         }
 1505 
 1506         ash->cbStruct = sizeof(*ash);
 1507         ash->fdwStatus = 0;
 1508         ash->dwUser = (DWORD_PTR)header;
 1509         ash->pbSrc = (BYTE*)header->lpData;
 1510         ash->cbSrcLength = header->dwBufferLength;
 1511         ash->dwSrcUser = header->dwUser;
 1512         ash->pbDst = (BYTE*)ash + sizeof(ACMSTREAMHEADER);
 1513         ash->cbDstLength = size;
 1514         ash->dwDstUser = 0;
 1515 
 1516         mr = acmStreamPrepareHeader(device->acm_handle, ash, 0);
 1517         if(mr != MMSYSERR_NOERROR){
 1518             HeapFree(GetProcessHeap(), 0, ash);
 1519             LeaveCriticalSection(&device->lock);
 1520             return mr;
 1521         }
 1522 
 1523         header->reserved = (DWORD_PTR)ash;
 1524     }
 1525 
 1526     LeaveCriticalSection(&device->lock);
 1527 
 1528     header->dwFlags |= WHDR_PREPARED;
 1529     header->dwFlags &= ~(WHDR_DONE|WHDR_INQUEUE); /* flags cleared since w2k */
 1530 
 1531     return MMSYSERR_NOERROR;
 1532 }
 1533 
 1534 static LRESULT WINMM_UnprepareHeader(HWAVE hwave, WAVEHDR *header)
 1535 {
 1536     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
 1537 
 1538     TRACE("(%p, %p)\n", hwave, header);
 1539 
 1540     if(!WINMM_ValidateAndLock(device))
 1541         return MMSYSERR_INVALHANDLE;
 1542 
 1543     if(device->render && device->acm_handle){
 1544         ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
 1545 
 1546         acmStreamUnprepareHeader(device->acm_handle, ash, 0);
 1547 
 1548         HeapFree(GetProcessHeap(), 0, ash);
 1549     }
 1550 
 1551     LeaveCriticalSection(&device->lock);
 1552 
 1553     header->dwFlags &= ~WHDR_PREPARED;
 1554 
 1555     return MMSYSERR_NOERROR;
 1556 }
 1557 
 1558 static UINT32 WINMM_HeaderLenBytes(WINMM_Device *device, WAVEHDR *header)
 1559 {
 1560     if(device->acm_handle){
 1561         ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
 1562         return WINMM_FixedBufferLen(ash->cbDstLengthUsed, device);
 1563     }
 1564 
 1565     return WINMM_FixedBufferLen(header->dwBufferLength, device);
 1566 }
 1567 
 1568 static UINT32 WINMM_HeaderLenFrames(WINMM_Device *device, WAVEHDR *header)
 1569 {
 1570     return WINMM_HeaderLenBytes(device, header) / device->bytes_per_frame;
 1571 }
 1572 
 1573 static WAVEHDR *WOD_MarkDoneHeaders(WINMM_Device *device)
 1574 {
 1575     HRESULT hr;
 1576     WAVEHDR *first = device->first, *queue = first, *last = NULL;
 1577     UINT64 clock_freq, clock_pos, clock_frames;
 1578     UINT32 nloops, queue_frames = 0;
 1579 
 1580     hr = IAudioClock_GetFrequency(device->clock, &clock_freq);
 1581     if(FAILED(hr)){
 1582         WARN("GetFrequency failed: %08x\n", hr);
 1583         return NULL;
 1584     }
 1585 
 1586     hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL);
 1587     if(FAILED(hr)){
 1588         WARN("GetPosition failed: %08x\n", hr);
 1589         return NULL;
 1590     }
 1591 
 1592     clock_frames = (clock_pos * device->samples_per_sec) / clock_freq;
 1593 
 1594     nloops = device->loop_counter;
 1595     while(queue &&
 1596             (queue_frames += WINMM_HeaderLenFrames(device, queue)) <=
 1597                 clock_frames - device->last_clock_pos + device->remainder_frames){
 1598         if(!nloops){
 1599             last = queue;
 1600             device->last_clock_pos += queue_frames;
 1601             device->remainder_frames = 0;
 1602             queue_frames = 0;
 1603             queue = device->first = queue->lpNext;
 1604         }else{
 1605             if(queue->dwFlags & WHDR_BEGINLOOP){
 1606                 if(queue->dwFlags & WHDR_ENDLOOP)
 1607                     --nloops;
 1608                 else
 1609                     queue = queue->lpNext;
 1610             }else if(queue->dwFlags & WHDR_ENDLOOP){
 1611                 queue = device->loop_start;
 1612                 --nloops;
 1613             }
 1614         }
 1615     }
 1616 
 1617     if(last){
 1618         last->lpNext = NULL;
 1619         return first;
 1620     }else
 1621         return NULL;
 1622 }
 1623 
 1624 static void WOD_PushData(WINMM_Device *device)
 1625 {
 1626     WINMM_CBInfo cb_info;
 1627     HRESULT hr;
 1628     UINT32 pad, bufsize, avail_frames, queue_frames, written, ofs;
 1629     UINT32 queue_bytes, nloops;
 1630     BYTE *data;
 1631     WAVEHDR *queue, *first = NULL;
 1632 
 1633     TRACE("(%p)\n", device->handle);
 1634 
 1635     EnterCriticalSection(&device->lock);
 1636 
 1637     if(!device->device)
 1638         goto exit;
 1639 
 1640     if(!device->first){
 1641         if (device->stopped)
 1642             goto exit;
 1643         device->stopped = TRUE;
 1644         device->last_clock_pos = 0;
 1645         IAudioClient_Stop(device->client);
 1646         IAudioClient_Reset(device->client);
 1647         goto exit;
 1648     }
 1649 
 1650     hr = IAudioClient_GetBufferSize(device->client, &bufsize);
 1651     if(FAILED(hr)){
 1652         WARN("GetBufferSize failed: %08x\n", hr);
 1653         goto exit;
 1654     }
 1655 
 1656     hr = IAudioClient_GetCurrentPadding(device->client, &pad);
 1657     if(FAILED(hr)){
 1658         WARN("GetCurrentPadding failed: %08x\n", hr);
 1659         goto exit;
 1660     }
 1661 
 1662     first = WOD_MarkDoneHeaders(device);
 1663 
 1664     /* determine which is larger between the available buffer size and
 1665      * the amount of data left in the queue */
 1666     avail_frames = bufsize - pad;
 1667 
 1668     queue = device->playing;
 1669     ofs = device->ofs_bytes;
 1670     queue_frames = 0;
 1671     nloops = 0;
 1672     while(queue && queue_frames < avail_frames){
 1673         queue_bytes = WINMM_HeaderLenBytes(device, queue);
 1674         queue_frames += (queue_bytes - ofs) / device->bytes_per_frame;
 1675         ofs = 0;
 1676 
 1677         if(queue->dwFlags & WHDR_ENDLOOP && nloops < device->loop_counter){
 1678             queue = device->loop_start;
 1679             ++nloops;
 1680         }else
 1681             queue = queue->lpNext;
 1682     }
 1683 
 1684     if(queue_frames < avail_frames)
 1685         avail_frames = queue_frames;
 1686     if(avail_frames == 0)
 1687         goto exit;
 1688 
 1689     hr = IAudioRenderClient_GetBuffer(device->render, avail_frames, &data);
 1690     if(FAILED(hr)){
 1691         WARN("GetBuffer failed: %08x\n", hr);
 1692         goto exit;
 1693     }
 1694 
 1695     written = 0;
 1696     while(device->playing && written < avail_frames){
 1697         UINT32 copy_frames, copy_bytes;
 1698         BYTE *queue_data;
 1699 
 1700         queue = device->playing;
 1701 
 1702         queue_bytes = WINMM_HeaderLenBytes(device, queue);
 1703         if(device->acm_handle)
 1704             queue_data = ((ACMSTREAMHEADER*)queue->reserved)->pbDst;
 1705         else
 1706             queue_data = (BYTE*)queue->lpData;
 1707 
 1708         queue_frames = (queue_bytes - device->ofs_bytes) /
 1709             device->bytes_per_frame;
 1710 
 1711         copy_frames = queue_frames < (avail_frames - written) ?
 1712             queue_frames : avail_frames - written;
 1713         copy_bytes = copy_frames * device->bytes_per_frame;
 1714 
 1715         memcpy(data, queue_data + device->ofs_bytes, copy_bytes);
 1716 
 1717         data += copy_bytes;
 1718         written += copy_frames;
 1719         device->ofs_bytes += copy_bytes;
 1720 
 1721         if(device->ofs_bytes >= queue_bytes){
 1722             device->ofs_bytes = 0;
 1723 
 1724             if(!(queue->dwFlags & (WHDR_BEGINLOOP | WHDR_ENDLOOP)))
 1725                 device->playing = queue->lpNext;
 1726             else{
 1727                 if(queue->dwFlags & WHDR_BEGINLOOP){
 1728                     device->loop_start = device->playing;
 1729                     device->playing = queue->lpNext;
 1730                     device->loop_counter = queue->dwLoops;
 1731                 }
 1732                 if(queue->dwFlags & WHDR_ENDLOOP){
 1733                     --device->loop_counter;
 1734                     if(device->loop_counter)
 1735                         device->playing = device->loop_start;
 1736                     else
 1737                         device->loop_start = device->playing = queue->lpNext;
 1738                 }
 1739             }
 1740         }
 1741     }
 1742 
 1743     hr = IAudioRenderClient_ReleaseBuffer(device->render, avail_frames, 0);
 1744     if(FAILED(hr)){
 1745         WARN("ReleaseBuffer failed: %08x\n", hr);
 1746         goto exit;
 1747     }
 1748 
 1749     if(device->orig_fmt->nSamplesPerSec != device->samples_per_sec)
 1750         device->played_frames += MulDiv(avail_frames, device->orig_fmt->nSamplesPerSec, device->samples_per_sec);
 1751     else
 1752         device->played_frames += avail_frames;
 1753 
 1754 exit:
 1755     cb_info = device->cb_info;
 1756 
 1757     LeaveCriticalSection(&device->lock);
 1758 
 1759     while(first){
 1760         WAVEHDR *next = first->lpNext;
 1761         first->dwFlags &= ~WHDR_INQUEUE;
 1762         first->dwFlags |= WHDR_DONE;
 1763         WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
 1764         first = next;
 1765     }
 1766 }
 1767 
 1768 static void WID_PullACMData(WINMM_Device *device)
 1769 {
 1770     UINT32 packet, packet_bytes;
 1771     DWORD flags;
 1772     BYTE *data;
 1773     WAVEHDR *queue;
 1774     HRESULT hr;
 1775     MMRESULT mr;
 1776 
 1777     if(device->acm_hdr.cbDstLength == 0){
 1778         hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet,
 1779                 &flags, NULL, NULL);
 1780         if(hr != S_OK){
 1781             if(FAILED(hr))
 1782                 WARN("GetBuffer failed: %08x\n", hr);
 1783             return;
 1784         }
 1785 
 1786         acmStreamSize(device->acm_handle, packet * device->bytes_per_frame,
 1787                 &packet_bytes, ACM_STREAMSIZEF_SOURCE);
 1788 
 1789         device->acm_offs = 0;
 1790 
 1791         device->acm_hdr.cbStruct = sizeof(device->acm_hdr);
 1792         device->acm_hdr.fdwStatus = 0;
 1793         device->acm_hdr.dwUser = 0;
 1794         device->acm_hdr.pbSrc = data;
 1795         device->acm_hdr.cbSrcLength = packet * device->bytes_per_frame;
 1796         device->acm_hdr.cbSrcLengthUsed = 0;
 1797         device->acm_hdr.dwSrcUser = 0;
 1798         device->acm_hdr.pbDst = HeapAlloc(GetProcessHeap(), 0, packet_bytes);
 1799         device->acm_hdr.cbDstLength = packet_bytes;
 1800         device->acm_hdr.cbDstLengthUsed = 0;
 1801         device->acm_hdr.dwDstUser = 0;
 1802 
 1803         mr = acmStreamPrepareHeader(device->acm_handle, &device->acm_hdr, 0);
 1804         if(mr != MMSYSERR_NOERROR){
 1805             WARN("acmStreamPrepareHeader failed: %d\n", mr);
 1806             return;
 1807         }
 1808 
 1809         mr = acmStreamConvert(device->acm_handle, &device->acm_hdr, 0);
 1810         if(mr != MMSYSERR_NOERROR){
 1811             WARN("acmStreamConvert failed: %d\n", mr);
 1812             return;
 1813         }
 1814 
 1815         hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet);
 1816         if(FAILED(hr))
 1817             WARN("ReleaseBuffer failed: %08x\n", hr);
 1818 
 1819         device->played_frames += packet;
 1820     }
 1821 
 1822     queue = device->first;
 1823     while(queue){
 1824         UINT32 to_copy_bytes;
 1825 
 1826         to_copy_bytes = min(WINMM_FixedBufferLen(queue->dwBufferLength, device) - queue->dwBytesRecorded,
 1827                 WINMM_FixedBufferLen(device->acm_hdr.cbDstLengthUsed, device) - device->acm_offs);
 1828 
 1829         memcpy(queue->lpData + queue->dwBytesRecorded,
 1830                 device->acm_hdr.pbDst + device->acm_offs, to_copy_bytes);
 1831 
 1832         queue->dwBytesRecorded += to_copy_bytes;
 1833         device->acm_offs += to_copy_bytes;
 1834 
 1835         if(queue->dwBufferLength - queue->dwBytesRecorded <
 1836                 device->bytes_per_frame){
 1837             queue->dwFlags &= ~WHDR_INQUEUE;
 1838             queue->dwFlags |= WHDR_DONE;
 1839             device->first = queue = queue->lpNext;
 1840         }
 1841 
 1842         if(device->acm_offs >= WINMM_FixedBufferLen(device->acm_hdr.cbDstLengthUsed, device)){
 1843             acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
 1844             HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
 1845             device->acm_hdr.cbDstLength = 0;
 1846             device->acm_hdr.cbDstLengthUsed = 0;
 1847 
 1848             /* done with this ACM Header, so try to pull more data */
 1849             WID_PullACMData(device);
 1850             return;
 1851         }
 1852     }
 1853 
 1854     /* out of WAVEHDRs to write into, so toss the rest of this packet */
 1855     acmStreamUnprepareHeader(device->acm_handle, &device->acm_hdr, 0);
 1856     HeapFree(GetProcessHeap(), 0, device->acm_hdr.pbDst);
 1857     device->acm_hdr.cbDstLength = 0;
 1858     device->acm_hdr.cbDstLengthUsed = 0;
 1859 }
 1860 
 1861 static void WID_PullData(WINMM_Device *device)
 1862 {
 1863     WINMM_CBInfo cb_info;
 1864     WAVEHDR *queue, *first = NULL, *last = NULL;
 1865     HRESULT hr;
 1866 
 1867     TRACE("(%p)\n", device->handle);
 1868 
 1869     EnterCriticalSection(&device->lock);
 1870 
 1871     if(!device->device || !device->first)
 1872         goto exit;
 1873 
 1874     first = device->first;
 1875 
 1876     if(device->acm_handle){
 1877         WID_PullACMData(device);
 1878         goto exit;
 1879     }
 1880 
 1881     while(device->first){
 1882         BYTE *data;
 1883         UINT32 packet_len, packet;
 1884         DWORD flags;
 1885 
 1886         hr = IAudioCaptureClient_GetBuffer(device->capture, &data, &packet_len,
 1887                 &flags, NULL, NULL);
 1888         if(hr != S_OK){
 1889             if(FAILED(hr))
 1890                 WARN("GetBuffer failed: %08x\n", hr);
 1891             else /* AUDCLNT_S_BUFFER_EMPTY success code */
 1892                 IAudioCaptureClient_ReleaseBuffer(device->capture, 0);
 1893             goto exit;
 1894         }
 1895 
 1896         packet = packet_len;
 1897         queue = device->first;
 1898         while(queue && packet > 0){
 1899             UINT32 to_copy_bytes;
 1900 
 1901             to_copy_bytes = min(packet * device->bytes_per_frame,
 1902                     WINMM_FixedBufferLen(queue->dwBufferLength, device) - queue->dwBytesRecorded);
 1903 
 1904             memcpy(queue->lpData + queue->dwBytesRecorded,
 1905                     data + (packet_len - packet) * device->bytes_per_frame,
 1906                     to_copy_bytes);
 1907 
 1908             queue->dwBytesRecorded += to_copy_bytes;
 1909 
 1910             if(queue->dwBufferLength - queue->dwBytesRecorded <
 1911                     device->bytes_per_frame){
 1912                 last = queue;
 1913                 device->first = queue = queue->lpNext;
 1914             }
 1915 
 1916             packet -= to_copy_bytes / device->bytes_per_frame;
 1917         }
 1918 
 1919         hr = IAudioCaptureClient_ReleaseBuffer(device->capture, packet_len);
 1920         if(FAILED(hr))
 1921             WARN("ReleaseBuffer failed: %08x\n", hr);
 1922 
 1923         if(packet > 0)
 1924             WARN("losing %u frames\n", packet);
 1925         device->played_frames += packet_len - packet;
 1926     }
 1927 
 1928 exit:
 1929     cb_info = device->cb_info;
 1930 
 1931     LeaveCriticalSection(&device->lock);
 1932 
 1933     if(last){
 1934         last->lpNext = NULL;
 1935         while(first){
 1936             WAVEHDR *next = first->lpNext;
 1937             first->dwFlags &= ~WHDR_INQUEUE;
 1938             first->dwFlags |= WHDR_DONE;
 1939             WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
 1940             first = next;
 1941         }
 1942     }
 1943 }
 1944 
 1945 static MMRESULT WINMM_BeginPlaying(WINMM_Device *device)
 1946 {
 1947     HRESULT hr;
 1948 
 1949     TRACE("(%p)\n", device->handle);
 1950 
 1951     if(device->render)
 1952         /* prebuffer data before starting */
 1953         WOD_PushData(device);
 1954 
 1955     if(device->stopped){
 1956         device->stopped = FALSE;
 1957 
 1958         hr = IAudioClient_Start(device->client);
 1959         if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){
 1960             device->stopped = TRUE;
 1961             WARN("Start failed: %08x\n", hr);
 1962             return MMSYSERR_ERROR;
 1963         }
 1964     }
 1965 
 1966     return MMSYSERR_NOERROR;
 1967 }
 1968 
 1969 static LRESULT WINMM_Pause(WINMM_Device *device)
 1970 {
 1971     HRESULT hr;
 1972 
 1973     TRACE("(%p)\n", device->handle);
 1974 
 1975     hr = IAudioClient_Stop(device->client);
 1976     if(FAILED(hr)){
 1977         WARN("Stop failed: %08x\n", hr);
 1978         return MMSYSERR_ERROR;
 1979     }
 1980 
 1981     device->stopped = FALSE;
 1982 
 1983     return MMSYSERR_NOERROR;
 1984 }
 1985 
 1986 static LRESULT WINMM_Reset(HWAVE hwave)
 1987 {
 1988     WINMM_CBInfo cb_info;
 1989     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
 1990     BOOL is_out;
 1991     WAVEHDR *first;
 1992     HRESULT hr;
 1993 
 1994     TRACE("(%p)\n", hwave);
 1995 
 1996     if(!WINMM_ValidateAndLock(device))
 1997         return MMSYSERR_INVALHANDLE;
 1998 
 1999     hr = IAudioClient_Stop(device->client);
 2000     if(FAILED(hr)){
 2001         LeaveCriticalSection(&device->lock);
 2002         WARN("Stop failed: %08x\n", hr);
 2003         return MMSYSERR_ERROR;
 2004     }
 2005     device->stopped = TRUE;
 2006 
 2007     first = device->first;
 2008     device->first = device->last = device->playing = NULL;
 2009     device->ofs_bytes = 0;
 2010     device->played_frames = 0;
 2011     device->loop_counter = 0;
 2012     device->last_clock_pos = 0;
 2013     IAudioClient_Reset(device->client);
 2014 
 2015     cb_info = device->cb_info;
 2016     is_out = device->render != NULL;
 2017 
 2018     LeaveCriticalSection(&device->lock);
 2019 
 2020     while(first){
 2021         WAVEHDR *next = first->lpNext;
 2022         first->dwFlags &= ~WHDR_INQUEUE;
 2023         first->dwFlags |= WHDR_DONE;
 2024         if(is_out)
 2025             WINMM_NotifyClient(&cb_info, WOM_DONE, (DWORD_PTR)first, 0);
 2026         else
 2027             WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)first, 0);
 2028         first = next;
 2029     }
 2030 
 2031     return MMSYSERR_NOERROR;
 2032 }
 2033 
 2034 static MMRESULT WINMM_FramesToMMTime(MMTIME *time, UINT32 played_frames,
 2035         UINT32 sample_rate, UINT32 bytes_per_sec)
 2036 {
 2037     switch(time->wType){
 2038     case TIME_SAMPLES:
 2039         time->u.sample = played_frames;
 2040         return MMSYSERR_NOERROR;
 2041     case TIME_SMPTE:
 2042         time->u.smpte.fps = 30;
 2043         played_frames += sample_rate / time->u.smpte.fps - 1; /* round up */
 2044         time->u.smpte.frame = (played_frames % sample_rate) * time->u.smpte.fps / sample_rate;
 2045         played_frames /= sample_rate; /* yields seconds */
 2046         time->u.smpte.sec = played_frames % 60;
 2047         played_frames /= 60;
 2048         time->u.smpte.min = played_frames % 60;
 2049         time->u.smpte.hour= played_frames / 60;
 2050         return MMSYSERR_NOERROR;
 2051     default:
 2052         time->wType = TIME_BYTES;
 2053         /* fall through */
 2054     case TIME_BYTES:
 2055         time->u.cb = MulDiv(played_frames, bytes_per_sec, sample_rate);
 2056         return MMSYSERR_NOERROR;
 2057     }
 2058 }
 2059 
 2060 static LRESULT WINMM_GetPosition(HWAVE hwave, MMTIME *time)
 2061 {
 2062     WINMM_Device *device = WINMM_GetDeviceFromHWAVE(hwave);
 2063     UINT32 played_frames, sample_rate, bytes_per_sec;
 2064 
 2065     TRACE("(%p, %p)\n", hwave, time);
 2066 
 2067     if(!WINMM_ValidateAndLock(device))
 2068         return MMSYSERR_INVALHANDLE;
 2069 
 2070     played_frames = device->played_frames;
 2071     sample_rate = device->orig_fmt->nSamplesPerSec;
 2072     bytes_per_sec = device->orig_fmt->nAvgBytesPerSec;
 2073 
 2074     LeaveCriticalSection(&device->lock);
 2075 
 2076     return WINMM_FramesToMMTime(time, played_frames, sample_rate, bytes_per_sec);
 2077 }
 2078 
 2079 static WINMM_MMDevice *WINMM_GetMixerMMDevice(HMIXEROBJ hmix, DWORD flags,
 2080         UINT *mmdev_index)
 2081 {
 2082     UINT mmdev, dev, junk, *out;
 2083     BOOL is_out;
 2084 
 2085     if(!mmdev_index)
 2086         out = &mmdev;
 2087     else
 2088         out = mmdev_index;
 2089 
 2090     switch(flags & 0xF0000000){
 2091     case MIXER_OBJECTF_MIXER: /* == 0 */
 2092         *out = HandleToULong(hmix);
 2093         if(*out < g_outmmdevices_count)
 2094             return read_map(g_out_map, *out);
 2095         if(*out - g_outmmdevices_count < g_inmmdevices_count){
 2096             *out -= g_outmmdevices_count;
 2097             return read_map(g_in_map, *out);
 2098         }
 2099         /* fall through -- if it's not a valid mixer device, then
 2100          * it could be a valid mixer handle. windows seems to do
 2101          * this as well. */
 2102     case MIXER_OBJECTF_HMIXER:
 2103     case MIXER_OBJECTF_HWAVEOUT:
 2104     case MIXER_OBJECTF_HWAVEIN:
 2105         WINMM_DecomposeHWAVE((HWAVE)hmix, out, &is_out, &dev, &junk);
 2106         if(junk != 0x1 || (is_out && *out >= g_outmmdevices_count) ||
 2107                (!is_out && *out >= g_inmmdevices_count))
 2108             return NULL;
 2109         if(is_out)
 2110             return read_map(g_out_map, *out);
 2111         return read_map(g_in_map, *out);
 2112     case MIXER_OBJECTF_WAVEOUT:
 2113         *out = HandleToULong(hmix);
 2114         if(*out < g_outmmdevices_count)
 2115             return read_map(g_out_map, *out);
 2116         return NULL;
 2117     case MIXER_OBJECTF_WAVEIN:
 2118         *out = HandleToULong(hmix);
 2119         if(*out < g_inmmdevices_count)
 2120             return read_map(g_in_map, *out);
 2121         return NULL;
 2122     }
 2123 
 2124     return NULL;
 2125 }
 2126 
 2127 static MMRESULT WINMM_SetupMMDeviceVolume(WINMM_MMDevice *mmdevice)
 2128 {
 2129     IAudioSessionManager *sesman;
 2130     IMMDevice *device;
 2131     HRESULT hr;
 2132 
 2133     hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id, &device);
 2134     if(FAILED(hr)){
 2135         WARN("Device %s (%s) unavailable: %08x\n",
 2136                 wine_dbgstr_w(mmdevice->dev_id),
 2137                 wine_dbgstr_w(mmdevice->out_caps.szPname), hr);
 2138         return MMSYSERR_ERROR;
 2139     }
 2140 
 2141     hr = IMMDevice_Activate(device, &IID_IAudioSessionManager,
 2142             CLSCTX_INPROC_SERVER, NULL, (void**)&sesman);
 2143     if(FAILED(hr)){
 2144         WARN("Activate failed: %08x\n", hr);
 2145         IMMDevice_Release(device);
 2146         return MMSYSERR_ERROR;
 2147     }
 2148 
 2149     IMMDevice_Release(device);
 2150 
 2151     hr = IAudioSessionManager_GetSimpleAudioVolume(sesman, &mmdevice->session,
 2152             FALSE, &mmdevice->volume);
 2153     IAudioSessionManager_Release(sesman);
 2154     if(FAILED(hr)){
 2155         WARN("GetSimpleAudioVolume failed: %08x\n", hr);
 2156         return MMSYSERR_ERROR;
 2157     }
 2158 
 2159     return MMSYSERR_NOERROR;
 2160 }
 2161 
 2162 static LRESULT MXD_GetControlDetails(WINMM_ControlDetails *details)
 2163 {
 2164     WINMM_MMDevice *mmdevice;
 2165     MIXERCONTROLDETAILS *control = details->details;
 2166     HRESULT hr;
 2167 
 2168     TRACE("(%p)\n", details->hmix);
 2169 
 2170     mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
 2171     if(!mmdevice)
 2172         return MMSYSERR_INVALHANDLE;
 2173 
 2174     EnterCriticalSection(&mmdevice->lock);
 2175 
 2176     if(!mmdevice->volume){
 2177         MMRESULT mr;
 2178 
 2179         mr = WINMM_SetupMMDeviceVolume(mmdevice);
 2180         if(mr != MMSYSERR_NOERROR){
 2181             LeaveCriticalSection(&mmdevice->lock);
 2182             return mr;
 2183         }
 2184     }
 2185 
 2186     if(control->dwControlID == 0){
 2187         float vol;
 2188         MIXERCONTROLDETAILS_UNSIGNED *udet;
 2189 
 2190         if(!control->paDetails ||
 2191                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
 2192             LeaveCriticalSection(&mmdevice->lock);
 2193             return MMSYSERR_INVALPARAM;
 2194         }
 2195 
 2196         hr = ISimpleAudioVolume_GetMasterVolume(mmdevice->volume, &vol);
 2197         if(FAILED(hr)){
 2198             WARN("GetMasterVolume failed: %08x\n", hr);
 2199             LeaveCriticalSection(&mmdevice->lock);
 2200             return MMSYSERR_ERROR;
 2201         }
 2202 
 2203         udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
 2204         udet->dwValue = vol * ((unsigned int)0xFFFF);
 2205     }else if(control->dwControlID == 1){
 2206         BOOL mute;
 2207         MIXERCONTROLDETAILS_BOOLEAN *bdet;
 2208 
 2209         if(!control->paDetails ||
 2210                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
 2211             LeaveCriticalSection(&mmdevice->lock);
 2212             return MMSYSERR_INVALPARAM;
 2213         }
 2214 
 2215         hr = ISimpleAudioVolume_GetMute(mmdevice->volume, &mute);
 2216         if(FAILED(hr)){
 2217             WARN("GetMute failed: %08x\n", hr);
 2218             LeaveCriticalSection(&mmdevice->lock);
 2219             return MMSYSERR_ERROR;
 2220         }
 2221 
 2222         bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
 2223         bdet->fValue = mute;
 2224     }else if(control->dwControlID == 2 || control->dwControlID == 3){
 2225         FIXME("What should the sw-side mixer controls map to?\n");
 2226     }else{
 2227         LeaveCriticalSection(&mmdevice->lock);
 2228         return MIXERR_INVALCONTROL;
 2229     }
 2230 
 2231     LeaveCriticalSection(&mmdevice->lock);
 2232 
 2233     return MMSYSERR_NOERROR;
 2234 }
 2235 
 2236 static LRESULT MXD_SetControlDetails(WINMM_ControlDetails *details)
 2237 {
 2238     WINMM_MMDevice *mmdevice;
 2239     MIXERCONTROLDETAILS *control = details->details;
 2240     HRESULT hr;
 2241 
 2242     TRACE("(%p)\n", details->hmix);
 2243 
 2244     mmdevice = WINMM_GetMixerMMDevice(details->hmix, details->flags, NULL);
 2245     if(!mmdevice)
 2246         return MMSYSERR_INVALHANDLE;
 2247 
 2248     EnterCriticalSection(&mmdevice->lock);
 2249 
 2250     if(!mmdevice->volume){
 2251         MMRESULT mr;
 2252 
 2253         mr = WINMM_SetupMMDeviceVolume(mmdevice);
 2254         if(mr != MMSYSERR_NOERROR){
 2255             LeaveCriticalSection(&mmdevice->lock);
 2256             return mr;
 2257         }
 2258     }
 2259 
 2260     if(control->dwControlID == 0){
 2261         float vol;
 2262         MIXERCONTROLDETAILS_UNSIGNED *udet;
 2263 
 2264         if(!control->paDetails ||
 2265                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED)){
 2266             LeaveCriticalSection(&mmdevice->lock);
 2267             return MMSYSERR_INVALPARAM;
 2268         }
 2269 
 2270         udet = (MIXERCONTROLDETAILS_UNSIGNED*)control->paDetails;
 2271 
 2272         if(udet->dwValue > 65535){
 2273             LeaveCriticalSection(&mmdevice->lock);
 2274             return MMSYSERR_INVALPARAM;
 2275         }
 2276 
 2277         vol = udet->dwValue / 65535.f;
 2278 
 2279         hr = ISimpleAudioVolume_SetMasterVolume(mmdevice->volume, vol, NULL);
 2280         if(FAILED(hr)){
 2281             WARN("SetMasterVolume failed: %08x\n", hr);
 2282             LeaveCriticalSection(&mmdevice->lock);
 2283             return MMSYSERR_ERROR;
 2284         }
 2285     }else if(control->dwControlID == 1){
 2286         BOOL mute;
 2287         MIXERCONTROLDETAILS_BOOLEAN *bdet;
 2288 
 2289         if(!control->paDetails ||
 2290                 control->cbDetails < sizeof(MIXERCONTROLDETAILS_BOOLEAN)){
 2291             LeaveCriticalSection(&mmdevice->lock);
 2292             return MMSYSERR_INVALPARAM;
 2293         }
 2294 
 2295         bdet = (MIXERCONTROLDETAILS_BOOLEAN*)control->paDetails;
 2296         mute = bdet->fValue;
 2297 
 2298         hr = ISimpleAudioVolume_SetMute(mmdevice->volume, mute, NULL);
 2299         if(FAILED(hr)){
 2300             WARN("SetMute failed: %08x\n", hr);
 2301             LeaveCriticalSection(&mmdevice->lock);
 2302             return MMSYSERR_ERROR;
 2303         }
 2304     }else if(control->dwControlID == 2 || control->dwControlID == 3){
 2305         FIXME("What should the sw-side mixer controls map to?\n");
 2306     }else{
 2307         LeaveCriticalSection(&mmdevice->lock);
 2308         return MIXERR_INVALCONTROL;
 2309     }
 2310 
 2311     LeaveCriticalSection(&mmdevice->lock);
 2312 
 2313     return MMSYSERR_NOERROR;
 2314 }
 2315 
 2316 static LRESULT DRV_QueryDeviceInterface(WINMM_QueryInterfaceInfo *info)
 2317 {
 2318     WINMM_MMDevice *mmdevice;
 2319     IMMDevice *device;
 2320     IPropertyStore *ps;
 2321     PROPVARIANT pv;
 2322     DWORD len_bytes;
 2323     HRESULT hr;
 2324 
 2325     static const PROPERTYKEY deviceinterface_key = {
 2326         {0x233164c8, 0x1b2c, 0x4c7d, {0xbc, 0x68, 0xb6, 0x71, 0x68, 0x7a, 0x25, 0x67}}, 1
 2327     };
 2328 
 2329     if(WINMM_IsMapper(info->index)){
 2330         if(info->str){
 2331             if(*info->len_bytes < sizeof(WCHAR))
 2332                 return MMSYSERR_INVALPARAM;
 2333             *info->str = 0;
 2334         }else
 2335             *info->len_bytes = sizeof(WCHAR);
 2336         return MMSYSERR_NOERROR;
 2337     }
 2338 
 2339     if(info->is_out){
 2340         if(info->index >= g_outmmdevices_count)
 2341             return MMSYSERR_INVALHANDLE;
 2342 
 2343         mmdevice = &g_out_mmdevices[info->index];
 2344     }else{
 2345         if(info->index >= g_inmmdevices_count)
 2346             return MMSYSERR_INVALHANDLE;
 2347 
 2348         mmdevice = &g_in_mmdevices[info->index];
 2349     }
 2350 
 2351     hr = IMMDeviceEnumerator_GetDevice(g_devenum, mmdevice->dev_id,
 2352             &device);
 2353     if(FAILED(hr)){
 2354         WARN("Device %s unavailable: %08x\n", wine_dbgstr_w(mmdevice->dev_id), hr);
 2355         return MMSYSERR_ERROR;
 2356     }
 2357 
 2358     hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps);
 2359     if(FAILED(hr)){
 2360         WARN("OpenPropertyStore failed: %08x\n", hr);
 2361         IMMDevice_Release(device);
 2362         return MMSYSERR_ERROR;
 2363     }
 2364 
 2365     PropVariantInit(&pv);
 2366     hr = IPropertyStore_GetValue(ps, &deviceinterface_key, &pv);
 2367     if(FAILED(hr)){
 2368         WARN("GetValue failed: %08x\n", hr);
 2369         IPropertyStore_Release(ps);
 2370         IMMDevice_Release(device);
 2371         return MMSYSERR_ERROR;
 2372     }
 2373     if(pv.vt != VT_LPWSTR){
 2374         WARN("Got unexpected property type: %u\n", pv.vt);
 2375         PropVariantClear(&pv);
 2376         IPropertyStore_Release(ps);
 2377         IMMDevice_Release(device);
 2378         return MMSYSERR_ERROR;
 2379     }
 2380 
 2381     len_bytes = (lstrlenW(pv.u.pwszVal) + 1) * sizeof(WCHAR);
 2382 
 2383     if(info->str){
 2384         if(len_bytes > *info->len_bytes){
 2385             PropVariantClear(&pv);
 2386             IPropertyStore_Release(ps);
 2387             IMMDevice_Release(device);
 2388             return MMSYSERR_INVALPARAM;
 2389         }
 2390 
 2391         memcpy(info->str, pv.u.pwszVal, len_bytes);
 2392     }else
 2393         *info->len_bytes = len_bytes;
 2394 
 2395     PropVariantClear(&pv);
 2396     IPropertyStore_Release(ps);
 2397     IMMDevice_Release(device);
 2398 
 2399     return MMSYSERR_NOERROR;
 2400 }
 2401 
 2402 static LRESULT CALLBACK WINMM_DevicesMsgProc(HWND hwnd, UINT msg, WPARAM wparam,
 2403         LPARAM lparam)
 2404 {
 2405     switch(msg){
 2406     case WODM_OPEN:
 2407         return WOD_Open((WINMM_OpenInfo*)wparam);
 2408     case WODM_CLOSE:
 2409         return WOD_Close((HWAVEOUT)wparam);
 2410     case WIDM_OPEN:
 2411         return WID_Open((WINMM_OpenInfo*)wparam);
 2412     case WIDM_CLOSE:
 2413         return WID_Close((HWAVEIN)wparam);
 2414     case MXDM_GETCONTROLDETAILS:
 2415         return MXD_GetControlDetails((WINMM_ControlDetails*)wparam);
 2416     case MXDM_SETCONTROLDETAILS:
 2417         return MXD_SetControlDetails((WINMM_ControlDetails*)wparam);
 2418     case DRV_QUERYDEVICEINTERFACESIZE:
 2419     case DRV_QUERYDEVICEINTERFACE:
 2420         return DRV_QueryDeviceInterface((WINMM_QueryInterfaceInfo*)wparam);
 2421     }
 2422     return DefWindowProcW(hwnd, msg, wparam, lparam);
 2423 }
 2424 
 2425 static BOOL WINMM_DevicesThreadDone(void)
 2426 {
 2427     UINT i;
 2428 
 2429     EnterCriticalSection(&g_devthread_lock);
 2430 
 2431     if(g_devthread_token > 0){
 2432         LeaveCriticalSection(&g_devthread_lock);
 2433         return FALSE;
 2434     }
 2435 
 2436     for(i = 0; i < g_devhandle_count; ++i){
 2437         if(g_handle_devices[i]->open){
 2438             LeaveCriticalSection(&g_devthread_lock);
 2439             return FALSE;
 2440         }
 2441     }
 2442 
 2443     DestroyWindow(g_devices_hwnd);
 2444     g_devices_hwnd = NULL;
 2445     IMMDeviceEnumerator_Release(g_devenum);
 2446     g_devenum = NULL;
 2447     CoUninitialize();
 2448 
 2449     LeaveCriticalSection(&g_devthread_lock);
 2450 
 2451     return TRUE;
 2452 }
 2453 
 2454 static DWORD WINAPI WINMM_DevicesThreadProc(void *arg)
 2455 {
 2456     HANDLE evt = arg;
 2457     HRESULT hr;
 2458 
 2459     hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
 2460     if(FAILED(hr)){
 2461         WARN("CoInitializeEx failed: %08x\n", hr);
 2462         FreeLibraryAndExitThread(g_devthread_module, 1);
 2463     }
 2464 
 2465     hr = WINMM_InitMMDevices();
 2466     if(FAILED(hr)){
 2467         CoUninitialize();
 2468         FreeLibraryAndExitThread(g_devthread_module, 1);
 2469     }
 2470 
 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);
 2477     }
 2478 
 2479     g_devices_hwnd = CreateWindowW(L"Message", 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);
 2485     }
 2486 
 2487     SetWindowLongPtrW(g_devices_hwnd, GWLP_WNDPROC,
 2488             (LONG_PTR)WINMM_DevicesMsgProc);
 2489 
 2490     /* inform caller that the thread is ready to process messages */
 2491     SetEvent(evt);
 2492     evt = NULL; /* do not use after this point */
 2493 
 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);
 2516         }
 2517     }
 2518 
 2519     FreeLibraryAndExitThread(g_devthread_module, 0);
 2520 }
 2521 
 2522 /* on success, increments g_devthread_token to prevent
 2523  * device thread shutdown. caller must decrement. */
 2524 static BOOL WINMM_StartDevicesThread(void)
 2525 {
 2526     HANDLE events[2];
 2527     DWORD wait;
 2528 
 2529     EnterCriticalSection(&g_devthread_lock);
 2530 
 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;
 2538         }
 2539         if(wait != WAIT_OBJECT_0){
 2540             /* error */
 2541             LeaveCriticalSection(&g_devthread_lock);
 2542             return FALSE;
 2543         }
 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;
 2552     }
 2553 
 2554     TRACE("Starting up devices thread\n");
 2555 
 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);
 2560 
 2561     events[0] = CreateEventW(NULL, FALSE, FALSE, NULL);
 2562 
 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;
 2570     }
 2571 
 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;
 2580         }
 2581         LeaveCriticalSection(&g_devthread_lock);
 2582         return FALSE;
 2583     }
 2584 
 2585     InterlockedIncrement(&g_devthread_token);
 2586 
 2587     LeaveCriticalSection(&g_devthread_lock);
 2588 
 2589     return TRUE;
 2590 }
 2591 
 2592 /**************************************************************************
 2593  *              waveOutGetNumDevs       [WINMM.@]
 2594  */
 2595 UINT WINAPI waveOutGetNumDevs(void)
 2596 {
 2597     HRESULT hr = WINMM_InitMMDevices();
 2598     if(FAILED(hr))
 2599         return 0;
 2600 
 2601     TRACE("count: %u\n", g_outmmdevices_count);
 2602 
 2603     return g_outmmdevices_count;
 2604 }
 2605 
 2606 /**************************************************************************
 2607  *              waveOutGetDevCapsA      [WINMM.@]
 2608  */
 2609 UINT WINAPI waveOutGetDevCapsA(UINT_PTR uDeviceID, LPWAVEOUTCAPSA lpCaps,
 2610                    UINT uSize)
 2611 {
 2612     WAVEOUTCAPSW    wocW;
 2613     UINT        ret;
 2614 
 2615     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
 2616 
 2617     if(!lpCaps)
 2618         return MMSYSERR_INVALPARAM;
 2619 
 2620     ret = waveOutGetDevCapsW(uDeviceID, &wocW, sizeof(wocW));
 2621 
 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)));
 2634     }
 2635     return ret;
 2636 }
 2637 
 2638 /**************************************************************************
 2639  *              waveOutGetDevCapsW      [WINMM.@]
 2640  */
 2641 UINT WINAPI waveOutGetDevCapsW(UINT_PTR uDeviceID, LPWAVEOUTCAPSW lpCaps,
 2642                    UINT uSize)
 2643 {
 2644     WAVEOUTCAPSW mapper_caps, *caps;
 2645     HRESULT hr;
 2646 
 2647     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
 2648 
 2649     hr = WINMM_InitMMDevices();
 2650     if(FAILED(hr))
 2651         return MMSYSERR_NODRIVER;
 2652 
 2653     if (lpCaps == NULL) return MMSYSERR_INVALPARAM;
 2654 
 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);
 2665 
 2666         caps = &mapper_caps;
 2667     }else{
 2668         if(uDeviceID >= g_outmmdevices_count){
 2669             WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)uDeviceID);
 2670 
 2671             if(!WINMM_ValidateAndLock(device))
 2672                 return MMSYSERR_BADDEVICEID;
 2673 
 2674             caps = &device->parent->out_caps;
 2675 
 2676             LeaveCriticalSection(&device->lock);
 2677         }else{
 2678             caps = &read_map(g_out_map, uDeviceID)->out_caps;
 2679         }
 2680     }
 2681 
 2682     memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
 2683 
 2684     return MMSYSERR_NOERROR;
 2685 }
 2686 
 2687 /**************************************************************************
 2688  *              waveOutGetErrorTextA    [WINMM.@]
 2689  *              waveInGetErrorTextA     [WINMM.@]
 2690  */
 2691 UINT WINAPI waveOutGetErrorTextA(UINT uError, LPSTR lpText, UINT uSize)
 2692 {
 2693     UINT    ret;
 2694 
 2695     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
 2696     else if (uSize == 0) ret = MMSYSERR_NOERROR;
 2697     else
 2698     {
 2699         LPWSTR  xstr = HeapAlloc(GetProcessHeap(), 0, uSize * sizeof(WCHAR));
 2700         if (!xstr) ret = MMSYSERR_NOMEM;
 2701         else
 2702         {
 2703             ret = waveOutGetErrorTextW(uError, xstr, uSize);
 2704             if (ret == MMSYSERR_NOERROR)
 2705                 WideCharToMultiByte(CP_ACP, 0, xstr, -1, lpText, uSize, NULL, NULL);
 2706             HeapFree(GetProcessHeap(), 0, xstr);
 2707         }
 2708     }
 2709     return ret;
 2710 }
 2711 
 2712 /**************************************************************************
 2713  *              waveOutGetErrorTextW    [WINMM.@]
 2714  *              waveInGetErrorTextW     [WINMM.@]
 2715  */
 2716 UINT WINAPI waveOutGetErrorTextW(UINT uError, LPWSTR lpText, UINT uSize)
 2717 {
 2718     UINT        ret = MMSYSERR_BADERRNUM;
 2719 
 2720     if (lpText == NULL) ret = MMSYSERR_INVALPARAM;
 2721     else if (uSize == 0) ret = MMSYSERR_NOERROR;
 2722     else if (
 2723            /* test has been removed because MMSYSERR_BASE is 0, and gcc did emit
 2724         * a warning for the test was always true */
 2725            (/*uError >= MMSYSERR_BASE && */ uError <= MMSYSERR_LASTERROR) ||
 2726            (uError >= WAVERR_BASE  && uError <= WAVERR_LASTERROR)) {
 2727     if (LoadStringW(hWinMM32Instance,
 2728             uError, lpText, uSize) > 0) {
 2729         ret = MMSYSERR_NOERROR;
 2730     }
 2731     }
 2732     return ret;
 2733 }
 2734 
 2735 /**************************************************************************
 2736  *          waveOutOpen         [WINMM.@]
 2737  * All the args/structs have the same layout as the win16 equivalents
 2738  */
 2739 MMRESULT WINAPI waveOutOpen(LPHWAVEOUT lphWaveOut, UINT uDeviceID,
 2740                        LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
 2741                        DWORD_PTR dwInstance, DWORD dwFlags)
 2742 {
 2743     LRESULT res;
 2744     WINMM_OpenInfo info;
 2745     WINMM_CBInfo cb_info;
 2746 
 2747     TRACE("(%p, %u, %p, %lx, %lx, %08x)\n", lphWaveOut, uDeviceID, lpFormat,
 2748             dwCallback, dwInstance, dwFlags);
 2749 
 2750     if(!lphWaveOut && !(dwFlags & WAVE_FORMAT_QUERY))
 2751         return MMSYSERR_INVALPARAM;
 2752 
 2753     res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
 2754     if(res != MMSYSERR_NOERROR)
 2755         return res;
 2756 
 2757     if(!WINMM_StartDevicesThread())
 2758         return MMSYSERR_NODRIVER;
 2759 
 2760     info.handle = 0;
 2761     info.format = (WAVEFORMATEX*)lpFormat;
 2762     info.callback = dwCallback;
 2763     info.cb_user = dwInstance;
 2764     info.req_device = uDeviceID;
 2765     info.flags = dwFlags;
 2766     info.reset = TRUE;
 2767 
 2768     res = SendMessageW(g_devices_hwnd, WODM_OPEN, (DWORD_PTR)&info, 0);
 2769     InterlockedDecrement(&g_devthread_token);
 2770     if(res != MMSYSERR_NOERROR || (dwFlags & WAVE_FORMAT_QUERY))
 2771         return res;
 2772 
 2773     if(lphWaveOut)
 2774         *lphWaveOut = (HWAVEOUT)info.handle;
 2775 
 2776     cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
 2777     cb_info.callback = dwCallback;
 2778     cb_info.user = dwInstance;
 2779     cb_info.hwave = info.handle;
 2780 
 2781     WINMM_NotifyClient(&cb_info, WOM_OPEN, 0, 0);
 2782 
 2783     return res;
 2784 }
 2785 
 2786 /**************************************************************************
 2787  *              waveOutClose        [WINMM.@]
 2788  */
 2789 UINT WINAPI waveOutClose(HWAVEOUT hWaveOut)
 2790 {
 2791     UINT res;
 2792     WINMM_Device *device;
 2793     WINMM_CBInfo cb_info;
 2794 
 2795     TRACE("(%p)\n", hWaveOut);
 2796 
 2797     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
 2798 
 2799     if(!WINMM_ValidateAndLock(device))
 2800         return MMSYSERR_INVALHANDLE;
 2801 
 2802     cb_info = device->cb_info;
 2803 
 2804     LeaveCriticalSection(&device->lock);
 2805 
 2806     res = SendMessageW(g_devices_hwnd, WODM_CLOSE, (WPARAM)hWaveOut, 0);
 2807 
 2808     if(res == MMSYSERR_NOERROR)
 2809         WINMM_NotifyClient(&cb_info, WOM_CLOSE, 0, 0);
 2810 
 2811     return res;
 2812 }
 2813 
 2814 /**************************************************************************
 2815  *              waveOutPrepareHeader    [WINMM.@]
 2816  */
 2817 UINT WINAPI waveOutPrepareHeader(HWAVEOUT hWaveOut,
 2818                  WAVEHDR* lpWaveOutHdr, UINT uSize)
 2819 {
 2820     TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
 2821 
 2822     if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
 2823         return MMSYSERR_INVALPARAM;
 2824 
 2825     if(lpWaveOutHdr->dwFlags & WHDR_PREPARED)
 2826         return MMSYSERR_NOERROR;
 2827 
 2828     return WINMM_PrepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
 2829 }
 2830 
 2831 /**************************************************************************
 2832  *              waveOutUnprepareHeader  [WINMM.@]
 2833  */
 2834 UINT WINAPI waveOutUnprepareHeader(HWAVEOUT hWaveOut,
 2835                    LPWAVEHDR lpWaveOutHdr, UINT uSize)
 2836 {
 2837     TRACE("(%p, %p, %u)\n", hWaveOut, lpWaveOutHdr, uSize);
 2838 
 2839     if(!lpWaveOutHdr || uSize < sizeof(WAVEHDR))
 2840         return MMSYSERR_INVALPARAM;
 2841 
 2842     if(lpWaveOutHdr->dwFlags & WHDR_INQUEUE)
 2843         return WAVERR_STILLPLAYING;
 2844 
 2845     if(!(lpWaveOutHdr->dwFlags & WHDR_PREPARED))
 2846         return MMSYSERR_NOERROR;
 2847 
 2848     return WINMM_UnprepareHeader((HWAVE)hWaveOut, lpWaveOutHdr);
 2849 }
 2850 
 2851 /**************************************************************************
 2852  *              waveOutWrite        [WINMM.@]
 2853  */
 2854 UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, WAVEHDR *header, UINT uSize)
 2855 {
 2856     WINMM_Device *device;
 2857     MMRESULT mr;
 2858 
 2859     TRACE("(%p, %p, %u)\n", hWaveOut, header, uSize);
 2860 
 2861     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
 2862 
 2863     if(!WINMM_ValidateAndLock(device))
 2864         return MMSYSERR_INVALHANDLE;
 2865 
 2866     if(!header->lpData || !(header->dwFlags & WHDR_PREPARED)){
 2867         LeaveCriticalSection(&device->lock);
 2868         return WAVERR_UNPREPARED;
 2869     }
 2870 
 2871     if(header->dwFlags & WHDR_INQUEUE){
 2872         LeaveCriticalSection(&device->lock);
 2873         return WAVERR_STILLPLAYING;
 2874     }
 2875 
 2876     TRACE("dwBufferLength: %u\n", header->dwBufferLength);
 2877 
 2878     if(device->acm_handle){
 2879         ACMSTREAMHEADER *ash = (ACMSTREAMHEADER*)header->reserved;
 2880 
 2881         ash->cbSrcLength = header->dwBufferLength;
 2882         mr = acmStreamConvert(device->acm_handle, ash, 0);
 2883         if(mr != MMSYSERR_NOERROR){
 2884             LeaveCriticalSection(&device->lock);
 2885             return mr;
 2886         }
 2887     }
 2888 
 2889     if(device->first){
 2890         device->last->lpNext = header;
 2891         device->last = header;
 2892         if(!device->playing)
 2893             device->playing = header;
 2894     }else{
 2895         device->playing = device->first = device->last = header;
 2896         if(header->dwFlags & WHDR_BEGINLOOP){
 2897             device->loop_counter = header->dwLoops;
 2898             device->loop_start = header;
 2899         }
 2900     }
 2901 
 2902     header->lpNext = NULL;
 2903     header->dwFlags &= ~WHDR_DONE;
 2904     header->dwFlags |= WHDR_INQUEUE;
 2905 
 2906     mr = WINMM_BeginPlaying(device);
 2907 
 2908     LeaveCriticalSection(&device->lock);
 2909 
 2910     return mr;
 2911 }
 2912 
 2913 /**************************************************************************
 2914  *              waveOutBreakLoop    [WINMM.@]
 2915  */
 2916 UINT WINAPI waveOutBreakLoop(HWAVEOUT hWaveOut)
 2917 {
 2918     WINMM_Device *device;
 2919 
 2920     TRACE("(%p)\n", hWaveOut);
 2921 
 2922     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
 2923 
 2924     if(!WINMM_ValidateAndLock(device))
 2925         return MMSYSERR_INVALHANDLE;
 2926 
 2927     device->loop_counter = 0;
 2928 
 2929     LeaveCriticalSection(&device->lock);
 2930 
 2931     return MMSYSERR_NOERROR;
 2932 }
 2933 
 2934 /**************************************************************************
 2935  *              waveOutPause        [WINMM.@]
 2936  */
 2937 UINT WINAPI waveOutPause(HWAVEOUT hWaveOut)
 2938 {
 2939     WINMM_Device *device;
 2940     MMRESULT mr;
 2941 
 2942     TRACE("(%p)\n", hWaveOut);
 2943 
 2944     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
 2945 
 2946     if(!WINMM_ValidateAndLock(device))
 2947         return MMSYSERR_INVALHANDLE;
 2948 
 2949     mr = WINMM_Pause(device);
 2950 
 2951     LeaveCriticalSection(&device->lock);
 2952 
 2953     return mr;
 2954 }
 2955 
 2956 /**************************************************************************
 2957  *              waveOutReset        [WINMM.@]
 2958  */
 2959 UINT WINAPI waveOutReset(HWAVEOUT hWaveOut)
 2960 {
 2961     TRACE("(%p)\n", hWaveOut);
 2962 
 2963     return WINMM_Reset((HWAVE)hWaveOut);
 2964 }
 2965 
 2966 /**************************************************************************
 2967  *              waveOutRestart      [WINMM.@]
 2968  */
 2969 UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut)
 2970 {
 2971     WINMM_Device *device;
 2972     MMRESULT mr;
 2973 
 2974     TRACE("(%p)\n", hWaveOut);
 2975 
 2976     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
 2977 
 2978     if(!WINMM_ValidateAndLock(device))
 2979         return MMSYSERR_INVALHANDLE;
 2980 
 2981     device->stopped = TRUE;
 2982 
 2983     mr = WINMM_BeginPlaying(device);
 2984 
 2985     LeaveCriticalSection(&device->lock);
 2986 
 2987     return mr;
 2988 }
 2989 
 2990 /**************************************************************************
 2991  *              waveOutGetPosition  [WINMM.@]
 2992  */
 2993 UINT WINAPI waveOutGetPosition(HWAVEOUT hWaveOut, LPMMTIME lpTime,
 2994                    UINT uSize)
 2995 {
 2996     TRACE("(%p, %p, %u)\n", hWaveOut, lpTime, uSize);
 2997 
 2998     if(!uSize || !lpTime)
 2999         return MMSYSERR_INVALPARAM;
 3000 
 3001     if(uSize < sizeof(MMTIME))
 3002         return MMSYSERR_ERROR;
 3003 
 3004     return WINMM_GetPosition((HWAVE)hWaveOut, lpTime);
 3005 }
 3006 
 3007 /**************************************************************************
 3008  *              waveOutGetPitch     [WINMM.@]
 3009  */
 3010 UINT WINAPI waveOutGetPitch(HWAVEOUT hWaveOut, LPDWORD lpdw)
 3011 {
 3012     TRACE("(%p, %p)\n", hWaveOut, lpdw);
 3013     return MMSYSERR_NOTSUPPORTED;
 3014 }
 3015 
 3016 /**************************************************************************
 3017  *              waveOutSetPitch     [WINMM.@]
 3018  */
 3019 UINT WINAPI waveOutSetPitch(HWAVEOUT hWaveOut, DWORD dw)
 3020 {
 3021     TRACE("(%p, %08x)\n", hWaveOut, dw);
 3022 
 3023     return MMSYSERR_NOTSUPPORTED;
 3024 }
 3025 
 3026 /**************************************************************************
 3027  *              waveOutGetPlaybackRate  [WINMM.@]
 3028  */
 3029 UINT WINAPI waveOutGetPlaybackRate(HWAVEOUT hWaveOut, LPDWORD lpdw)
 3030 {
 3031     TRACE("(%p, %p)\n", hWaveOut, lpdw);
 3032 
 3033     return MMSYSERR_NOTSUPPORTED;
 3034 }
 3035 
 3036 /**************************************************************************
 3037  *              waveOutSetPlaybackRate  [WINMM.@]
 3038  */
 3039 UINT WINAPI waveOutSetPlaybackRate(HWAVEOUT hWaveOut, DWORD dw)
 3040 {
 3041     TRACE("(%p, %08x)\n", hWaveOut, dw);
 3042 
 3043     return MMSYSERR_NOTSUPPORTED;
 3044 }
 3045 
 3046 /**************************************************************************
 3047  *              waveOutGetVolume    [WINMM.@]
 3048  */
 3049 UINT WINAPI waveOutGetVolume(HWAVEOUT hWaveOut, DWORD *out)
 3050 {
 3051     WINMM_Device *device;
 3052     UINT32 channels;
 3053     float *vols;
 3054     HRESULT hr;
 3055 
 3056     TRACE("(%p, %p)\n", hWaveOut, out);
 3057 
 3058     if(!out)
 3059         return MMSYSERR_INVALPARAM;
 3060 
 3061     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
 3062 
 3063     if(!WINMM_ValidateAndLock(device))
 3064         return MMSYSERR_INVALHANDLE;
 3065 
 3066     hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
 3067     if(FAILED(hr)){
 3068         LeaveCriticalSection(&device->lock);
 3069         WARN("GetChannelCount failed: %08x\n", hr);
 3070         return MMSYSERR_ERROR;
 3071     }
 3072 
 3073     vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
 3074     if(!vols){
 3075         LeaveCriticalSection(&device->lock);
 3076         return MMSYSERR_NOMEM;
 3077     }
 3078 
 3079     hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
 3080     if(FAILED(hr)){
 3081         LeaveCriticalSection(&device->lock);
 3082         HeapFree(GetProcessHeap(), 0, vols);
 3083         WARN("GetAllVolumes failed: %08x\n", hr);
 3084         return MMSYSERR_ERROR;
 3085     }
 3086 
 3087     LeaveCriticalSection(&device->lock);
 3088 
 3089     *out = ((UINT16)(vols[0] * (DWORD)0xFFFF));
 3090     if(channels > 1)
 3091         *out |= ((UINT16)(vols[1] * (DWORD)0xFFFF)) << 16;
 3092 
 3093     HeapFree(GetProcessHeap(), 0, vols);
 3094 
 3095     return MMSYSERR_NOERROR;
 3096 }
 3097 
 3098 /**************************************************************************
 3099  *              waveOutSetVolume    [WINMM.@]
 3100  */
 3101 UINT WINAPI waveOutSetVolume(HWAVEOUT hWaveOut, DWORD in)
 3102 {
 3103     WINMM_Device *device;
 3104     UINT32 channels;
 3105     float *vols;
 3106     HRESULT hr;
 3107 
 3108     TRACE("(%p, %08x)\n", hWaveOut, in);
 3109 
 3110     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
 3111 
 3112     if(!WINMM_ValidateAndLock(device))
 3113         return MMSYSERR_INVALHANDLE;
 3114 
 3115     hr = IAudioStreamVolume_GetChannelCount(device->volume, &channels);
 3116     if(FAILED(hr)){
 3117         LeaveCriticalSection(&device->lock);
 3118         WARN("GetChannelCount failed: %08x\n", hr);
 3119         return MMSYSERR_ERROR;
 3120     }
 3121 
 3122     vols = HeapAlloc(GetProcessHeap(), 0, sizeof(float) * channels);
 3123     if(!vols){
 3124         LeaveCriticalSection(&device->lock);
 3125         return MMSYSERR_NOMEM;
 3126     }
 3127 
 3128     hr = IAudioStreamVolume_GetAllVolumes(device->volume, channels, vols);
 3129     if(FAILED(hr)){
 3130         LeaveCriticalSection(&device->lock);
 3131         HeapFree(GetProcessHeap(), 0, vols);
 3132         WARN("GetAllVolumes failed: %08x\n", hr);
 3133         return MMSYSERR_ERROR;
 3134     }
 3135 
 3136     vols[0] = (float)((in & 0xFFFF) / (float)0xFFFF);
 3137     if(channels > 1)
 3138         vols[1] = (float)((in >> 16) / (float)0xFFFF);
 3139 
 3140     hr = IAudioStreamVolume_SetAllVolumes(device->volume, channels, vols);
 3141     if(FAILED(hr)){
 3142         LeaveCriticalSection(&device->lock);
 3143         HeapFree(GetProcessHeap(), 0, vols);
 3144         WARN("SetAllVolumes failed: %08x\n", hr);
 3145         return MMSYSERR_ERROR;
 3146     }
 3147 
 3148     LeaveCriticalSection(&device->lock);
 3149 
 3150     HeapFree(GetProcessHeap(), 0, vols);
 3151 
 3152     return MMSYSERR_NOERROR;
 3153 }
 3154 
 3155 /**************************************************************************
 3156  *              waveOutGetID        [WINMM.@]
 3157  */
 3158 UINT WINAPI waveOutGetID(HWAVEOUT hWaveOut, UINT* lpuDeviceID)
 3159 {
 3160     WINMM_Device *device;
 3161     UINT dev, junk;
 3162     BOOL is_out;
 3163 
 3164     TRACE("(%p, %p)\n", hWaveOut, lpuDeviceID);
 3165 
 3166     if(!lpuDeviceID)
 3167         return MMSYSERR_INVALPARAM;
 3168 
 3169     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveOut);
 3170     if(!WINMM_ValidateAndLock(device))
 3171         return MMSYSERR_INVALHANDLE;
 3172 
 3173     LeaveCriticalSection(&device->lock);
 3174 
 3175     WINMM_DecomposeHWAVE((HWAVE)hWaveOut, lpuDeviceID, &is_out, &dev, &junk);
 3176 
 3177     return MMSYSERR_NOERROR;
 3178 }
 3179 
 3180 static UINT WINMM_QueryInstanceIDSize(UINT device, DWORD_PTR *len, BOOL is_out)
 3181 {
 3182     UINT count;
 3183     WINMM_MMDevice **devices;
 3184 
 3185     TRACE("(%u, %p, %d)\n", device, len, is_out);
 3186 
 3187     if(is_out){
 3188         count = g_outmmdevices_count;
 3189         devices = g_out_map;
 3190     }else{
 3191         count = g_inmmdevices_count;
 3192         devices = g_in_map;
 3193     }
 3194 
 3195     if(device >= count)
 3196         return MMSYSERR_INVALHANDLE;
 3197 
 3198     EnterCriticalSection(&g_devthread_lock);
 3199     *len = (lstrlenW(devices[device]->dev_id) + 1) * sizeof(WCHAR);
 3200     LeaveCriticalSection(&g_devthread_lock);
 3201 
 3202     return MMSYSERR_NOERROR;
 3203 }
 3204 
 3205 static UINT WINMM_QueryInstanceID(UINT device, WCHAR *str, DWORD_PTR len,
 3206         BOOL is_out)
 3207 {
 3208     UINT count, id_len;
 3209     WINMM_MMDevice **devices;
 3210 
 3211     TRACE("(%u, %p, %d)\n", device, str, is_out);
 3212 
 3213     if(is_out){
 3214         count = g_outmmdevices_count;
 3215         devices = g_out_map;
 3216     }else{
 3217         count = g_inmmdevices_count;
 3218         devices = g_in_map;
 3219     }
 3220 
 3221     if(device >= count)
 3222         return MMSYSERR_INVALHANDLE;
 3223 
 3224     EnterCriticalSection(&g_devthread_lock);
 3225     id_len = (lstrlenW(devices[device]->dev_id) + 1) * sizeof(WCHAR);
 3226     if(len < id_len){
 3227         LeaveCriticalSection(&g_devthread_lock);
 3228         return MMSYSERR_ERROR;
 3229     }
 3230 
 3231     memcpy(str, devices[device]->dev_id, id_len);
 3232     LeaveCriticalSection(&g_devthread_lock);
 3233 
 3234     return MMSYSERR_NOERROR;
 3235 }
 3236 
 3237 static UINT get_device_interface(UINT msg, BOOL is_out, UINT index, WCHAR *out, ULONG *out_len)
 3238 {
 3239     WINMM_QueryInterfaceInfo info;
 3240     UINT ret;
 3241 
 3242     if(!WINMM_StartDevicesThread())
 3243         return MMSYSERR_NODRIVER;
 3244 
 3245     info.is_out = is_out;
 3246     info.index = index;
 3247     info.str = out;
 3248     info.len_bytes = out_len;
 3249 
 3250     ret = SendMessageW(g_devices_hwnd, msg, (DWORD_PTR)&info, 0);
 3251     InterlockedDecrement(&g_devthread_token);
 3252     return ret;
 3253 }
 3254 
 3255 /**************************************************************************
 3256  *              waveOutMessage      [WINMM.@]
 3257  */
 3258 UINT WINAPI waveOutMessage(HWAVEOUT hWaveOut, UINT uMessage,
 3259                            DWORD_PTR dwParam1, DWORD_PTR dwParam2)
 3260 {
 3261     TRACE("(%p, %u, %lx, %lx)\n", hWaveOut, uMessage, dwParam1, dwParam2);
 3262 
 3263     switch(uMessage){
 3264     case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
 3265         return WINMM_QueryInstanceIDSize(HandleToULong(hWaveOut),
 3266                 (DWORD_PTR*)dwParam1, TRUE);
 3267     case DRV_QUERYFUNCTIONINSTANCEID:
 3268         return WINMM_QueryInstanceID(HandleToULong(hWaveOut), (WCHAR*)dwParam1, dwParam2, TRUE);
 3269     case DRV_QUERYDEVICEINTERFACESIZE:
 3270         return get_device_interface(DRV_QUERYDEVICEINTERFACESIZE, TRUE, HandleToULong(hWaveOut),
 3271                 NULL, (ULONG*)dwParam1);
 3272     case DRV_QUERYDEVICEINTERFACE:
 3273         {
 3274             ULONG size = dwParam2;
 3275             return get_device_interface(DRV_QUERYDEVICEINTERFACE, TRUE, HandleToULong(hWaveOut),
 3276                     (WCHAR*)dwParam1, &size);
 3277         }
 3278     case DRV_QUERYMAPPABLE:
 3279         return MMSYSERR_NOERROR;
 3280     case DRVM_MAPPER_PREFERRED_GET:
 3281         if(!dwParam1 || !dwParam2)
 3282             return MMSYSERR_INVALPARAM;
 3283 
 3284         if(g_outmmdevices_count > 0)
 3285             /* Device 0 is always the default device */
 3286             *(DWORD *)dwParam1 = 0;
 3287         else
 3288             *(DWORD *)dwParam1 = -1;
 3289 
 3290         /* Status flags */
 3291         *(DWORD *)dwParam2 = 0;
 3292 
 3293         return MMSYSERR_NOERROR;
 3294     }
 3295 
 3296     TRACE("Message not supported: %u\n", uMessage);
 3297 
 3298     return MMSYSERR_NOTSUPPORTED;
 3299 }
 3300 
 3301 /**************************************************************************
 3302  *              waveInGetNumDevs        [WINMM.@]
 3303  */
 3304 UINT WINAPI waveInGetNumDevs(void)
 3305 {
 3306     HRESULT hr = WINMM_InitMMDevices();
 3307     if(FAILED(hr))
 3308         return 0;
 3309 
 3310     TRACE("count: %u\n", g_inmmdevices_count);
 3311 
 3312     return g_inmmdevices_count;
 3313 }
 3314 
 3315 /**************************************************************************
 3316  *              waveInGetDevCapsW       [WINMM.@]
 3317  */
 3318 UINT WINAPI waveInGetDevCapsW(UINT_PTR uDeviceID, LPWAVEINCAPSW lpCaps, UINT uSize)
 3319 {
 3320     WAVEINCAPSW mapper_caps, *caps;
 3321     HRESULT hr;
 3322 
 3323     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
 3324 
 3325     hr = WINMM_InitMMDevices();
 3326     if(FAILED(hr))
 3327         return MMSYSERR_NODRIVER;
 3328 
 3329     if(!lpCaps)
 3330         return MMSYSERR_INVALPARAM;
 3331 
 3332     if(WINMM_IsMapper(uDeviceID)){
 3333         mapper_caps.wMid = 0xFF;
 3334         mapper_caps.wPid = 0xFF;
 3335         mapper_caps.vDriverVersion = 0x00010001;
 3336         mapper_caps.dwFormats = 0xFFFFFFFF;
 3337         mapper_caps.wReserved1 = 0;
 3338         mapper_caps.wChannels = 2;
 3339         LoadStringW(hWinMM32Instance, IDS_MAPPER_NAME, mapper_caps.szPname, MAXPNAMELEN);
 3340 
 3341         caps = &mapper_caps;
 3342     }else{
 3343         if(uDeviceID >= g_inmmdevices_count){
 3344             WINMM_Device *device = WINMM_GetDeviceFromHWAVE((HWAVE)uDeviceID);
 3345 
 3346             if(!WINMM_ValidateAndLock(device))
 3347                 return MMSYSERR_BADDEVICEID;
 3348 
 3349             caps = &device->parent->in_caps;
 3350 
 3351             LeaveCriticalSection(&device->lock);
 3352         }else{
 3353             caps = &read_map(g_in_map, uDeviceID)->in_caps;
 3354         }
 3355     }
 3356 
 3357     memcpy(lpCaps, caps, min(uSize, sizeof(*lpCaps)));
 3358 
 3359     return MMSYSERR_NOERROR;
 3360 }
 3361 
 3362 /**************************************************************************
 3363  *              waveInGetDevCapsA       [WINMM.@]
 3364  */
 3365 UINT WINAPI waveInGetDevCapsA(UINT_PTR uDeviceID, LPWAVEINCAPSA lpCaps, UINT uSize)
 3366 {
 3367     UINT ret;
 3368     WAVEINCAPSW wicW;
 3369 
 3370     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
 3371 
 3372     if(!lpCaps)
 3373         return MMSYSERR_INVALPARAM;
 3374 
 3375     ret = waveInGetDevCapsW(uDeviceID, &wicW, sizeof(wicW));
 3376 
 3377     if (ret == MMSYSERR_NOERROR) {
 3378     WAVEINCAPSA wicA;
 3379     wicA.wMid           = wicW.wMid;
 3380     wicA.wPid           = wicW.wPid;
 3381     wicA.vDriverVersion = wicW.vDriverVersion;
 3382         WideCharToMultiByte( CP_ACP, 0, wicW.szPname, -1, wicA.szPname,
 3383                              sizeof(wicA.szPname), NULL, NULL );
 3384     wicA.dwFormats      = wicW.dwFormats;
 3385     wicA.wChannels      = wicW.wChannels;
 3386     wicA.wReserved1     = wicW.wReserved1;
 3387     memcpy(lpCaps, &wicA, min(uSize, sizeof(wicA)));
 3388     }
 3389     return ret;
 3390 }
 3391 
 3392 /**************************************************************************
 3393  *              waveInOpen          [WINMM.@]
 3394  */
 3395 MMRESULT WINAPI waveInOpen(HWAVEIN* lphWaveIn, UINT uDeviceID,
 3396                            LPCWAVEFORMATEX lpFormat, DWORD_PTR dwCallback,
 3397                            DWORD_PTR dwInstance, DWORD dwFlags)
 3398 {
 3399     LRESULT res;
 3400     WINMM_OpenInfo info;
 3401     WINMM_CBInfo cb_info;
 3402 
 3403     TRACE("(%p, %x, %p, %lx, %lx, %08x)\n", lphWaveIn, uDeviceID, lpFormat,
 3404             dwCallback, dwInstance, dwFlags);
 3405 
 3406     if(!lphWaveIn && !(dwFlags & WAVE_FORMAT_QUERY))
 3407         return MMSYSERR_INVALPARAM;
 3408 
 3409     res = WINMM_CheckCallback(dwCallback, dwFlags, FALSE);
 3410     if(res != MMSYSERR_NOERROR)
 3411         return res;
 3412 
 3413     if(!WINMM_StartDevicesThread())
 3414         return MMSYSERR_NODRIVER;
 3415 
 3416     info.handle = 0;
 3417     info.format = (WAVEFORMATEX*)lpFormat;
 3418     info.callback = dwCallback;
 3419     info.cb_user = dwInstance;
 3420     info.req_device = uDeviceID;
 3421     info.flags = dwFlags;
 3422     info.reset = TRUE;
 3423 
 3424     res = SendMessageW(g_devices_hwnd, WIDM_OPEN, (DWORD_PTR)&info, 0);
 3425     InterlockedDecrement(&g_devthread_token);
 3426     if(res != MMSYSERR_NOERROR || (dwFlags & WAVE_FORMAT_QUERY))
 3427         return res;
 3428 
 3429     if(lphWaveIn)
 3430         *lphWaveIn = (HWAVEIN)info.handle;
 3431 
 3432     cb_info.flags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
 3433     cb_info.callback = dwCallback;
 3434     cb_info.user = dwInstance;
 3435     cb_info.hwave = info.handle;
 3436 
 3437     WINMM_NotifyClient(&cb_info, WIM_OPEN, 0, 0);
 3438 
 3439     return res;
 3440 }
 3441 
 3442 /**************************************************************************
 3443  *              waveInClose         [WINMM.@]
 3444  */
 3445 UINT WINAPI waveInClose(HWAVEIN hWaveIn)
 3446 {
 3447     WINMM_Device *device;
 3448     WINMM_CBInfo cb_info;
 3449     UINT res;
 3450 
 3451     TRACE("(%p)\n", hWaveIn);
 3452 
 3453     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
 3454 
 3455     if(!WINMM_ValidateAndLock(device))
 3456         return MMSYSERR_INVALHANDLE;
 3457 
 3458     cb_info = device->cb_info;
 3459 
 3460     LeaveCriticalSection(&device->lock);
 3461 
 3462     res = SendMessageW(g_devices_hwnd, WIDM_CLOSE, (WPARAM)hWaveIn, 0);
 3463 
 3464     if(res == MMSYSERR_NOERROR)
 3465         WINMM_NotifyClient(&cb_info, WIM_CLOSE, 0, 0);
 3466 
 3467     return res;
 3468 }
 3469 
 3470 /**************************************************************************
 3471  *              waveInPrepareHeader     [WINMM.@]
 3472  */
 3473 UINT WINAPI waveInPrepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
 3474                 UINT uSize)
 3475 {
 3476     TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
 3477 
 3478     if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
 3479         return MMSYSERR_INVALPARAM;
 3480 
 3481     if(lpWaveInHdr->dwFlags & WHDR_PREPARED)
 3482         return MMSYSERR_NOERROR;
 3483 
 3484     return WINMM_PrepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
 3485 }
 3486 
 3487 /**************************************************************************
 3488  *              waveInUnprepareHeader   [WINMM.@]
 3489  */
 3490 UINT WINAPI waveInUnprepareHeader(HWAVEIN hWaveIn, WAVEHDR* lpWaveInHdr,
 3491                   UINT uSize)
 3492 {
 3493     TRACE("(%p, %p, %u)\n", hWaveIn, lpWaveInHdr, uSize);
 3494 
 3495     if(!lpWaveInHdr || uSize < sizeof(WAVEHDR))
 3496         return MMSYSERR_INVALPARAM;
 3497 
 3498     if(lpWaveInHdr->dwFlags & WHDR_INQUEUE)
 3499         return WAVERR_STILLPLAYING;
 3500 
 3501     if(!(lpWaveInHdr->dwFlags & WHDR_PREPARED))
 3502         return MMSYSERR_NOERROR;
 3503 
 3504     return WINMM_UnprepareHeader((HWAVE)hWaveIn, lpWaveInHdr);
 3505 }
 3506 
 3507 /**************************************************************************
 3508  *              waveInAddBuffer     [WINMM.@]
 3509  */
 3510 UINT WINAPI waveInAddBuffer(HWAVEIN hWaveIn, WAVEHDR *header, UINT uSize)
 3511 {
 3512     WINMM_Device *device;
 3513 
 3514     TRACE("(%p, %p, %u)\n", hWaveIn, header, uSize);
 3515 
 3516     if(!header || uSize < sizeof(WAVEHDR))
 3517         return MMSYSERR_INVALPARAM;
 3518 
 3519     if(!(header->dwFlags & WHDR_PREPARED))
 3520         return WAVERR_UNPREPARED;
 3521 
 3522     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
 3523 
 3524     if(!WINMM_ValidateAndLock(device))
 3525         return MMSYSERR_INVALHANDLE;
 3526 
 3527     if(!device->first)
 3528         device->first = device->last = header;
 3529     else{
 3530         device->last->lpNext = header;
 3531         device->last = header;
 3532     }
 3533 
 3534     header->dwBytesRecorded = 0;
 3535     header->lpNext = NULL;
 3536     header->dwFlags &= ~WHDR_DONE;
 3537     header->dwFlags |= WHDR_INQUEUE;
 3538 
 3539     LeaveCriticalSection(&device->lock);
 3540 
 3541     return MMSYSERR_NOERROR;
 3542 }
 3543 
 3544 /**************************************************************************
 3545  *              waveInReset     [WINMM.@]
 3546  */
 3547 UINT WINAPI waveInReset(HWAVEIN hWaveIn)
 3548 {
 3549     TRACE("(%p)\n", hWaveIn);
 3550 
 3551     return WINMM_Reset((HWAVE)hWaveIn);
 3552 }
 3553 
 3554 /**************************************************************************
 3555  *              waveInStart     [WINMM.@]
 3556  */
 3557 UINT WINAPI waveInStart(HWAVEIN hWaveIn)
 3558 {
 3559     WINMM_Device *device;
 3560     MMRESULT mr;
 3561 
 3562     TRACE("(%p)\n", hWaveIn);
 3563 
 3564     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
 3565 
 3566     if(!WINMM_ValidateAndLock(device))
 3567         return MMSYSERR_INVALHANDLE;
 3568 
 3569     mr = WINMM_BeginPlaying(device);
 3570 
 3571     LeaveCriticalSection(&device->lock);
 3572 
 3573     return mr;
 3574 }
 3575 
 3576 /**************************************************************************
 3577  *              waveInStop      [WINMM.@]
 3578  */
 3579 UINT WINAPI waveInStop(HWAVEIN hWaveIn)
 3580 {
 3581     WINMM_CBInfo cb_info;
 3582     WINMM_Device *device;
 3583     WAVEHDR *buf;
 3584     HRESULT hr;
 3585 
 3586     TRACE("(%p)\n", hWaveIn);
 3587 
 3588     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
 3589 
 3590     if(!WINMM_ValidateAndLock(device))
 3591         return MMSYSERR_INVALHANDLE;
 3592 
 3593     hr = WINMM_Pause(device);
 3594     if(FAILED(hr)){
 3595         LeaveCriticalSection(&device->lock);
 3596         return MMSYSERR_ERROR;
 3597     }
 3598     device->stopped = TRUE;
 3599 
 3600     buf = device->first;
 3601     if(buf && buf->dwBytesRecorded > 0){
 3602         device->first = buf->lpNext;
 3603     }else
 3604         buf = NULL;
 3605 
 3606     cb_info = device->cb_info;
 3607 
 3608     LeaveCriticalSection(&device->lock);
 3609 
 3610     if(buf){
 3611         buf->dwFlags &= ~WHDR_INQUEUE;
 3612         buf->dwFlags |= WHDR_DONE;
 3613         WINMM_NotifyClient(&cb_info, WIM_DATA, (DWORD_PTR)buf, 0);
 3614     }
 3615 
 3616     return MMSYSERR_NOERROR;
 3617 }
 3618 
 3619 /**************************************************************************
 3620  *              waveInGetPosition   [WINMM.@]
 3621  */
 3622 UINT WINAPI waveInGetPosition(HWAVEIN hWaveIn, LPMMTIME lpTime,
 3623                   UINT uSize)
 3624 {
 3625     TRACE("(%p, %p, %u)\n", hWaveIn, lpTime, uSize);
 3626 
 3627     if(!uSize || !lpTime)
 3628         return MMSYSERR_INVALPARAM;
 3629 
 3630     if(uSize < sizeof(MMTIME))
 3631         return MMSYSERR_ERROR;
 3632 
 3633     return WINMM_GetPosition((HWAVE)hWaveIn, lpTime);
 3634 }
 3635 
 3636 /**************************************************************************
 3637  *              waveInGetID         [WINMM.@]
 3638  */
 3639 UINT WINAPI waveInGetID(HWAVEIN hWaveIn, UINT* lpuDeviceID)
 3640 {
 3641     UINT dev, junk;
 3642     BOOL is_out;
 3643     WINMM_Device *device;
 3644 
 3645     TRACE("(%p, %p)\n", hWaveIn, lpuDeviceID);
 3646 
 3647     if(!lpuDeviceID)
 3648         return MMSYSERR_INVALPARAM;
 3649 
 3650     device = WINMM_GetDeviceFromHWAVE((HWAVE)hWaveIn);
 3651     if(!WINMM_ValidateAndLock(device))
 3652         return MMSYSERR_INVALHANDLE;
 3653 
 3654     LeaveCriticalSection(&device->lock);
 3655 
 3656     WINMM_DecomposeHWAVE((HWAVE)hWaveIn, lpuDeviceID, &is_out, &dev, &junk);
 3657 
 3658     return MMSYSERR_NOERROR;
 3659 }
 3660 
 3661 /**************************************************************************
 3662  *              waveInMessage       [WINMM.@]
 3663  */
 3664 UINT WINAPI waveInMessage(HWAVEIN hWaveIn, UINT uMessage,
 3665                           DWORD_PTR dwParam1, DWORD_PTR dwParam2)
 3666 {
 3667     TRACE("(%p, %u, %ld, %ld)\n", hWaveIn, uMessage, dwParam1, dwParam2);
 3668 
 3669     switch(uMessage){
 3670     case DRV_QUERYFUNCTIONINSTANCEIDSIZE:
 3671         return WINMM_QueryInstanceIDSize(HandleToULong(hWaveIn),
 3672                 (DWORD_PTR*)dwParam1, FALSE);
 3673     case DRV_QUERYFUNCTIONINSTANCEID:
 3674         return WINMM_QueryInstanceID(HandleToULong(hWaveIn), (WCHAR*)dwParam1, dwParam2, FALSE);
 3675     case DRV_QUERYDEVICEINTERFACESIZE:
 3676         return get_device_interface(DRV_QUERYDEVICEINTERFACESIZE, FALSE, HandleToULong(hWaveIn),
 3677                 NULL, (ULONG*)dwParam1);
 3678     case DRV_QUERYDEVICEINTERFACE:
 3679         {
 3680             ULONG size = dwParam2;
 3681             return get_device_interface(DRV_QUERYDEVICEINTERFACE, FALSE, HandleToULong(hWaveIn),
 3682                     (WCHAR*)dwParam1, &size);
 3683         }
 3684     case DRV_QUERYMAPPABLE:
 3685         return MMSYSERR_NOERROR;
 3686     case DRVM_MAPPER_PREFERRED_GET:
 3687         if(!dwParam1 || !dwParam2)
 3688             return MMSYSERR_INVALPARAM;
 3689 
 3690         if(g_inmmdevices_count > 0)
 3691             /* Device 0 is always the default device */
 3692             *(DWORD *)dwParam1 = 0;
 3693         else
 3694             *(DWORD *)dwParam1 = -1;
 3695 
 3696         /* Status flags */
 3697         *(DWORD *)dwParam2 = 0;
 3698 
 3699         return MMSYSERR_NOERROR;
 3700     }
 3701 
 3702     TRACE("Message not supported: %u\n", uMessage);
 3703 
 3704     return MMSYSERR_NOTSUPPORTED;
 3705 }
 3706 
 3707 UINT WINAPI mixerGetNumDevs(void)
 3708 {
 3709     HRESULT hr;
 3710 
 3711     TRACE("\n");
 3712 
 3713     hr = WINMM_InitMMDevices();
 3714     if(FAILED(hr))
 3715         return 0;
 3716 
 3717     return g_outmmdevices_count + g_inmmdevices_count;
 3718 }
 3719 
 3720 /**************************************************************************
 3721  *              mixerGetDevCapsA        [WINMM.@]
 3722  */
 3723 UINT WINAPI mixerGetDevCapsA(UINT_PTR uDeviceID, LPMIXERCAPSA lpCaps, UINT uSize)
 3724 {
 3725     MIXERCAPSW micW;
 3726     UINT       ret;
 3727 
 3728     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
 3729 
 3730     if(!lpCaps)
 3731         return MMSYSERR_INVALPARAM;
 3732 
 3733     ret = mixerGetDevCapsW(uDeviceID, &micW, sizeof(micW));
 3734 
 3735     if (ret == MMSYSERR_NOERROR) {
 3736         MIXERCAPSA micA;
 3737         micA.wMid           = micW.wMid;
 3738         micA.wPid           = micW.wPid;
 3739         micA.vDriverVersion = micW.vDriverVersion;
 3740         WideCharToMultiByte( CP_ACP, 0, micW.szPname, -1, micA.szPname,
 3741                              sizeof(micA.szPname), NULL, NULL );
 3742         micA.fdwSupport     = micW.fdwSupport;
 3743         micA.cDestinations  = micW.cDestinations;
 3744         memcpy(lpCaps, &micA, min(uSize, sizeof(micA)));
 3745     }
 3746     return ret;
 3747 }
 3748 
 3749 /**************************************************************************
 3750  *              mixerGetDevCapsW        [WINMM.@]
 3751  */
 3752 UINT WINAPI mixerGetDevCapsW(UINT_PTR uDeviceID, LPMIXERCAPSW lpCaps, UINT uSize)
 3753 {
 3754     WINMM_MMDevice *mmdevice;
 3755     MIXERCAPSW caps;
 3756     HRESULT hr;
 3757 
 3758     TRACE("(%lu, %p, %u)\n", uDeviceID, lpCaps, uSize);
 3759 
 3760     hr = WINMM_InitMMDevices();
 3761     if(FAILED(hr))
 3762         return MMSYSERR_NODRIVER;
 3763 
 3764     if(!lpCaps)
 3765         return MMSYSERR_INVALPARAM;
 3766 
 3767     if(!uSize)
 3768         return MMSYSERR_NOERROR;
 3769 
 3770     if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
 3771         mmdevice = WINMM_GetMixerMMDevice((HMIXEROBJ)uDeviceID,
 3772                 MIXER_OBJECTF_MIXER, NULL);
 3773     else if(uDeviceID < g_outmmdevices_count)
 3774         mmdevice = read_map(g_out_map, uDeviceID);
 3775     else
 3776         mmdevice = read_map(g_in_map, uDeviceID - g_outmmdevices_count);
 3777 
 3778     if(!mmdevice)
 3779         return MMSYSERR_BADDEVICEID;
 3780 
 3781     if(mmdevice->dataflow == eRender)
 3782         memcpy(caps.szPname, mmdevice->out_caps.szPname, sizeof(caps.szPname));
 3783     else
 3784         memcpy(caps.szPname, mmdevice->in_caps.szPname, sizeof(caps.szPname));
 3785 
 3786     caps.wMid = 0xFF;
 3787     caps.wPid = 0xFF;
 3788     caps.vDriverVersion = 0x00010001;
 3789     caps.fdwSupport = 0;
 3790     caps.cDestinations = 1;
 3791 
 3792     memcpy(lpCaps, &caps, uSize);
 3793 
 3794     return MMSYSERR_NOERROR;
 3795 }
 3796 
 3797 /**************************************************************************
 3798  *              mixerOpen           [WINMM.@]
 3799  */
 3800 UINT WINAPI mixerOpen(LPHMIXER lphMix, UINT uDeviceID, DWORD_PTR dwCallback,
 3801                       DWORD_PTR dwInstance, DWORD fdwOpen)
 3802 {
 3803     WINMM_MMDevice *mmdevice;
 3804     MMRESULT mr;
 3805     HRESULT hr;
 3806 
 3807     TRACE("(%p, %d, %lx, %lx, %x)\n", lphMix, uDeviceID, dwCallback,
 3808             dwInstance, fdwOpen);
 3809 
 3810     hr = WINMM_InitMMDevices();
 3811     if(FAILED(hr))
 3812         return MMSYSERR_NODRIVER;
 3813 
 3814     if(!lphMix)
 3815         return MMSYSERR_INVALPARAM;
 3816 
 3817     mr = WINMM_CheckCallback(dwCallback, fdwOpen, TRUE);
 3818     if(mr != MMSYSERR_NOERROR)
 3819         return mr;
 3820 
 3821     if(uDeviceID >= g_outmmdevices_count + g_inmmdevices_count)
 3822         return MMSYSERR_BADDEVICEID;
 3823 
 3824     if(uDeviceID < g_outmmdevices_count){
 3825         mmdevice = read_map(g_out_map, uDeviceID);
 3826         *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID, TRUE,
 3827                 mmdevice->mixer_count);
 3828     }else{
 3829         mmdevice = read_map(g_in_map, uDeviceID - g_outmmdevices_count);
 3830         *lphMix = (HMIXER)WINMM_MakeHWAVE(uDeviceID - g_outmmdevices_count,
 3831                 FALSE, mmdevice->mixer_count);
 3832     }
 3833 
 3834     ++mmdevice->mixer_count;
 3835 
 3836     return MMSYSERR_NOERROR;
 3837 }
 3838 
 3839 /**************************************************************************
 3840  *              mixerClose          [WINMM.@]
 3841  */
 3842 UINT WINAPI mixerClose(HMIXER hMix)
 3843 {
 3844     TRACE("(%p)\n", hMix);
 3845 
 3846     return MMSYSERR_NOERROR;
 3847 }
 3848 
 3849 /**************************************************************************
 3850  *              mixerGetID          [WINMM.@]
 3851  */
 3852 UINT WINAPI mixerGetID(HMIXEROBJ hmix, LPUINT lpid, DWORD fdwID)
 3853 {
 3854     WINMM_MMDevice *mmdevice;
 3855     HRESULT hr;
 3856 
 3857     TRACE("(%p, %p, %x)\n", hmix, lpid, fdwID);
 3858 
 3859     hr = WINMM_InitMMDevices();
 3860     if(FAILED(hr))
 3861         return MMSYSERR_NODRIVER;
 3862 
 3863     if(!lpid)
 3864         return MMSYSERR_INVALPARAM;
 3865 
 3866     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwID, lpid);
 3867     if(!mmdevice)
 3868         return MMSYSERR_INVALHANDLE;
 3869 
 3870     if(mmdevice->in_caps.szPname[0] != '\0')
 3871         *lpid += g_outmmdevices_count;
 3872 
 3873     return MMSYSERR_NOERROR;
 3874 }
 3875 
 3876 /**************************************************************************
 3877  *              mixerGetControlDetailsW     [WINMM.@]
 3878  */
 3879 UINT WINAPI mixerGetControlDetailsW(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdW,
 3880                     DWORD fdwDetails)
 3881 {
 3882     WINMM_ControlDetails details;
 3883 
 3884     TRACE("(%p, %p, %x)\n", hmix, lpmcdW, fdwDetails);
 3885 
 3886     if(!WINMM_StartDevicesThread())
 3887         return MMSYSERR_NODRIVER;
 3888 
 3889     if(!lpmcdW || !lpmcdW->paDetails)
 3890         return MMSYSERR_INVALPARAM;
 3891 
 3892     TRACE("dwControlID: %u\n", lpmcdW->dwControlID);
 3893 
 3894     details.hmix = hmix;
 3895     details.details = lpmcdW;
 3896     details.flags = fdwDetails;
 3897 
 3898     return SendMessageW(g_devices_hwnd, MXDM_GETCONTROLDETAILS,
 3899             (DWORD_PTR)&details, 0);
 3900 }
 3901 
 3902 /**************************************************************************
 3903  *              mixerGetControlDetailsA [WINMM.@]
 3904  */
 3905 UINT WINAPI mixerGetControlDetailsA(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcdA,
 3906                                     DWORD fdwDetails)
 3907 {
 3908     UINT ret = MMSYSERR_NOTSUPPORTED;
 3909 
 3910     TRACE("(%p, %p, %08x)\n", hmix, lpmcdA, fdwDetails);
 3911 
 3912     if (lpmcdA == NULL || lpmcdA->cbStruct != sizeof(*lpmcdA))
 3913     return MMSYSERR_INVALPARAM;
 3914 
 3915     switch (fdwDetails & MIXER_GETCONTROLDETAILSF_QUERYMASK) {
 3916     case MIXER_GETCONTROLDETAILSF_VALUE:
 3917     /* can safely use A structure as it is, no string inside */
 3918     ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
 3919     break;
 3920     case MIXER_GETCONTROLDETAILSF_LISTTEXT:
 3921     {
 3922             MIXERCONTROLDETAILS_LISTTEXTA *pDetailsA = lpmcdA->paDetails;
 3923             MIXERCONTROLDETAILS_LISTTEXTW *pDetailsW;
 3924         int size = max(1, lpmcdA->cChannels) * sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
 3925             unsigned int i;
 3926 
 3927         if (lpmcdA->u.cMultipleItems != 0) {
 3928         size *= lpmcdA->u.cMultipleItems;
 3929         }
 3930         pDetailsW = HeapAlloc(GetProcessHeap(), 0, size);
 3931             lpmcdA->paDetails = pDetailsW;
 3932             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTW);
 3933         /* set up lpmcd->paDetails */
 3934         ret = mixerGetControlDetailsW(hmix, lpmcdA, fdwDetails);
 3935         /* copy from lpmcd->paDetails back to paDetailsW; */
 3936             if (ret == MMSYSERR_NOERROR) {
 3937                 for (i = 0; i < lpmcdA->u.cMultipleItems * lpmcdA->cChannels; i++) {
 3938                     pDetailsA->dwParam1 = pDetailsW->dwParam1;
 3939                     pDetailsA->dwParam2 = pDetailsW->dwParam2;
 3940                     WideCharToMultiByte( CP_ACP, 0, pDetailsW->szName, -1,
 3941                                          pDetailsA->szName,
 3942                                          sizeof(pDetailsA->szName), NULL, NULL );
 3943                     pDetailsA++;
 3944                     pDetailsW++;
 3945                 }
 3946                 pDetailsA -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
 3947                 pDetailsW -= lpmcdA->u.cMultipleItems * lpmcdA->cChannels;
 3948             }
 3949         HeapFree(GetProcessHeap(), 0, pDetailsW);
 3950         lpmcdA->paDetails = pDetailsA;
 3951             lpmcdA->cbDetails = sizeof(MIXERCONTROLDETAILS_LISTTEXTA);
 3952     }
 3953     break;
 3954     default:
 3955     WARN("Unsupported fdwDetails=0x%08x\n", fdwDetails);
 3956     }
 3957 
 3958     return ret;
 3959 }
 3960 
 3961 /**************************************************************************
 3962  *              mixerGetLineControlsA   [WINMM.@]
 3963  */
 3964 UINT WINAPI mixerGetLineControlsA(HMIXEROBJ hmix, LPMIXERLINECONTROLSA lpmlcA,
 3965                   DWORD fdwControls)
 3966 {
 3967     MIXERLINECONTROLSW  mlcW;
 3968     DWORD       ret;
 3969     unsigned int    i;
 3970 
 3971     TRACE("(%p, %p, %x)\n", hmix, lpmlcA, fdwControls);
 3972 
 3973     if (lpmlcA == NULL || lpmlcA->cbStruct != sizeof(*lpmlcA) ||
 3974     lpmlcA->cbmxctrl != sizeof(MIXERCONTROLA))
 3975     return MMSYSERR_INVALPARAM;
 3976 
 3977     mlcW.cbStruct = sizeof(mlcW);
 3978     mlcW.dwLineID = lpmlcA->dwLineID;
 3979     mlcW.u.dwControlID = lpmlcA->u.dwControlID;
 3980     mlcW.u.dwControlType = lpmlcA->u.dwControlType;
 3981 
 3982     /* Debugging on Windows shows for MIXER_GETLINECONTROLSF_ONEBYTYPE only,
 3983        the control count is assumed to be 1 - This is relied upon by a game,
 3984        "Dynomite Deluze"                                                    */
 3985     if (MIXER_GETLINECONTROLSF_ONEBYTYPE == (fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK)) {
 3986         mlcW.cControls = 1;
 3987     } else {
 3988         mlcW.cControls = lpmlcA->cControls;
 3989     }
 3990     mlcW.cbmxctrl = sizeof(MIXERCONTROLW);
 3991     mlcW.pamxctrl = HeapAlloc(GetProcessHeap(), 0,
 3992                   mlcW.cControls * mlcW.cbmxctrl);
 3993 
 3994     ret = mixerGetLineControlsW(hmix, &mlcW, fdwControls);
 3995 
 3996     if (ret == MMSYSERR_NOERROR) {
 3997     lpmlcA->dwLineID = mlcW.dwLineID;
 3998     lpmlcA->u.dwControlID = mlcW.u.dwControlID;
 3999     lpmlcA->u.dwControlType = mlcW.u.dwControlType;
 4000 
 4001     for (i = 0; i < mlcW.cControls; i++) {
 4002         lpmlcA->pamxctrl[i].cbStruct = sizeof(MIXERCONTROLA);
 4003         lpmlcA->pamxctrl[i].dwControlID = mlcW.pamxctrl[i].dwControlID;
 4004         lpmlcA->pamxctrl[i].dwControlType = mlcW.pamxctrl[i].dwControlType;
 4005         lpmlcA->pamxctrl[i].fdwControl = mlcW.pamxctrl[i].fdwControl;
 4006         lpmlcA->pamxctrl[i].cMultipleItems = mlcW.pamxctrl[i].cMultipleItems;
 4007             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szShortName, -1,
 4008                                  lpmlcA->pamxctrl[i].szShortName,
 4009                                  sizeof(lpmlcA->pamxctrl[i].szShortName), NULL, NULL );
 4010             WideCharToMultiByte( CP_ACP, 0, mlcW.pamxctrl[i].szName, -1,
 4011                                  lpmlcA->pamxctrl[i].szName,
 4012                                  sizeof(lpmlcA->pamxctrl[i].szName), NULL, NULL );
 4013         /* sizeof(lpmlcA->pamxctrl[i].Bounds) ==
 4014          * sizeof(mlcW.pamxctrl[i].Bounds) */
 4015         memcpy(&lpmlcA->pamxctrl[i].Bounds, &mlcW.pamxctrl[i].Bounds,
 4016            sizeof(mlcW.pamxctrl[i].Bounds));
 4017         /* sizeof(lpmlcA->pamxctrl[i].Metrics) ==
 4018          * sizeof(mlcW.pamxctrl[i].Metrics) */
 4019         memcpy(&lpmlcA->pamxctrl[i].Metrics, &mlcW.pamxctrl[i].Metrics,
 4020            sizeof(mlcW.pamxctrl[i].Metrics));
 4021     }
 4022     }
 4023 
 4024     HeapFree(GetProcessHeap(), 0, mlcW.pamxctrl);
 4025 
 4026     return ret;
 4027 }
 4028 
 4029 static UINT WINMM_GetVolumeLineControl(WINMM_MMDevice *mmdevice, DWORD line,
 4030         MIXERCONTROLW *ctl, DWORD flags)
 4031 {
 4032     ctl->dwControlID = (line == 0xFFFF0000) ? 0 : 2;
 4033     ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME;
 4034     ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
 4035     ctl->cMultipleItems = 0;
 4036     LoadStringW(hWinMM32Instance, IDS_VOLUME, ctl->szShortName, MIXER_SHORT_NAME_CHARS);
 4037     LoadStringW(hWinMM32Instance, IDS_VOLUME, ctl->szName, MIXER_LONG_NAME_CHARS);
 4038     ctl->Bounds.s1.dwMinimum = 0;
 4039     ctl->Bounds.s1.dwMaximum = 0xFFFF;
 4040     ctl->Metrics.cSteps = 192;
 4041 
 4042     return MMSYSERR_NOERROR;
 4043 }
 4044 
 4045 static UINT WINMM_GetMuteLineControl(WINMM_MMDevice *mmdevice, DWORD line,
 4046         MIXERCONTROLW *ctl, DWORD flags)
 4047 {
 4048     ctl->dwControlID = (line == 0xFFFF0000) ? 1 : 3;
 4049     ctl->dwControlType = MIXERCONTROL_CONTROLTYPE_MUTE;
 4050     ctl->fdwControl = MIXERCONTROL_CONTROLF_UNIFORM;
 4051     ctl->cMultipleItems = 0;
 4052     LoadStringW(hWinMM32Instance, IDS_MUTE, ctl->szShortName, MIXER_SHORT_NAME_CHARS);
 4053     LoadStringW(hWinMM32Instance, IDS_MUTE, ctl->szName, MIXER_LONG_NAME_CHARS);
 4054     ctl->Bounds.s1.dwMinimum = 0;
 4055     ctl->Bounds.s1.dwMaximum = 1;
 4056     ctl->Metrics.cSteps = 0;
 4057 
 4058     return MMSYSERR_NOERROR;
 4059 }
 4060 
 4061 /**************************************************************************
 4062  *              mixerGetLineControlsW       [WINMM.@]
 4063  */
 4064 UINT WINAPI mixerGetLineControlsW(HMIXEROBJ hmix, LPMIXERLINECONTROLSW lpmlcW,
 4065                   DWORD fdwControls)
 4066 {
 4067     WINMM_MMDevice *mmdevice;
 4068     HRESULT hr;
 4069 
 4070     TRACE("(%p, %p, %08x)\n", hmix, lpmlcW, fdwControls);
 4071 
 4072     hr = WINMM_InitMMDevices();
 4073     if(FAILED(hr))
 4074         return MMSYSERR_NODRIVER;
 4075 
 4076     if(fdwControls & ~(MIXER_GETLINECONTROLSF_ALL |
 4077                 MIXER_GETLINECONTROLSF_ONEBYID |
 4078                 MIXER_GETLINECONTROLSF_ONEBYTYPE |
 4079                 MIXER_OBJECTF_HMIXER |
 4080                 MIXER_OBJECTF_MIXER)){
 4081         WARN("Unknown GetLineControls flag: %x\n", fdwControls);
 4082         return MMSYSERR_INVALFLAG;
 4083     }
 4084 
 4085     if(!lpmlcW || lpmlcW->cbStruct < sizeof(*lpmlcW) || !lpmlcW->pamxctrl)
 4086         return MMSYSERR_INVALPARAM;
 4087 
 4088     TRACE("dwLineID: %u\n", lpmlcW->dwLineID);
 4089     TRACE("dwControl: %x\n", lpmlcW->u.dwControlID);
 4090     TRACE("cControls: %u\n", lpmlcW->cControls);
 4091 
 4092     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwControls, NULL);
 4093     if(!mmdevice)
 4094         return MMSYSERR_INVALHANDLE;
 4095 
 4096     switch(fdwControls & MIXER_GETLINECONTROLSF_QUERYMASK){
 4097     case MIXER_GETLINECONTROLSF_ALL:
 4098         if(lpmlcW->cControls != 2)
 4099             return MMSYSERR_INVALPARAM;
 4100         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
 4101             return MMSYSERR_INVALPARAM;
 4102         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
 4103             return MIXERR_INVALLINE;
 4104         WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
 4105                 &lpmlcW->pamxctrl[0], fdwControls);
 4106         WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
 4107                 &lpmlcW->pamxctrl[1], fdwControls);
 4108         return MMSYSERR_NOERROR;
 4109     case MIXER_GETLINECONTROLSF_ONEBYID:
 4110         if(lpmlcW->cControls != 1)
 4111             return MMSYSERR_INVALPARAM;
 4112         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
 4113             return MMSYSERR_INVALPARAM;
 4114         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
 4115             return MIXERR_INVALLINE;
 4116         if(lpmlcW->u.dwControlID == 0)
 4117             return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
 4118                     lpmlcW->pamxctrl, fdwControls);
 4119         if(lpmlcW->u.dwControlID == 1)
 4120             return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
 4121                     lpmlcW->pamxctrl, fdwControls);
 4122         return MMSYSERR_NOTSUPPORTED;
 4123     case MIXER_GETLINECONTROLSF_ONEBYTYPE:
 4124         if(lpmlcW->cControls != 1)
 4125             return MMSYSERR_INVALPARAM;
 4126         if(lpmlcW->cbmxctrl < sizeof(MIXERCONTROLW))
 4127             return MMSYSERR_INVALPARAM;
 4128         if(lpmlcW->dwLineID != 0 && lpmlcW->dwLineID != 0xFFFF0000)
 4129             return MIXERR_INVALLINE;
 4130         if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)
 4131             return WINMM_GetVolumeLineControl(mmdevice, lpmlcW->dwLineID,
 4132                     lpmlcW->pamxctrl, fdwControls);
 4133         if(lpmlcW->u.dwControlType == MIXERCONTROL_CONTROLTYPE_MUTE)
 4134             return WINMM_GetMuteLineControl(mmdevice, lpmlcW->dwLineID,
 4135                     lpmlcW->pamxctrl, fdwControls);
 4136         return MMSYSERR_NOTSUPPORTED;
 4137     }
 4138 
 4139     return MMSYSERR_NOTSUPPORTED;
 4140 }
 4141 
 4142 static UINT WINMM_GetSourceLineInfo(WINMM_MMDevice *mmdevice, UINT mmdev_index,
 4143         MIXERLINEW *info, DWORD flags)
 4144 {
 4145     BOOL is_out = TRUE;
 4146     if(mmdevice->in_caps.szPname[0] != '\0')
 4147         is_out = FALSE;
 4148 
 4149     if(info->dwSource != 0)
 4150         return MIXERR_INVALLINE;
 4151 
 4152     info->dwDestination = 0;
 4153     info->dwLineID = 0;
 4154     info->fdwLine = MIXERLINE_LINEF_ACTIVE | MIXERLINE_LINEF_SOURCE;
 4155     info->cConnections = 0;
 4156     info->cControls = 2;
 4157     /* volume & mute always affect all channels, so claim 1 channel */
 4158     info->cChannels = 1;
 4159     info->Target.dwDeviceID = mmdev_index;
 4160     info->Target.wMid = ~0;
 4161     info->Target.wPid = ~0;
 4162     info->Target.vDriverVersion = 0;
 4163 
 4164     LoadStringW(hWinMM32Instance, IDS_VOLUME, info->szShortName, MIXER_SHORT_NAME_CHARS);
 4165     LoadStringW(hWinMM32Instance, IDS_MASTER_VOLUME, info->szName, MIXER_LONG_NAME_CHARS);
 4166 
 4167     if(is_out){
 4168         info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT;
 4169         info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEOUT;
 4170         memcpy(info->Target.szPname, mmdevice->out_caps.szPname,
 4171                 sizeof(info->Target.szPname));
 4172     }else{
 4173         info->dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE;
 4174         info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
 4175         info->Target.szPname[0] = '\0';
 4176     }
 4177 
 4178     return MMSYSERR_NOERROR;
 4179 }
 4180 
 4181 static UINT WINMM_GetDestinationLineInfo(WINMM_MMDevice *mmdevice,
 4182         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
 4183 {
 4184     BOOL is_out = TRUE;
 4185     if(mmdevice->in_caps.szPname[0] != '\0')
 4186         is_out = FALSE;
 4187 
 4188     if(info->dwDestination != 0)
 4189         return MIXERR_INVALLINE;
 4190 
 4191     info->dwSource = 0xFFFFFFFF;
 4192     info->dwLineID = 0xFFFF0000;
 4193     info->fdwLine = MIXERLINE_LINEF_ACTIVE;
 4194     info->cConnections = 1;
 4195     info->cControls = 2;
 4196 
 4197     LoadStringW(hWinMM32Instance, IDS_VOLUME, info->szShortName, MIXER_SHORT_NAME_CHARS);
 4198     LoadStringW(hWinMM32Instance, IDS_MASTER_VOLUME, info->szName, MIXER_LONG_NAME_CHARS);
 4199 
 4200     info->Target.dwDeviceID = mmdev_index;
 4201     info->Target.wMid = ~0;
 4202     info->Target.wPid = ~0;
 4203     info->Target.vDriverVersion = 0;
 4204 
 4205     if(is_out){
 4206         info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
 4207         info->cChannels = mmdevice->out_caps.wChannels;
 4208         info->Target.dwType = MIXERLINE_TARGETTYPE_UNDEFINED;
 4209         info->Target.szPname[0] = '\0';
 4210     }else{
 4211         info->dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
 4212         info->cChannels = mmdevice->in_caps.wChannels;
 4213         info->Target.dwType = MIXERLINE_TARGETTYPE_WAVEIN;
 4214         memcpy(info->Target.szPname, mmdevice->in_caps.szPname,
 4215                 sizeof(info->Target.szPname));
 4216     }
 4217 
 4218     return MMSYSERR_NOERROR;
 4219 }
 4220 
 4221 static UINT WINMM_GetComponentTypeLineInfo(WINMM_MMDevice *mmdevice,
 4222         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
 4223 {
 4224     BOOL is_out = TRUE;
 4225     if(mmdevice->in_caps.szPname[0] != '\0')
 4226         is_out = FALSE;
 4227 
 4228     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_WAVEIN){
 4229         if(is_out)
 4230             return MIXERR_INVALLINE;
 4231         info->dwDestination = 0;
 4232         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
 4233     }
 4234 
 4235     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_DST_SPEAKERS){
 4236         if(!is_out)
 4237             return MIXERR_INVALLINE;
 4238         info->dwDestination = 0;
 4239         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
 4240     }
 4241 
 4242     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE){
 4243         if(is_out)
 4244             return MIXERR_INVALLINE;
 4245         info->dwSource = 0;
 4246         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
 4247     }
 4248 
 4249     if(info->dwComponentType == MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT){
 4250         if(!is_out)
 4251             return MIXERR_INVALLINE;
 4252         info->dwSource = 0;
 4253         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
 4254     }
 4255 
 4256     TRACE("Returning INVALLINE on this component type: %u\n",
 4257             info->dwComponentType);
 4258 
 4259     return MIXERR_INVALLINE;
 4260 }
 4261 
 4262 static UINT WINMM_GetLineIDLineInfo(WINMM_MMDevice *mmdevice,
 4263         UINT mmdev_index, MIXERLINEW *info, DWORD flags)
 4264 {
 4265     if(info->dwLineID == 0xFFFF0000){
 4266         info->dwDestination = 0;
 4267         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, info, flags);
 4268     }
 4269 
 4270     if(info->dwLineID == 0){
 4271         info->dwSource = 0;
 4272         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, info, flags);
 4273     }
 4274 
 4275     TRACE("Returning INVALLINE on this dwLineID: %u\n", info->dwLineID);
 4276     return MIXERR_INVALLINE;
 4277 }
 4278 
 4279 /**************************************************************************
 4280  *              mixerGetLineInfoW       [WINMM.@]
 4281  */
 4282 UINT WINAPI mixerGetLineInfoW(HMIXEROBJ hmix, LPMIXERLINEW lpmliW, DWORD fdwInfo)
 4283 {
 4284     UINT mmdev_index;
 4285     WINMM_MMDevice *mmdevice;
 4286     HRESULT hr;
 4287 
 4288     TRACE("(%p, %p, %x)\n", hmix, lpmliW, fdwInfo);
 4289 
 4290     hr = WINMM_InitMMDevices();
 4291     if(FAILED(hr))
 4292         return MMSYSERR_NODRIVER;
 4293 
 4294     if(!lpmliW || lpmliW->cbStruct < sizeof(MIXERLINEW))
 4295         return MMSYSERR_INVALPARAM;
 4296 
 4297     TRACE("dwDestination: %u\n", lpmliW->dwDestination);
 4298     TRACE("dwSource: %u\n", lpmliW->dwSource);
 4299     TRACE("dwLineID: %u\n", lpmliW->dwLineID);
 4300     TRACE("fdwLine: 0x%x\n", lpmliW->fdwLine);
 4301     TRACE("dwComponentType: 0x%x\n", lpmliW->dwComponentType);
 4302 
 4303     if(fdwInfo & ~(MIXER_GETLINEINFOF_COMPONENTTYPE |
 4304                 MIXER_GETLINEINFOF_DESTINATION |
 4305                 MIXER_GETLINEINFOF_LINEID |
 4306                 MIXER_GETLINEINFOF_SOURCE |
 4307                 MIXER_GETLINEINFOF_TARGETTYPE |
 4308                 MIXER_OBJECTF_HMIXER |
 4309                 MIXER_OBJECTF_MIXER)){
 4310         WARN("Unknown GetLineInfo flag: %x\n", fdwInfo);
 4311         return MMSYSERR_INVALFLAG;
 4312     }
 4313 
 4314     mmdevice = WINMM_GetMixerMMDevice(hmix, fdwInfo, &mmdev_index);
 4315     if(!mmdevice)
 4316         return MMSYSERR_INVALHANDLE;
 4317 
 4318     lpmliW->dwUser = 0;
 4319 
 4320     switch(fdwInfo & MIXER_GETLINEINFOF_QUERYMASK){
 4321     case MIXER_GETLINEINFOF_DESTINATION:
 4322         return WINMM_GetDestinationLineInfo(mmdevice, mmdev_index, lpmliW,
 4323                 fdwInfo);
 4324     case MIXER_GETLINEINFOF_SOURCE:
 4325         return WINMM_GetSourceLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
 4326     case MIXER_GETLINEINFOF_COMPONENTTYPE:
 4327         return WINMM_GetComponentTypeLineInfo(mmdevice, mmdev_index, lpmliW,
 4328                 fdwInfo);
 4329     case MIXER_GETLINEINFOF_LINEID:
 4330         return WINMM_GetLineIDLineInfo(mmdevice, mmdev_index, lpmliW, fdwInfo);
 4331     case MIXER_GETLINEINFOF_TARGETTYPE:
 4332         FIXME("TARGETTYPE flag not implemented!\n");
 4333         return MIXERR_INVALLINE;
 4334     }
 4335 
 4336     TRACE("Returning INVALFLAG on these flags: %x\n", fdwInfo);
 4337     return MMSYSERR_INVALFLAG;
 4338 }
 4339 
 4340 /**************************************************************************
 4341  *              mixerGetLineInfoA       [WINMM.@]
 4342  */
 4343 UINT WINAPI mixerGetLineInfoA(HMIXEROBJ hmix, LPMIXERLINEA lpmliA,
 4344                   DWORD fdwInfo)
 4345 {
 4346     MIXERLINEW      mliW;
 4347     UINT        ret;
 4348 
 4349     TRACE("(%p, %p, %x)\n", hmix, lpmliA, fdwInfo);
 4350 
 4351     if (lpmliA == NULL || lpmliA->cbStruct != sizeof(*lpmliA))
 4352     return MMSYSERR_INVALPARAM;
 4353 
 4354     mliW.cbStruct = sizeof(mliW);
 4355     switch (fdwInfo & MIXER_GETLINEINFOF_QUERYMASK) {
 4356     case MIXER_GETLINEINFOF_COMPONENTTYPE:
 4357     mliW.dwComponentType = lpmliA->dwComponentType;
 4358     break;
 4359     case MIXER_GETLINEINFOF_DESTINATION:
 4360     mliW.dwDestination = lpmliA->dwDestination;
 4361     break;
 4362     case MIXER_GETLINEINFOF_LINEID:
 4363     mliW.dwLineID = lpmliA->dwLineID;
 4364     break;
 4365     case MIXER_GETLINEINFOF_SOURCE:
 4366     mliW.dwDestination = lpmliA->dwDestination;
 4367     mliW.dwSource = lpmliA->dwSource;
 4368     break;
 4369     case MIXER_GETLINEINFOF_TARGETTYPE:
 4370     mliW.Target.dwType = lpmliA->Target.dwType;
 4371     mliW.Target.wMid = lpmliA->Target.wMid;
 4372     mliW.Target.wPid = lpmliA->Target.wPid;
 4373     mliW.Target.vDriverVersion = lpmliA->Target.vDriverVersion;
 4374         MultiByteToWideChar(CP_ACP, 0, lpmliA->Target.szPname, -1, mliW.Target.szPname, ARRAY_SIZE(mliW.Target.szPname));
 4375     break;
 4376     default:
 4377     WARN("Unsupported fdwControls=0x%08x\n", fdwInfo);
 4378         return MMSYSERR_INVALFLAG;
 4379     }
 4380 
 4381     ret = mixerGetLineInfoW(hmix, &mliW, fdwInfo);
 4382 
 4383     if(ret == MMSYSERR_NOERROR)
 4384     {
 4385         lpmliA->dwDestination = mliW.dwDestination;
 4386         lpmliA->dwSource = mliW.dwSource;
 4387         lpmliA->dwLineID = mliW.dwLineID;
 4388         lpmliA->fdwLine = mliW.fdwLine;
 4389         lpmliA->dwUser = mliW.dwUser;
 4390         lpmliA->dwComponentType = mliW.dwComponentType;
 4391         lpmliA->cChannels = mliW.cChannels;
 4392         lpmliA->cConnections = mliW.cConnections;
 4393         lpmliA->cControls = mliW.cControls;
 4394         WideCharToMultiByte( CP_ACP, 0, mliW.szShortName, -1, lpmliA->szShortName,
 4395                              sizeof(lpmliA->szShortName), NULL, NULL);
 4396         WideCharToMultiByte( CP_ACP, 0, mliW.szName, -1, lpmliA->szName,
 4397                              sizeof(lpmliA->szName), NULL, NULL );
 4398         lpmliA->Target.dwType = mliW.Target.dwType;
 4399         lpmliA->Target.dwDeviceID = mliW.Target.dwDeviceID;
 4400         lpmliA->Target.wMid = mliW.Target.wMid;
 4401         lpmliA->Target.wPid = mliW.Target.wPid;
 4402         lpmliA->Target.vDriverVersion = mliW.Target.vDriverVersion;
 4403         WideCharToMultiByte( CP_ACP, 0, mliW.Target.szPname, -1, lpmliA->Target.szPname,
 4404                              sizeof(lpmliA->Target.szPname), NULL, NULL );
 4405     }
 4406     return ret;
 4407 }
 4408 
 4409 /**************************************************************************
 4410  *              mixerSetControlDetails  [WINMM.@]
 4411  */
 4412 UINT WINAPI mixerSetControlDetails(HMIXEROBJ hmix, LPMIXERCONTROLDETAILS lpmcd,
 4413                    DWORD fdwDetails)
 4414 {
 4415     WINMM_ControlDetails details;
 4416     UINT ret;
 4417 
 4418     TRACE("(%p, %p, %x)\n", hmix, lpmcd, fdwDetails);
 4419 
 4420     if((fdwDetails & MIXER_SETCONTROLDETAILSF_QUERYMASK) ==
 4421             MIXER_SETCONTROLDETAILSF_CUSTOM)
 4422         return MMSYSERR_NOTSUPPORTED;
 4423 
 4424     if(!lpmcd)
 4425         return MMSYSERR_INVALPARAM;
 4426 
 4427     if(!WINMM_StartDevicesThread())
 4428         return MMSYSERR_NODRIVER;
 4429 
 4430     TRACE("dwControlID: %u\n", lpmcd->dwControlID);
 4431 
 4432     details.hmix = hmix;
 4433     details.details = lpmcd;
 4434     details.flags = fdwDetails;
 4435 
 4436     ret = SendMessageW(g_devices_hwnd, MXDM_SETCONTROLDETAILS,
 4437             (DWORD_PTR)&details, 0);
 4438     InterlockedDecrement(&g_devthread_token);
 4439     return ret;
 4440 }
 4441 
 4442 /**************************************************************************
 4443  *              mixerMessage        [WINMM.@]
 4444  */
 4445 DWORD WINAPI mixerMessage(HMIXER hmix, UINT uMsg, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
 4446 {
 4447     TRACE("(%p, %d, %lx, %lx)\n", hmix, uMsg, dwParam1, dwParam2);
 4448 
 4449     return MMSYSERR_NOTSUPPORTED;
 4450 }