dsound: Reimplement rendering devices on mmdevapi.

This commit is contained in:
Andrew Eikum 2011-09-27 08:51:07 -05:00 committed by Alexandre Julliard
parent 4b8a296365
commit e786998daf
7 changed files with 875 additions and 382 deletions

View File

@ -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 \

View File

@ -23,12 +23,12 @@
#include <stdarg.h>
#include <stdio.h>
#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; wod<wodn; wod++) {
if (IsEqualGUID( &devGUID, &DSOUND_renderer_guids[wod])) {
found = TRUE;
break;
}
}
EnterCriticalSection(&DSOUND_renderers_lock);
if (found == FALSE) {
WARN("No device found matching given ID!\n");
return DSERR_NODRIVER;
}
if (DSOUND_renderer[wod]) {
if (IsEqualGUID(&devGUID, &DSOUND_renderer[wod]->guid)) {
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;
}

View File

@ -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");

View File

@ -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;

View File

@ -26,6 +26,7 @@
#include <stdarg.h>
#include <math.h> /* 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; i<prebuf_frags; i++){
TRACE("queueing wave buffer %i\n", wave_fragpos);
waveOutWrite(device->hwo, &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");
}

View File

@ -25,6 +25,7 @@
#include <stdarg.h>
#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; c<device->helfrags; 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; c<device->helfrags; 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, &ampfactors);
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, &ampfactors);
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, &ampfactors);
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, &ampfactors);
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;
}

View File

@ -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");