From e786998daff4ad49521a4c9c39c172ddcdcad82a Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Tue, 27 Sep 2011 08:51:07 -0500 Subject: [PATCH] dsound: Reimplement rendering devices on mmdevapi. --- dlls/dsound/Makefile.in | 2 +- dlls/dsound/dsound.c | 223 +++++++------- dlls/dsound/dsound_main.c | 369 +++++++++++++++++++---- dlls/dsound/dsound_private.h | 25 +- dlls/dsound/mixer.c | 140 +++++---- dlls/dsound/primary.c | 496 ++++++++++++++++++++++--------- dlls/mmdevapi/tests/dependency.c | 2 +- 7 files changed, 875 insertions(+), 382 deletions(-) diff --git a/dlls/dsound/Makefile.in b/dlls/dsound/Makefile.in index 58b1998c258..8258637b732 100644 --- a/dlls/dsound/Makefile.in +++ b/dlls/dsound/Makefile.in @@ -1,6 +1,6 @@ MODULE = dsound.dll IMPORTLIB = dsound -IMPORTS = dxguid uuid winmm ole32 advapi32 +IMPORTS = dxguid uuid winmm ole32 advapi32 user32 C_SRCS = \ buffer.c \ diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c index befa58dc029..7616bc62148 100644 --- a/dlls/dsound/dsound.c +++ b/dlls/dsound/dsound.c @@ -23,12 +23,12 @@ #include #include +#define COBJMACROS #define NONAMELESSSTRUCT #define NONAMELESSUNION #include "windef.h" #include "winbase.h" #include "winuser.h" -#include "mmsystem.h" #include "winternl.h" #include "mmddk.h" #include "wingdi.h" @@ -1248,6 +1248,10 @@ ULONG DirectSoundDevice_Release(DirectSoundDevice * device) RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE); RtlReleaseResource(&(device->buffer_list_lock)); + EnterCriticalSection(&DSOUND_renderers_lock); + list_remove(&device->entry); + LeaveCriticalSection(&DSOUND_renderers_lock); + /* It is allowed to release this object even when buffers are playing */ if (device->buffers) { WARN("%d secondary buffers not released\n", device->nrofbuffers); @@ -1264,9 +1268,14 @@ ULONG DirectSoundDevice_Release(DirectSoundDevice * device) if (hr != DS_OK) WARN("DSOUND_PrimaryDestroy failed\n"); - waveOutClose(device->hwo); - - DSOUND_renderer[device->drvdesc.dnDevNode] = NULL; + if(device->client) + IAudioClient_Release(device->client); + if(device->render) + IAudioRenderClient_Release(device->render); + if(device->clock) + IAudioClock_Release(device->clock); + if(device->volume) + IAudioStreamVolume_Release(device->volume); HeapFree(GetProcessHeap(), 0, device->tmp_buffer); HeapFree(GetProcessHeap(), 0, device->mix_buffer); @@ -1334,14 +1343,57 @@ HRESULT DirectSoundDevice_GetCaps( return DS_OK; } +static BOOL DSOUND_check_supported(IAudioClient *client, DWORD rate, + DWORD depth, WORD channels) +{ + WAVEFORMATEX fmt, *junk; + HRESULT hr; + + fmt.wFormatTag = WAVE_FORMAT_PCM; + fmt.nChannels = channels; + fmt.nSamplesPerSec = rate; + fmt.wBitsPerSample = depth; + fmt.nBlockAlign = (channels * depth) / 8; + fmt.nAvgBytesPerSec = rate * fmt.nBlockAlign; + fmt.cbSize = 0; + + hr = IAudioClient_IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, &fmt, &junk); + if(SUCCEEDED(hr)) + CoTaskMemFree(junk); + + return hr == S_OK; +} + +static UINT DSOUND_create_timer(LPTIMECALLBACK cb, DWORD_PTR user) +{ + UINT triggertime = DS_TIME_DEL, res = DS_TIME_RES, id; + TIMECAPS time; + + timeGetDevCaps(&time, sizeof(TIMECAPS)); + TRACE("Minimum timer resolution: %u, max timer: %u\n", time.wPeriodMin, time.wPeriodMax); + if (triggertime < time.wPeriodMin) + triggertime = time.wPeriodMin; + if (res < time.wPeriodMin) + res = time.wPeriodMin; + if (timeBeginPeriod(res) == TIMERR_NOCANDO) + WARN("Could not set minimum resolution, don't expect sound\n"); + id = timeSetEvent(triggertime, res, cb, user, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); + if (!id) + { + WARN("Timer not created! Retrying without TIME_KILL_SYNCHRONOUS\n"); + id = timeSetEvent(triggertime, res, cb, user, TIME_PERIODIC); + if (!id) + ERR("Could not create timer, sound playback will not occur\n"); + } + return id; +} + HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcGUID) { HRESULT hr = DS_OK; - unsigned wod, wodn; - BOOLEAN found = FALSE; GUID devGUID; - DirectSoundDevice * device = *ppDevice; - WAVEOUTCAPSA woc; + DirectSoundDevice *device; + IMMDevice *mmdevice; TRACE("(%p,%s)\n",ppDevice,debugstr_guid(lpcGUID)); @@ -1354,130 +1406,97 @@ HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcG if (!lpcGUID || IsEqualGUID(lpcGUID, &GUID_NULL)) lpcGUID = &DSDEVID_DefaultPlayback; + if(IsEqualGUID(lpcGUID, &DSDEVID_DefaultCapture) || + IsEqualGUID(lpcGUID, &DSDEVID_DefaultVoiceCapture)) + return DSERR_NODRIVER; + if (GetDeviceID(lpcGUID, &devGUID) != DS_OK) { WARN("invalid parameter: lpcGUID\n"); return DSERR_INVALIDPARAM; } - /* Enumerate WINMM audio devices and find the one we want */ - wodn = waveOutGetNumDevs(); - if (!wodn) { - WARN("no driver\n"); - return DSERR_NODRIVER; - } + hr = get_mmdevice(eRender, &devGUID, &mmdevice); + if(FAILED(hr)) + return hr; - for (wod=0; wodguid)) { - device = DSOUND_renderer[wod]; + LIST_FOR_EACH_ENTRY(device, &DSOUND_renderers, DirectSoundDevice, entry){ + if(IsEqualGUID(&device->guid, &devGUID)){ + IMMDevice_Release(mmdevice); DirectSoundDevice_AddRef(device); *ppDevice = device; + LeaveCriticalSection(&DSOUND_renderers_lock); return DS_OK; - } else { - ERR("device GUID doesn't match\n"); - hr = DSERR_GENERIC; - return hr; - } - } else { - hr = DirectSoundDevice_Create(&device); - if (hr != DS_OK) { - WARN("DirectSoundDevice_Create failed\n"); - return hr; } } - *ppDevice = device; + hr = DirectSoundDevice_Create(&device); + if(FAILED(hr)){ + WARN("DirectSoundDevice_Create failed\n"); + IMMDevice_Release(mmdevice); + LeaveCriticalSection(&DSOUND_renderers_lock); + return hr; + } + + device->mmdevice = mmdevice; device->guid = devGUID; - device->drvdesc.dnDevNode = wod; hr = DSOUND_ReopenDevice(device, FALSE); if (FAILED(hr)) { + HeapFree(GetProcessHeap(), 0, device); + LeaveCriticalSection(&DSOUND_renderers_lock); + IMMDevice_Release(mmdevice); WARN("DSOUND_ReopenDevice failed: %08x\n", hr); return hr; } - hr = mmErr(waveOutGetDevCapsA(device->drvdesc.dnDevNode, &woc, sizeof(woc))); - if (hr != DS_OK) { - WARN("waveOutGetDevCaps failed\n"); - return hr; - } ZeroMemory(&device->drvcaps, sizeof(device->drvcaps)); - if ((woc.dwFormats & WAVE_FORMAT_1M08) || - (woc.dwFormats & WAVE_FORMAT_2M08) || - (woc.dwFormats & WAVE_FORMAT_4M08) || - (woc.dwFormats & WAVE_FORMAT_48M08) || - (woc.dwFormats & WAVE_FORMAT_96M08)) { - device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT; - device->drvcaps.dwFlags |= DSCAPS_PRIMARYMONO; - } - if ((woc.dwFormats & WAVE_FORMAT_1M16) || - (woc.dwFormats & WAVE_FORMAT_2M16) || - (woc.dwFormats & WAVE_FORMAT_4M16) || - (woc.dwFormats & WAVE_FORMAT_48M16) || - (woc.dwFormats & WAVE_FORMAT_96M16)) { - device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT; - device->drvcaps.dwFlags |= DSCAPS_PRIMARYMONO; - } - if ((woc.dwFormats & WAVE_FORMAT_1S08) || - (woc.dwFormats & WAVE_FORMAT_2S08) || - (woc.dwFormats & WAVE_FORMAT_4S08) || - (woc.dwFormats & WAVE_FORMAT_48S08) || - (woc.dwFormats & WAVE_FORMAT_96S08)) { - device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT; - device->drvcaps.dwFlags |= DSCAPS_PRIMARYSTEREO; - } - if ((woc.dwFormats & WAVE_FORMAT_1S16) || - (woc.dwFormats & WAVE_FORMAT_2S16) || - (woc.dwFormats & WAVE_FORMAT_4S16) || - (woc.dwFormats & WAVE_FORMAT_48S16) || - (woc.dwFormats & WAVE_FORMAT_96S16)) { - device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT; - device->drvcaps.dwFlags |= DSCAPS_PRIMARYSTEREO; - } - if(ds_emuldriver) - device->drvcaps.dwFlags |= DSCAPS_EMULDRIVER; + if(DSOUND_check_supported(device->client, 11025, 8, 1) || + DSOUND_check_supported(device->client, 22050, 8, 1) || + DSOUND_check_supported(device->client, 44100, 8, 1) || + DSOUND_check_supported(device->client, 48000, 8, 1) || + DSOUND_check_supported(device->client, 96000, 8, 1)) + device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYMONO; + + if(DSOUND_check_supported(device->client, 11025, 16, 1) || + DSOUND_check_supported(device->client, 22050, 16, 1) || + DSOUND_check_supported(device->client, 44100, 16, 1) || + DSOUND_check_supported(device->client, 48000, 16, 1) || + DSOUND_check_supported(device->client, 96000, 16, 1)) + device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYMONO; + + if(DSOUND_check_supported(device->client, 11025, 8, 2) || + DSOUND_check_supported(device->client, 22050, 8, 2) || + DSOUND_check_supported(device->client, 44100, 8, 2) || + DSOUND_check_supported(device->client, 48000, 8, 2) || + DSOUND_check_supported(device->client, 96000, 8, 2)) + device->drvcaps.dwFlags |= DSCAPS_PRIMARY8BIT | DSCAPS_PRIMARYSTEREO; + + if(DSOUND_check_supported(device->client, 11025, 16, 2) || + DSOUND_check_supported(device->client, 22050, 16, 2) || + DSOUND_check_supported(device->client, 44100, 16, 2) || + DSOUND_check_supported(device->client, 48000, 16, 2) || + DSOUND_check_supported(device->client, 96000, 16, 2)) + device->drvcaps.dwFlags |= DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARYSTEREO; + device->drvcaps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN; device->drvcaps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX; + ZeroMemory(&device->volpan, sizeof(device->volpan)); hr = DSOUND_PrimaryCreate(device); - if (hr == DS_OK) { - UINT triggertime = DS_TIME_DEL, res = DS_TIME_RES, id; - TIMECAPS time; + if (hr == DS_OK) + device->timerID = DSOUND_create_timer(DSOUND_timer, (DWORD_PTR)device); + else + WARN("DSOUND_PrimaryCreate failed: %08x\n", hr); - DSOUND_renderer[device->drvdesc.dnDevNode] = device; - timeGetDevCaps(&time, sizeof(TIMECAPS)); - TRACE("Minimum timer resolution: %u, max timer: %u\n", time.wPeriodMin, time.wPeriodMax); - if (triggertime < time.wPeriodMin) - triggertime = time.wPeriodMin; - if (res < time.wPeriodMin) - res = time.wPeriodMin; - if (timeBeginPeriod(res) == TIMERR_NOCANDO) - WARN("Could not set minimum resolution, don't expect sound\n"); - id = timeSetEvent(triggertime, res, DSOUND_timer, (DWORD_PTR)device, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); - if (!id) - { - WARN("Timer not created! Retrying without TIME_KILL_SYNCHRONOUS\n"); - id = timeSetEvent(triggertime, res, DSOUND_timer, (DWORD_PTR)device, TIME_PERIODIC); - if (!id) ERR("Could not create timer, sound playback will not occur\n"); - } - DSOUND_renderer[device->drvdesc.dnDevNode]->timerID = id; - } else { - WARN("DSOUND_PrimaryCreate failed\n"); - } + *ppDevice = device; + list_add_tail(&DSOUND_renderers, &device->entry); + + LeaveCriticalSection(&DSOUND_renderers_lock); return hr; } diff --git a/dlls/dsound/dsound_main.c b/dlls/dsound/dsound_main.c index 927c04cefc5..681e3984b6f 100644 --- a/dlls/dsound/dsound_main.c +++ b/dlls/dsound/dsound_main.c @@ -50,17 +50,31 @@ #include "dsconf.h" #include "ks.h" #include "rpcproxy.h" +#include "rpc.h" +#include "rpcndr.h" +#include "unknwn.h" +#include "oleidl.h" +#include "shobjidl.h" + #include "initguid.h" #include "ksmedia.h" +#include "propkey.h" +#include "devpkey.h" #include "dsound_private.h" WINE_DEFAULT_DEBUG_CHANNEL(dsound); -DirectSoundDevice* DSOUND_renderer[MAXWAVEDRIVERS]; +struct list DSOUND_renderers = LIST_INIT(DSOUND_renderers); +CRITICAL_SECTION DSOUND_renderers_lock; + GUID DSOUND_renderer_guids[MAXWAVEDRIVERS]; GUID DSOUND_capture_guids[MAXWAVEDRIVERS]; +static IMMDeviceEnumerator *g_devenum; +static CRITICAL_SECTION g_devenum_lock; +static HANDLE g_devenum_thread; + HRESULT mmErr(UINT err) { switch(err) { @@ -196,6 +210,117 @@ static const char * get_device_id(LPCGUID pGuid) return debugstr_guid(pGuid); } +/* The MMDeviceEnumerator object has to be created & destroyed + * from the same thread. */ +static DWORD WINAPI devenum_thread_proc(void *arg) +{ + HANDLE evt = arg; + HRESULT hr; + MSG msg; + + hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + if(FAILED(hr)){ + ERR("CoInitializeEx failed: %08x\n", hr); + return 1; + } + + hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, + CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&g_devenum); + if(FAILED(hr)){ + ERR("CoCreateInstance failed: %08x\n", hr); + CoUninitialize(); + return 1; + } + + SetEvent(evt); + + PeekMessageW(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); + + while(GetMessageW(&msg, NULL, 0, 0)){ + if(msg.hwnd) + DispatchMessageW(&msg); + else + ERR("Unknown message: %04x\n", msg.message); + } + + IMMDeviceEnumerator_Release(g_devenum); + g_devenum = NULL; + CoUninitialize(); + + return 0; +} + +static IMMDeviceEnumerator *get_mmdevenum(void) +{ + HANDLE events[2]; + DWORD wait; + + EnterCriticalSection(&g_devenum_lock); + + if(g_devenum){ + LeaveCriticalSection(&g_devenum_lock); + return g_devenum; + } + + events[0] = CreateEventW(NULL, FALSE, FALSE, NULL); + + g_devenum_thread = CreateThread(NULL, 0, devenum_thread_proc, + events[0], 0, NULL); + if(!g_devenum_thread){ + LeaveCriticalSection(&g_devenum_lock); + CloseHandle(events[0]); + return NULL; + } + + events[1] = g_devenum_thread; + wait = WaitForMultipleObjects(2, events, FALSE, INFINITE); + CloseHandle(events[0]); + if(wait != WAIT_OBJECT_0){ + if(wait == 1 + WAIT_OBJECT_0){ + CloseHandle(g_devenum_thread); + g_devenum_thread = NULL; + } + LeaveCriticalSection(&g_devenum_lock); + return NULL; + } + + LeaveCriticalSection(&g_devenum_lock); + + return g_devenum; +} + +static HRESULT get_mmdevice_guid(IMMDevice *device, IPropertyStore *ps, + GUID *guid) +{ + PROPVARIANT pv; + HRESULT hr; + + if(!ps){ + hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps); + if(FAILED(hr)){ + WARN("OpenPropertyStore failed: %08x\n", hr); + return hr; + } + }else + IPropertyStore_AddRef(ps); + + PropVariantInit(&pv); + + hr = IPropertyStore_GetValue(ps, &PKEY_AudioEndpoint_GUID, &pv); + if(FAILED(hr)){ + IPropertyStore_Release(ps); + WARN("GetValue(GUID) failed: %08x\n", hr); + return hr; + } + + CLSIDFromString(pv.u.pwszVal, guid); + + PropVariantClear(&pv); + IPropertyStore_Release(ps); + + return S_OK; +} + /*************************************************************************** * GetDeviceID [DSOUND.9] * @@ -218,34 +343,51 @@ static const char * get_device_id(LPCGUID pGuid) */ HRESULT WINAPI GetDeviceID(LPCGUID pGuidSrc, LPGUID pGuidDest) { + IMMDeviceEnumerator *devenum; + EDataFlow flow = (EDataFlow)-1; + ERole role = (ERole)-1; + HRESULT hr; + TRACE("(%s,%p)\n", get_device_id(pGuidSrc),pGuidDest); - if ( pGuidSrc == NULL) { - WARN("invalid parameter: pGuidSrc == NULL\n"); - return DSERR_INVALIDPARAM; + if(!pGuidSrc || !pGuidDest) + return DSERR_INVALIDPARAM; + + devenum = get_mmdevenum(); + if(!devenum) + return DSERR_GENERIC; + + if(IsEqualGUID(&DSDEVID_DefaultPlayback, pGuidSrc)){ + role = eMultimedia; + flow = eRender; + }else if(IsEqualGUID(&DSDEVID_DefaultVoicePlayback, pGuidSrc)){ + role = eCommunications; + flow = eRender; + }else if(IsEqualGUID(&DSDEVID_DefaultCapture, pGuidSrc)){ + role = eMultimedia; + flow = eCapture; + }else if(IsEqualGUID(&DSDEVID_DefaultVoiceCapture, pGuidSrc)){ + role = eCommunications; + flow = eCapture; } - if ( pGuidDest == NULL ) { - WARN("invalid parameter: pGuidDest == NULL\n"); - return DSERR_INVALIDPARAM; - } + if(role != (ERole)-1 && flow != (EDataFlow)-1){ + IMMDevice *device; - if ( IsEqualGUID( &DSDEVID_DefaultPlayback, pGuidSrc ) || - IsEqualGUID( &DSDEVID_DefaultVoicePlayback, pGuidSrc ) ) { - *pGuidDest = DSOUND_renderer_guids[ds_default_playback]; - TRACE("returns %s\n", get_device_id(pGuidDest)); - return DS_OK; - } + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, + flow, role, &device); + if(FAILED(hr)){ + WARN("GetDefaultAudioEndpoint failed: %08x\n", hr); + return DSERR_NODRIVER; + } - if ( IsEqualGUID( &DSDEVID_DefaultCapture, pGuidSrc ) || - IsEqualGUID( &DSDEVID_DefaultVoiceCapture, pGuidSrc ) ) { - *pGuidDest = DSOUND_capture_guids[ds_default_capture]; - TRACE("returns %s\n", get_device_id(pGuidDest)); - return DS_OK; + hr = get_mmdevice_guid(device, NULL, pGuidDest); + IMMDevice_Release(device); + + return (hr == S_OK) ? DS_OK : hr; } *pGuidDest = *pGuidSrc; - TRACE("returns %s\n", get_device_id(pGuidDest)); return DS_OK; } @@ -297,6 +439,141 @@ HRESULT WINAPI DirectSoundEnumerateA( return DirectSoundEnumerateW(a_to_w_callback, &context); } +HRESULT get_mmdevice(EDataFlow flow, const GUID *tgt, IMMDevice **device) +{ + IMMDeviceEnumerator *devenum; + IMMDeviceCollection *coll; + UINT count, i; + HRESULT hr; + + devenum = get_mmdevenum(); + if(!devenum) + return DSERR_GENERIC; + + hr = IMMDeviceEnumerator_EnumAudioEndpoints(devenum, flow, + DEVICE_STATE_ACTIVE, &coll); + if(FAILED(hr)){ + WARN("EnumAudioEndpoints failed: %08x\n", hr); + return hr; + } + + hr = IMMDeviceCollection_GetCount(coll, &count); + if(FAILED(hr)){ + IMMDeviceCollection_Release(coll); + WARN("GetCount failed: %08x\n", hr); + return hr; + } + + for(i = 0; i < count; ++i){ + GUID guid; + + hr = IMMDeviceCollection_Item(coll, i, device); + if(FAILED(hr)) + continue; + + hr = get_mmdevice_guid(*device, NULL, &guid); + if(FAILED(hr)){ + IMMDevice_Release(*device); + continue; + } + + if(IsEqualGUID(&guid, tgt)) + return DS_OK; + + IMMDevice_Release(*device); + } + + WARN("No device with GUID %s found!\n", wine_dbgstr_guid(tgt)); + + return DSERR_INVALIDPARAM; +} + +static HRESULT enumerate_mmdevices(EDataFlow flow, GUID *guids, + LPDSENUMCALLBACKW cb, void *user) +{ + IMMDeviceEnumerator *devenum; + IMMDeviceCollection *coll; + UINT count, i; + BOOL keep_going; + HRESULT hr; + + static const WCHAR primary_desc[] = {'P','r','i','m','a','r','y',' ', + 'S','o','u','n','d',' ','D','r','i','v','e','r',0}; + static const WCHAR empty_drv[] = {0}; + static const WCHAR wine_vxd_drv[] = { 'w','i','n','e','m','m','.', + 'v','x','d', 0 }; + + devenum = get_mmdevenum(); + if(!devenum) + return DS_OK; + + hr = IMMDeviceEnumerator_EnumAudioEndpoints(g_devenum, flow, + DEVICE_STATE_ACTIVE, &coll); + if(FAILED(hr)){ + WARN("EnumAudioEndpoints failed: %08x\n", hr); + return DS_OK; + } + + hr = IMMDeviceCollection_GetCount(coll, &count); + if(FAILED(hr)){ + IMMDeviceCollection_Release(coll); + WARN("GetCount failed: %08x\n", hr); + return DS_OK; + } + + if(count == 0) + return DS_OK; + + keep_going = cb(NULL, primary_desc, empty_drv, user); + + for(i = 0; keep_going && i < count; ++i){ + IMMDevice *device; + IPropertyStore *ps; + PROPVARIANT pv; + + PropVariantInit(&pv); + + hr = IMMDeviceCollection_Item(coll, i, &device); + if(FAILED(hr)){ + WARN("Item failed: %08x\n", hr); + continue; + } + + hr = IMMDevice_OpenPropertyStore(device, STGM_READ, &ps); + if(FAILED(hr)){ + IMMDevice_Release(device); + WARN("OpenPropertyStore failed: %08x\n", hr); + continue; + } + + hr = get_mmdevice_guid(device, ps, &guids[i]); + if(FAILED(hr)){ + IPropertyStore_Release(ps); + IMMDevice_Release(device); + continue; + } + + hr = IPropertyStore_GetValue(ps, + (const PROPERTYKEY *)&DEVPKEY_Device_FriendlyName, &pv); + if(FAILED(hr)){ + IPropertyStore_Release(ps); + IMMDevice_Release(device); + WARN("GetValue(FriendlyName) failed: %08x\n", hr); + continue; + } + + keep_going = cb(&guids[i], pv.u.pwszVal, wine_vxd_drv, user); + + PropVariantClear(&pv); + IPropertyStore_Release(ps); + IMMDevice_Release(device); + } + + IMMDeviceCollection_Release(coll); + + return DS_OK; +} + /*************************************************************************** * DirectSoundEnumerateW [DSOUND.3] * @@ -314,55 +591,17 @@ HRESULT WINAPI DirectSoundEnumerateW( LPDSENUMCALLBACKW lpDSEnumCallback, LPVOID lpContext ) { - unsigned devs, wod; - GUID guid; - int err; - WAVEOUTCAPSW caps; - - const static WCHAR winmmW[] = {'w','i','n','m','m','.','d','l','l',0}; - const static WCHAR primary_driverW[] = {'P','r','i','m','a','r','y',' ', - 'S','o','u','n','d',' ','D','r','i','v','e','r',0}; - - TRACE("lpDSEnumCallback = %p, lpContext = %p\n", - lpDSEnumCallback, lpContext); + TRACE("(%p,%p)\n", lpDSEnumCallback, lpContext); if (lpDSEnumCallback == NULL) { - WARN("invalid parameter: lpDSEnumCallback == NULL\n"); - return DSERR_INVALIDPARAM; + WARN("invalid parameter: lpDSEnumCallback == NULL\n"); + return DSERR_INVALIDPARAM; } setup_dsound_options(); - devs = waveOutGetNumDevs(); - if (devs > 0) { - if (GetDeviceID(&DSDEVID_DefaultPlayback, &guid) == DS_OK) { - static const WCHAR empty[] = { 0 }; - for (wod = 0; wod < devs; ++wod) { - if (IsEqualGUID( &guid, &DSOUND_renderer_guids[wod] ) ) { - err = mmErr(waveOutGetDevCapsW(wod, &caps, sizeof(caps))); - if (err == DS_OK) { - TRACE("calling lpDSEnumCallback(NULL,\"%s\",\"%s\",%p)\n", - "Primary Sound Driver","",lpContext); - if (lpDSEnumCallback(NULL, primary_driverW, empty, lpContext) == FALSE) - return DS_OK; - } - } - } - } - } - - for (wod = 0; wod < devs; ++wod) { - err = mmErr(waveOutGetDevCapsW(wod, &caps, sizeof(caps))); - if (err == DS_OK) { - TRACE("calling lpDSEnumCallback(%s,\"%s\",\"%s\",%p)\n", - debugstr_guid(&DSOUND_renderer_guids[wod]), - wine_dbgstr_w(caps.szPname),"winmm.dll",lpContext); - - if (lpDSEnumCallback(&DSOUND_renderer_guids[wod], caps.szPname, winmmW, lpContext) == FALSE) - return DS_OK; - } - } - return DS_OK; + return enumerate_mmdevices(eRender, DSOUND_renderer_guids, + lpDSEnumCallback, lpContext); } /*************************************************************************** @@ -645,15 +884,15 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved) case DLL_PROCESS_ATTACH: TRACE("DLL_PROCESS_ATTACH\n"); for (i = 0; i < MAXWAVEDRIVERS; i++) { - DSOUND_renderer[i] = NULL; DSOUND_capture[i] = NULL; - INIT_GUID(DSOUND_renderer_guids[i], 0xbd6dd71a, 0x3deb, 0x11d1, 0xb1, 0x71, 0x00, 0xc0, 0x4f, 0xc2, 0x00, 0x00 + i); INIT_GUID(DSOUND_capture_guids[i], 0xbd6dd71b, 0x3deb, 0x11d1, 0xb1, 0x71, 0x00, 0xc0, 0x4f, 0xc2, 0x00, 0x00 + i); } instance = hInstDLL; DisableThreadLibraryCalls(hInstDLL); /* Increase refcount on dsound by 1 */ GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCWSTR)hInstDLL, &hInstDLL); + InitializeCriticalSection(&DSOUND_renderers_lock); + InitializeCriticalSection(&g_devenum_lock); break; case DLL_PROCESS_DETACH: TRACE("DLL_PROCESS_DETACH\n"); diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h index 633ef4dfc74..c25c3557ebb 100644 --- a/dlls/dsound/dsound_private.h +++ b/dlls/dsound/dsound_private.h @@ -23,6 +23,11 @@ #define DS_TIME_RES 2 /* Resolution of multimedia timer */ #define DS_TIME_DEL 10 /* Delay of multimedia timer callback, and duration of HEL fragment */ +#include "wingdi.h" +#include "mmdeviceapi.h" +#include "audioclient.h" +#include "mmsystem.h" + #include "wine/list.h" extern int ds_emuldriver DECLSPEC_HIDDEN; @@ -132,9 +137,8 @@ struct DirectSoundDevice DSDRIVERCAPS drvcaps; DWORD priolevel; PWAVEFORMATEX pwfx; - HWAVEOUT hwo; - LPWAVEHDR pwave; UINT timerID, pwplay, pwqueue, prebuf, helfrags; + UINT64 last_pos_bytes; DWORD fraglen; LPBYTE buffer; DWORD writelead, buflen, state, playpos, mixpos; @@ -156,6 +160,14 @@ struct DirectSoundDevice IDirectSound3DListenerImpl* listener; DS3DLISTENER ds3dl; BOOL ds3dl_need_recalc; + + IMMDevice *mmdevice; + IAudioClient *client; + IAudioClock *clock; + IAudioStreamVolume *volume; + IAudioRenderClient *render; + + struct list entry; }; /* reference counted buffer memory for duplicated buffer memory */ @@ -397,7 +409,6 @@ void DSOUND_MixToTemporary(const IDirectSoundBufferImpl *dsb, DWORD writepos, DW DWORD DSOUND_secpos_to_bufpos(const IDirectSoundBufferImpl *dsb, DWORD secpos, DWORD secmixpos, DWORD* overshot) DECLSPEC_HIDDEN; void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) DECLSPEC_HIDDEN; -void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) DECLSPEC_HIDDEN; /* sound3d.c */ @@ -416,12 +427,16 @@ HRESULT DSOUND_CaptureCreate8(REFIID riid, LPDIRECTSOUNDCAPTURE8 *ppDSC8) DECLSP #define DSOUND_FREQSHIFT (20) -extern DirectSoundDevice* DSOUND_renderer[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; -extern GUID DSOUND_renderer_guids[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; +extern CRITICAL_SECTION DSOUND_renderers_lock DECLSPEC_HIDDEN; +extern struct list DSOUND_renderers DECLSPEC_HIDDEN; extern DirectSoundCaptureDevice * DSOUND_capture[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; + +extern GUID DSOUND_renderer_guids[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; extern GUID DSOUND_capture_guids[MAXWAVEDRIVERS] DECLSPEC_HIDDEN; HRESULT mmErr(UINT err) DECLSPEC_HIDDEN; void setup_dsound_options(void) DECLSPEC_HIDDEN; const char * dumpCooperativeLevel(DWORD level) DECLSPEC_HIDDEN; + +HRESULT get_mmdevice(EDataFlow flow, const GUID *tgt, IMMDevice **device) DECLSPEC_HIDDEN; diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c index e574790798b..936ea3ed9c8 100644 --- a/dlls/dsound/mixer.c +++ b/dlls/dsound/mixer.c @@ -26,6 +26,7 @@ #include #include /* Insomnia - pow() function */ +#define COBJMACROS #define NONAMELESSSTRUCT #define NONAMELESSUNION #include "windef.h" @@ -742,17 +743,21 @@ static DWORD DSOUND_MixToPrimary(const DirectSoundDevice *device, DWORD writepos static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force) { - DWORD prebuf_frags, wave_writepos, wave_fragpos, i; + DWORD prebuf_frames, buf_offs_bytes, wave_fragpos; + int prebuf_frags; + BYTE *buffer; + HRESULT hr; + TRACE("(%p)\n", device); /* calculate the current wave frag position */ wave_fragpos = (device->pwplay + device->pwqueue) % device->helfrags; /* calculate the current wave write position */ - wave_writepos = wave_fragpos * device->fraglen; + buf_offs_bytes = wave_fragpos * device->fraglen; - TRACE("wave_fragpos = %i, wave_writepos = %i, pwqueue = %i, prebuf = %i\n", - wave_fragpos, wave_writepos, device->pwqueue, device->prebuf); + TRACE("wave_fragpos = %i, buf_offs_bytes = %i, pwqueue = %i, prebuf = %i\n", + wave_fragpos, buf_offs_bytes, device->pwqueue, device->prebuf); if (!force) { @@ -776,23 +781,50 @@ static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force) TRACE("prebuf_frags = %i\n", prebuf_frags); + if(!prebuf_frags) + return; + /* adjust queue */ device->pwqueue += prebuf_frags; - /* get out of CS when calling the wave system */ - LeaveCriticalSection(&(device->mixlock)); - /* **** */ + prebuf_frames = ((prebuf_frags + wave_fragpos > device->helfrags) ? + (prebuf_frags + wave_fragpos - device->helfrags) : + (prebuf_frags)) * device->fraglen / device->pwfx->nBlockAlign; - /* queue up the new buffers */ - for(i=0; ihwo, &device->pwave[wave_fragpos], sizeof(WAVEHDR)); - wave_fragpos++; - wave_fragpos %= device->helfrags; + hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer); + if(FAILED(hr)){ + WARN("GetBuffer failed: %08x\n", hr); + return; } - /* **** */ - EnterCriticalSection(&(device->mixlock)); + memcpy(buffer, device->buffer + buf_offs_bytes, + prebuf_frames * device->pwfx->nBlockAlign); + + hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0); + if(FAILED(hr)){ + WARN("ReleaseBuffer failed: %08x\n", hr); + return; + } + + /* check if anything wrapped */ + prebuf_frags = prebuf_frags + wave_fragpos - device->helfrags; + if(prebuf_frags > 0){ + prebuf_frames = prebuf_frags * device->fraglen / device->pwfx->nBlockAlign; + + hr = IAudioRenderClient_GetBuffer(device->render, prebuf_frames, &buffer); + if(FAILED(hr)){ + WARN("GetBuffer failed: %08x\n", hr); + return; + } + + memcpy(buffer, device->buffer, prebuf_frames * device->pwfx->nBlockAlign); + + hr = IAudioRenderClient_ReleaseBuffer(device->render, prebuf_frames, 0); + if(FAILED(hr)){ + WARN("ReleaseBuffer failed: %08x\n", hr); + return; + } + } TRACE("queue now = %i\n", device->pwqueue); } @@ -804,10 +836,39 @@ static void DSOUND_WaveQueue(DirectSoundDevice *device, BOOL force) */ static void DSOUND_PerformMix(DirectSoundDevice *device) { + UINT64 clock_pos, clock_freq, pos_bytes; + UINT delta_frags; + HRESULT hr; + TRACE("(%p)\n", device); /* **** */ - EnterCriticalSection(&(device->mixlock)); + EnterCriticalSection(&device->mixlock); + + hr = IAudioClock_GetFrequency(device->clock, &clock_freq); + if(FAILED(hr)){ + WARN("GetFrequency failed: %08x\n", hr); + LeaveCriticalSection(&device->mixlock); + return; + } + + hr = IAudioClock_GetPosition(device->clock, &clock_pos, NULL); + if(FAILED(hr)){ + WARN("GetCurrentPadding failed: %08x\n", hr); + LeaveCriticalSection(&device->mixlock); + return; + } + + pos_bytes = (clock_pos / (double)clock_freq) * device->pwfx->nSamplesPerSec * + device->pwfx->nBlockAlign; + + delta_frags = (pos_bytes - device->last_pos_bytes) / device->fraglen; + if(delta_frags > 0){ + device->pwplay += delta_frags; + device->pwplay %= device->helfrags; + device->pwqueue -= delta_frags; + device->last_pos_bytes = pos_bytes - (pos_bytes % device->fraglen); + } if (device->priolevel != DSSCL_WRITEPRIMARY) { BOOL recover = FALSE, all_stopped = FALSE; @@ -825,7 +886,7 @@ static void DSOUND_PerformMix(DirectSoundDevice *device) } TRACE("primary playpos=%d, writepos=%d, clrpos=%d, mixpos=%d, buflen=%d\n", - playpos,writepos,device->playpos,device->mixpos,device->buflen); + playpos,writepos,device->playpos,device->mixpos,device->buflen); assert(device->playpos < device->buflen); mixplaypos = DSOUND_bufpos_to_mixpos(device, device->playpos); @@ -910,7 +971,7 @@ static void DSOUND_PerformMix(DirectSoundDevice *device) if (prebuff_left >= device->fraglen){ /* update the wave queue */ - DSOUND_WaveQueue(device, FALSE); + DSOUND_WaveQueue(device, FALSE); /* buffers are full. start playing if applicable */ if(device->state == STATE_STARTING){ @@ -948,7 +1009,7 @@ static void DSOUND_PerformMix(DirectSoundDevice *device) } else { - DSOUND_WaveQueue(device, TRUE); + DSOUND_WaveQueue(device, TRUE); /* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */ if (device->state == STATE_STARTING) { @@ -978,13 +1039,6 @@ void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser, TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2); TRACE("entering at %d\n", start_time); - if (DSOUND_renderer[device->drvdesc.dnDevNode] != device) { - ERR("dsound died without killing us?\n"); - timeKillEvent(timerID); - timeEndPeriod(DS_TIME_RES); - return; - } - RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE); if (device->ref) @@ -995,37 +1049,3 @@ void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser, end_time = GetTickCount(); TRACE("completed processing at %d, duration = %d\n", end_time, end_time - start_time); } - -void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) -{ - DirectSoundDevice * device = (DirectSoundDevice*)dwUser; - TRACE("(%p,%x,%lx,%lx,%lx)\n",hwo,msg,dwUser,dw1,dw2); - TRACE("entering at %d, msg=%08x(%s)\n", GetTickCount(), msg, - msg==MM_WOM_DONE ? "MM_WOM_DONE" : msg==MM_WOM_CLOSE ? "MM_WOM_CLOSE" : - msg==MM_WOM_OPEN ? "MM_WOM_OPEN" : "UNKNOWN"); - - /* check if packet completed from wave driver */ - if (msg == MM_WOM_DONE) { - - /* **** */ - EnterCriticalSection(&(device->mixlock)); - - TRACE("done playing primary pos=%d\n", device->pwplay * device->fraglen); - - /* update playpos */ - device->pwplay++; - device->pwplay %= device->helfrags; - - /* sanity */ - if(device->pwqueue == 0){ - ERR("Wave queue corrupted!\n"); - } - - /* update queue */ - device->pwqueue--; - - LeaveCriticalSection(&(device->mixlock)); - /* **** */ - } - TRACE("completed\n"); -} diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c index df35abd2e88..24db07d450c 100644 --- a/dlls/dsound/primary.c +++ b/dlls/dsound/primary.c @@ -25,6 +25,7 @@ #include +#define COBJMACROS #define NONAMELESSSTRUCT #define NONAMELESSUNION #include "windef.h" @@ -83,32 +84,86 @@ static void DSOUND_RecalcPrimary(DirectSoundDevice *device) HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave) { - HRESULT hres = DS_OK; - TRACE("(%p, %d)\n", device, forcewave); + HRESULT hres; - waveOutClose(device->hwo); + TRACE("(%p, %d)\n", device, forcewave); + + if(device->client){ + IAudioClient_Release(device->client); + device->client = NULL; + } + if(device->render){ + IAudioRenderClient_Release(device->render); + device->render = NULL; + } + if(device->clock){ + IAudioClock_Release(device->clock); + device->clock = NULL; + } + if(device->volume){ + IAudioStreamVolume_Release(device->volume); + device->volume = NULL; + } device->drvdesc.dwFlags = 0; - - hres = mmErr(waveOutOpen(&(device->hwo), device->drvdesc.dnDevNode, - device->pwfx, (DWORD_PTR)DSOUND_callback, (DWORD_PTR)device, - CALLBACK_FUNCTION | WAVE_MAPPED)); - if (FAILED(hres)) { - WARN("waveOutOpen failed: %08x\n", hres); + hres = IMMDevice_Activate(device->mmdevice, &IID_IAudioClient, + CLSCTX_INPROC_SERVER, NULL, (void **)&device->client); + if(FAILED(hres)){ + WARN("Activate failed: %08x\n", hres); return hres; } - return hres; + /* buffer size = 200 * 100000 (100 ns) = 2.0 seconds */ + hres = IAudioClient_Initialize(device->client, + AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST, + 200 * 100000, 50000, device->pwfx, NULL); + if(FAILED(hres)){ + IAudioClient_Release(device->client); + device->client = NULL; + WARN("Initialize failed: %08x\n", hres); + return hres; + } + + hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient, + (void**)&device->render); + if(FAILED(hres)){ + IAudioClient_Release(device->client); + device->client = NULL; + WARN("GetService failed: %08x\n", hres); + return hres; + } + + hres = IAudioClient_GetService(device->client, &IID_IAudioClock, + (void**)&device->clock); + if(FAILED(hres)){ + IAudioClient_Release(device->client); + IAudioRenderClient_Release(device->render); + device->client = NULL; + device->render = NULL; + WARN("GetService failed: %08x\n", hres); + return hres; + } + + hres = IAudioClient_GetService(device->client, &IID_IAudioStreamVolume, + (void**)&device->volume); + if(FAILED(hres)){ + IAudioClient_Release(device->client); + IAudioRenderClient_Release(device->render); + IAudioClock_Release(device->clock); + device->client = NULL; + device->render = NULL; + device->clock = NULL; + WARN("GetService failed: %08x\n", hres); + return hres; + } + + return S_OK; } static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) { DWORD buflen; - HRESULT err = DS_OK; - LPBYTE newbuf; - LPWAVEHDR headers = NULL; - DWORD overshot; - unsigned int c; + LPBYTE newbuf; TRACE("(%p)\n", device); @@ -128,9 +183,6 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) if (device->state == STATE_PLAYING) device->state = STATE_STARTING; else if (device->state == STATE_STOPPING) device->state = STATE_STOPPED; - /* Start in pause mode, to allow buffers to get filled */ - waveOutPause(device->hwo); - TRACE("desired buflen=%d, old buffer=%p\n", buflen, device->buffer); /* reallocate emulated primary buffer */ @@ -146,70 +198,33 @@ static HRESULT DSOUND_PrimaryOpen(DirectSoundDevice *device) } DSOUND_RecalcPrimary(device); - if (device->pwave) - headers = HeapReAlloc(GetProcessHeap(),0,device->pwave, device->helfrags * sizeof(WAVEHDR)); - else - headers = HeapAlloc(GetProcessHeap(),0,device->helfrags * sizeof(WAVEHDR)); - - if (!headers) { - ERR("failed to allocate wave headers\n"); - HeapFree(GetProcessHeap(), 0, newbuf); - DSOUND_RecalcPrimary(device); - return DSERR_OUTOFMEMORY; - } device->buffer = newbuf; - device->pwave = headers; - /* prepare fragment headers */ - for (c=0; chelfrags; c++) { - device->pwave[c].lpData = (char*)device->buffer + c*device->fraglen; - device->pwave[c].dwBufferLength = device->fraglen; - device->pwave[c].dwUser = (DWORD_PTR)device; - device->pwave[c].dwFlags = 0; - device->pwave[c].dwLoops = 0; - err = mmErr(waveOutPrepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR))); - if (err != DS_OK) { - while (c--) - waveOutUnprepareHeader(device->hwo,&device->pwave[c],sizeof(WAVEHDR)); - break; - } - } - - overshot = device->buflen % device->fraglen; - /* sanity */ - if(overshot) - { - overshot -= overshot % device->pwfx->nBlockAlign; - device->pwave[device->helfrags - 1].dwBufferLength += overshot; - } - - TRACE("fraglen=%d, overshot=%d\n", device->fraglen, overshot); + TRACE("fraglen=%d\n", device->fraglen); device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1]; device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1]; FillMemory(device->buffer, device->buflen, (device->pwfx->wBitsPerSample == 8) ? 128 : 0); FillMemory(device->mix_buffer, device->mix_buffer_len, 0); - device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0; - return err; + device->last_pos_bytes = device->pwplay = device->pwqueue = device->playpos = device->mixpos = 0; + return DS_OK; } static void DSOUND_PrimaryClose(DirectSoundDevice *device) { - unsigned c; + HRESULT hr; - TRACE("(%p)\n", device); + TRACE("(%p)\n", device); - /* get out of CS when calling the wave system */ - LeaveCriticalSection(&(device->mixlock)); - /* **** */ device->pwqueue = (DWORD)-1; /* resetting queues */ - waveOutReset(device->hwo); - for (c=0; chelfrags; c++) - waveOutUnprepareHeader(device->hwo, &device->pwave[c], sizeof(WAVEHDR)); - /* **** */ - EnterCriticalSection(&(device->mixlock)); + + if(device->client){ + hr = IAudioClient_Stop(device->client); + if(FAILED(hr)) + WARN("Stop failed: %08x\n", hr); + } /* clear the queue */ device->pwqueue = 0; @@ -240,7 +255,6 @@ HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device) EnterCriticalSection(&(device->mixlock)); DSOUND_PrimaryClose(device); - HeapFree(GetProcessHeap(),0,device->pwave); HeapFree(GetProcessHeap(),0,device->pwfx); device->pwfx=NULL; @@ -252,32 +266,32 @@ HRESULT DSOUND_PrimaryDestroy(DirectSoundDevice *device) HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device) { - HRESULT err = DS_OK; - TRACE("(%p)\n", device); + HRESULT hr; - err = mmErr(waveOutRestart(device->hwo)); - if (err != DS_OK) - WARN("waveOutRestart failed\n"); + TRACE("(%p)\n", device); - return err; + hr = IAudioClient_Start(device->client); + if(FAILED(hr)){ + WARN("Start failed: %08x\n", hr); + return hr; + } + + return DS_OK; } HRESULT DSOUND_PrimaryStop(DirectSoundDevice *device) { - HRESULT err = DS_OK; - TRACE("(%p)\n", device); + HRESULT hr; - /* don't call the wave system with the lock set */ - LeaveCriticalSection(&(device->mixlock)); + TRACE("(%p)\n", device); - err = mmErr(waveOutPause(device->hwo)); + hr = IAudioClient_Stop(device->client); + if(FAILED(hr)){ + WARN("Stop failed: %08x\n", hr); + return hr; + } - EnterCriticalSection(&(device->mixlock)); - - if (err != DS_OK) - WARN("waveOutPause failed\n"); - - return err; + return DS_OK; } HRESULT DSOUND_PrimaryGetPosition(DirectSoundDevice *device, LPDWORD playpos, LPDWORD writepos) @@ -331,15 +345,15 @@ LPWAVEFORMATEX DSOUND_CopyFormat(LPCWAVEFORMATEX wfex) return pwfx; } -HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex) +HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX passed_fmt) { HRESULT err = DSERR_BUFFERLOST; int i; - DWORD nSamplesPerSec, bpp, chans; - LPWAVEFORMATEX oldpwfx; - BOOL forced = device->priolevel == DSSCL_WRITEPRIMARY; + WAVEFORMATEX *old_fmt; + WAVEFORMATEXTENSIBLE *fmtex; + BOOL forced = (device->priolevel == DSSCL_WRITEPRIMARY); - TRACE("(%p,%p)\n", device, wfex); + TRACE("(%p,%p)\n", device, passed_fmt); if (device->priolevel == DSSCL_NORMAL) { WARN("failed priority check!\n"); @@ -347,29 +361,26 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex) } /* Let's be pedantic! */ - if (wfex == NULL) { - WARN("invalid parameter: wfex==NULL!\n"); + if (passed_fmt == NULL) { + WARN("invalid parameter: passed_fmt==NULL!\n"); return DSERR_INVALIDPARAM; } TRACE("(formattag=0x%04x,chans=%d,samplerate=%d," - "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n", - wfex->wFormatTag, wfex->nChannels, wfex->nSamplesPerSec, - wfex->nAvgBytesPerSec, wfex->nBlockAlign, - wfex->wBitsPerSample, wfex->cbSize); + "bytespersec=%d,blockalign=%d,bitspersamp=%d,cbSize=%d)\n", + passed_fmt->wFormatTag, passed_fmt->nChannels, passed_fmt->nSamplesPerSec, + passed_fmt->nAvgBytesPerSec, passed_fmt->nBlockAlign, + passed_fmt->wBitsPerSample, passed_fmt->cbSize); /* **** */ RtlAcquireResourceExclusive(&(device->buffer_list_lock), TRUE); EnterCriticalSection(&(device->mixlock)); - nSamplesPerSec = device->pwfx->nSamplesPerSec; - bpp = device->pwfx->wBitsPerSample; - chans = device->pwfx->nChannels; - - oldpwfx = device->pwfx; - device->pwfx = DSOUND_CopyFormat(wfex); + old_fmt = device->pwfx; + device->pwfx = DSOUND_CopyFormat(passed_fmt); + fmtex = (WAVEFORMATEXTENSIBLE *)device->pwfx; if (device->pwfx == NULL) { - device->pwfx = oldpwfx; - oldpwfx = NULL; + device->pwfx = old_fmt; + old_fmt = NULL; err = DSERR_OUTOFMEMORY; goto done; } @@ -377,21 +388,97 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex) DSOUND_PrimaryClose(device); err = DSOUND_ReopenDevice(device, FALSE); - if (FAILED(err)) - { - WARN("DSOUND_ReopenDevice failed: %08x\n", err); - goto done; + if(SUCCEEDED(err)) + goto opened; + + /* requested format failed, so try others */ + if(device->pwfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT){ + device->pwfx->wFormatTag = WAVE_FORMAT_PCM; + device->pwfx->wBitsPerSample = 32; + device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign; + device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8); + + err = DSOUND_ReopenDevice(device, FALSE); + if(SUCCEEDED(err)) + goto opened; } + + if(device->pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE && + IsEqualGUID(&fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)){ + fmtex->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + device->pwfx->wBitsPerSample = 32; + device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign; + device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8); + + err = DSOUND_ReopenDevice(device, FALSE); + if(SUCCEEDED(err)) + goto opened; + } + + device->pwfx->wBitsPerSample = 32; + device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign; + device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8); + err = DSOUND_ReopenDevice(device, FALSE); + if(SUCCEEDED(err)) + goto opened; + + device->pwfx->wBitsPerSample = 16; + device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign; + device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8); + err = DSOUND_ReopenDevice(device, FALSE); + if(SUCCEEDED(err)) + goto opened; + + device->pwfx->wBitsPerSample = 8; + device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign; + device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8); + err = DSOUND_ReopenDevice(device, FALSE); + if(SUCCEEDED(err)) + goto opened; + + device->pwfx->nChannels = (passed_fmt->nChannels == 2) ? 1 : 2; + device->pwfx->wBitsPerSample = passed_fmt->wBitsPerSample; + device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign; + device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8); + err = DSOUND_ReopenDevice(device, FALSE); + if(SUCCEEDED(err)) + goto opened; + + device->pwfx->wBitsPerSample = 32; + device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign; + device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8); + err = DSOUND_ReopenDevice(device, FALSE); + if(SUCCEEDED(err)) + goto opened; + + device->pwfx->wBitsPerSample = 16; + device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign; + device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8); + err = DSOUND_ReopenDevice(device, FALSE); + if(SUCCEEDED(err)) + goto opened; + + device->pwfx->wBitsPerSample = 8; + device->pwfx->nAvgBytesPerSec = passed_fmt->nSamplesPerSec * device->pwfx->nBlockAlign; + device->pwfx->nBlockAlign = passed_fmt->nChannels * (device->pwfx->wBitsPerSample / 8); + err = DSOUND_ReopenDevice(device, FALSE); + if(SUCCEEDED(err)) + goto opened; + + WARN("No formats could be opened\n"); + goto done; + +opened: err = DSOUND_PrimaryOpen(device); if (err != DS_OK) { WARN("DSOUND_PrimaryOpen failed\n"); goto done; } - if (wfex->nSamplesPerSec/100 != device->pwfx->nSamplesPerSec/100 && forced && device->buffer) + if (passed_fmt->nSamplesPerSec/100 != device->pwfx->nSamplesPerSec/100 && forced && device->buffer) { DSOUND_PrimaryClose(device); - device->pwfx->nSamplesPerSec = wfex->nSamplesPerSec; + device->pwfx->nSamplesPerSec = passed_fmt->nSamplesPerSec; err = DSOUND_ReopenDevice(device, TRUE); if (FAILED(err)) WARN("DSOUND_ReopenDevice(2) failed: %08x\n", err); @@ -405,7 +492,9 @@ HRESULT primarybuffer_SetFormat(DirectSoundDevice *device, LPCWAVEFORMATEX wfex) device->mixfunction = mixfunctions[device->pwfx->wBitsPerSample/8 - 1]; device->normfunction = normfunctions[device->pwfx->wBitsPerSample/8 - 1]; - if (nSamplesPerSec != device->pwfx->nSamplesPerSec || bpp != device->pwfx->wBitsPerSample || chans != device->pwfx->nChannels) { + if (old_fmt->nSamplesPerSec != device->pwfx->nSamplesPerSec || + old_fmt->wBitsPerSample != device->pwfx->wBitsPerSample || + old_fmt->nChannels != device->pwfx->nChannels) { IDirectSoundBufferImpl** dsb = device->buffers; for (i = 0; i < device->nrofbuffers; i++, dsb++) { /* **** */ @@ -426,7 +515,7 @@ done: RtlReleaseResource(&(device->buffer_list_lock)); /* **** */ - HeapFree(GetProcessHeap(), 0, oldpwfx); + HeapFree(GetProcessHeap(), 0, old_fmt); return err; } @@ -453,10 +542,11 @@ static HRESULT WINAPI PrimaryBufferImpl_SetFormat( static HRESULT WINAPI PrimaryBufferImpl_SetVolume( LPDIRECTSOUNDBUFFER iface,LONG vol ) { - IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface); - DirectSoundDevice *device = This->device; - DWORD ampfactors; - HRESULT hres = DS_OK; + IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface); + DirectSoundDevice *device = This->device; + HRESULT hr; + float lvol, rvol; + TRACE("(%p,%d)\n", iface, vol); if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) { @@ -470,31 +560,64 @@ static HRESULT WINAPI PrimaryBufferImpl_SetVolume( } /* **** */ - EnterCriticalSection(&(device->mixlock)); + EnterCriticalSection(&device->mixlock); + + hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("GetChannelVolume failed: %08x\n", hr); + return hr; + } + + if(device->pwfx->nChannels > 1){ + hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("GetChannelVolume failed: %08x\n", hr); + return hr; + } + }else + rvol = 1; + + device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF)); + device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF)); - waveOutGetVolume(device->hwo, &factors); - device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff; - device->volpan.dwTotalRightAmpFactor=ampfactors >> 16; DSOUND_AmpFactorToVolPan(&device->volpan); if (vol != device->volpan.lVolume) { - device->volpan.lVolume=vol; - DSOUND_RecalcVolPan(&device->volpan); - ampfactors = (device->volpan.dwTotalLeftAmpFactor & 0xffff) | (device->volpan.dwTotalRightAmpFactor << 16); - waveOutSetVolume(device->hwo, ampfactors); + device->volpan.lVolume=vol; + DSOUND_RecalcVolPan(&device->volpan); + lvol = (float)((DWORD)(device->volpan.dwTotalLeftAmpFactor & 0xFFFF) / (float)0xFFFF); + hr = IAudioStreamVolume_SetChannelVolume(device->volume, 0, lvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("SetChannelVolume failed: %08x\n", hr); + return hr; + } + + if(device->pwfx->nChannels > 1){ + rvol = (float)((DWORD)(device->volpan.dwTotalRightAmpFactor & 0xFFFF) / (float)0xFFFF); + hr = IAudioStreamVolume_SetChannelVolume(device->volume, 1, rvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("SetChannelVolume failed: %08x\n", hr); + return hr; + } + } } LeaveCriticalSection(&(device->mixlock)); /* **** */ - return hres; + return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_GetVolume( LPDIRECTSOUNDBUFFER iface,LPLONG vol ) { - IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface); - DirectSoundDevice *device = This->device; - DWORD ampfactors; + IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface); + DirectSoundDevice *device = This->device; + float lvol, rvol; + HRESULT hr; TRACE("(%p,%p)\n", iface, vol); if (!(This->dsbd.dwFlags & DSBCAPS_CTRLVOLUME)) { @@ -507,12 +630,33 @@ static HRESULT WINAPI PrimaryBufferImpl_GetVolume( return DSERR_INVALIDPARAM; } - waveOutGetVolume(device->hwo, &factors); - device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff; - device->volpan.dwTotalRightAmpFactor=ampfactors >> 16; + EnterCriticalSection(&device->mixlock); + + hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("GetChannelVolume failed: %08x\n", hr); + return hr; + } + + if(device->pwfx->nChannels > 1){ + hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("GetChannelVolume failed: %08x\n", hr); + return hr; + } + }else + rvol = 1; + + device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF)); + device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF)); + DSOUND_AmpFactorToVolPan(&device->volpan); *vol = device->volpan.lVolume; + LeaveCriticalSection(&device->mixlock); + return DS_OK; } @@ -776,10 +920,10 @@ static HRESULT WINAPI PrimaryBufferImpl_SetCurrentPosition( static HRESULT WINAPI PrimaryBufferImpl_SetPan( LPDIRECTSOUNDBUFFER iface,LONG pan ) { - IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface); - DirectSoundDevice *device = This->device; - DWORD ampfactors; - HRESULT hres = DS_OK; + IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface); + DirectSoundDevice *device = This->device; + float lvol, rvol; + HRESULT hr; TRACE("(%p,%d)\n", iface, pan); if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) { @@ -793,31 +937,65 @@ static HRESULT WINAPI PrimaryBufferImpl_SetPan( } /* **** */ - EnterCriticalSection(&(device->mixlock)); + EnterCriticalSection(&device->mixlock); + + hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("GetChannelVolume failed: %08x\n", hr); + return hr; + } + + if(device->pwfx->nChannels > 1){ + hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("GetChannelVolume failed: %08x\n", hr); + return hr; + } + }else + rvol = 1; + + device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF)); + device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF)); - waveOutGetVolume(device->hwo, &factors); - device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff; - device->volpan.dwTotalRightAmpFactor=ampfactors >> 16; DSOUND_AmpFactorToVolPan(&device->volpan); if (pan != device->volpan.lPan) { device->volpan.lPan=pan; DSOUND_RecalcVolPan(&device->volpan); - ampfactors = (device->volpan.dwTotalLeftAmpFactor & 0xffff) | (device->volpan.dwTotalRightAmpFactor << 16); - waveOutSetVolume(device->hwo, ampfactors); + + lvol = (float)((DWORD)(device->volpan.dwTotalLeftAmpFactor & 0xFFFF) / (float)0xFFFF); + hr = IAudioStreamVolume_SetChannelVolume(device->volume, 0, lvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("SetChannelVolume failed: %08x\n", hr); + return hr; + } + + if(device->pwfx->nChannels > 1){ + rvol = (float)((DWORD)(device->volpan.dwTotalRightAmpFactor & 0xFFFF) / (float)0xFFFF); + hr = IAudioStreamVolume_SetChannelVolume(device->volume, 1, rvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("SetChannelVolume failed: %08x\n", hr); + return hr; + } + } } - LeaveCriticalSection(&(device->mixlock)); + LeaveCriticalSection(&device->mixlock); /* **** */ - return hres; + return DS_OK; } static HRESULT WINAPI PrimaryBufferImpl_GetPan( LPDIRECTSOUNDBUFFER iface,LPLONG pan ) { - IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface); - DirectSoundDevice *device = This->device; - DWORD ampfactors; + IDirectSoundBufferImpl *This = impl_from_IDirectSoundBuffer(iface); + DirectSoundDevice *device = This->device; + float lvol, rvol; + HRESULT hr; TRACE("(%p,%p)\n", iface, pan); if (!(This->dsbd.dwFlags & DSBCAPS_CTRLPAN)) { @@ -830,11 +1008,33 @@ static HRESULT WINAPI PrimaryBufferImpl_GetPan( return DSERR_INVALIDPARAM; } - waveOutGetVolume(device->hwo, &factors); - device->volpan.dwTotalLeftAmpFactor=ampfactors & 0xffff; - device->volpan.dwTotalRightAmpFactor=ampfactors >> 16; + EnterCriticalSection(&device->mixlock); + + hr = IAudioStreamVolume_GetChannelVolume(device->volume, 0, &lvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("GetChannelVolume failed: %08x\n", hr); + return hr; + } + + if(device->pwfx->nChannels > 1){ + hr = IAudioStreamVolume_GetChannelVolume(device->volume, 1, &rvol); + if(FAILED(hr)){ + LeaveCriticalSection(&device->mixlock); + WARN("GetChannelVolume failed: %08x\n", hr); + return hr; + } + }else + rvol = 1; + + device->volpan.dwTotalLeftAmpFactor = ((UINT16)(lvol * (DWORD)0xFFFF)); + device->volpan.dwTotalRightAmpFactor = ((UINT16)(rvol * (DWORD)0xFFFF)); + DSOUND_AmpFactorToVolPan(&device->volpan); *pan = device->volpan.lPan; + + LeaveCriticalSection(&device->mixlock); + return DS_OK; } diff --git a/dlls/mmdevapi/tests/dependency.c b/dlls/mmdevapi/tests/dependency.c index c14208e8599..59c48f82a88 100644 --- a/dlls/mmdevapi/tests/dependency.c +++ b/dlls/mmdevapi/tests/dependency.c @@ -71,7 +71,7 @@ START_TEST(dependency) ok(!GetModuleHandle("dsound.dll"), "dsound.dll was already loaded!\n"); hr = IMMDevice_Activate(dev, &IID_IDirectSound8, CLSCTX_INPROC_SERVER, NULL, (void**)&ds8); - todo_wine ok(hr == S_OK, "Activating ds8 interface failed: 0x%08x\n", hr); + ok(hr == S_OK, "Activating ds8 interface failed: 0x%08x\n", hr); if (hr == S_OK) { ok(GetModuleHandle("dsound.dll") != NULL, "dsound.dll not loaded!\n");