Maarten Lankhorst : dsound: Use a thread instead of a timer for greater precision.

Alexandre Julliard julliard at winehq.org
Thu Dec 27 13:02:29 CST 2012


Module: wine
Branch: master
Commit: f5abeb8471df40819679982b03177acd7d82b763
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=f5abeb8471df40819679982b03177acd7d82b763

Author: Maarten Lankhorst <m.b.lankhorst at gmail.com>
Date:   Wed Dec 19 10:30:04 2012 +0100

dsound: Use a thread instead of a timer for greater precision.

---

 dlls/dsound/dsound.c         |   21 +++++++++++----------
 dlls/dsound/dsound_private.h |    7 ++++---
 dlls/dsound/mixer.c          |   39 +++++++++++++++++++++++----------------
 dlls/dsound/primary.c        |   27 ++++++++++++++++++++++++---
 4 files changed, 62 insertions(+), 32 deletions(-)

diff --git a/dlls/dsound/dsound.c b/dlls/dsound/dsound.c
index ac85ba2..381d0e1 100644
--- a/dlls/dsound/dsound.c
+++ b/dlls/dsound/dsound.c
@@ -670,14 +670,13 @@ ULONG DirectSoundDevice_Release(DirectSoundDevice * device)
     TRACE("(%p) ref was %u\n", device, ref + 1);
     if (!ref) {
         int i;
-        timeKillEvent(device->timerID);
-        timeEndPeriod(DS_TIME_RES);
 
-        /* The kill event should have allowed the timer process to expire
-         * but try to grab the lock just in case. Can't hold lock because
-         * secondarybuffer_destroy also grabs the lock */
-        RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE);
-        RtlReleaseResource(&(device->buffer_list_lock));
+        SetEvent(device->sleepev);
+        if (device->thread) {
+            WaitForSingleObject(device->thread, INFINITE);
+            CloseHandle(device->thread);
+        }
+        CloseHandle(device->sleepev);
 
         EnterCriticalSection(&DSOUND_renderers_lock);
         list_remove(&device->entry);
@@ -813,6 +812,7 @@ HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcG
 
     device->mmdevice = mmdevice;
     device->guid = devGUID;
+    device->sleepev = CreateEventW(0, 0, 0, 0);
 
     hr = DSOUND_ReopenDevice(device, FALSE);
     if (FAILED(hr))
@@ -869,9 +869,10 @@ HRESULT DirectSoundDevice_Initialize(DirectSoundDevice ** ppDevice, LPCGUID lpcG
     ZeroMemory(&device->volpan, sizeof(device->volpan));
 
     hr = DSOUND_PrimaryCreate(device);
-    if (hr == DS_OK)
-        device->timerID = DSOUND_create_timer(DSOUND_timer, (DWORD_PTR)device);
-    else
+    if (hr == DS_OK) {
+        device->thread = CreateThread(0, 0, DSOUND_mixthread, device, 0, 0);
+        SetThreadPriority(device->thread, THREAD_PRIORITY_TIME_CRITICAL);
+    } else
         WARN("DSOUND_PrimaryCreate failed: %08x\n", hr);
 
     *ppDevice = device;
diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h
index 62656a5..1a3900c 100644
--- a/dlls/dsound/dsound_private.h
+++ b/dlls/dsound/dsound_private.h
@@ -68,9 +68,9 @@ struct DirectSoundDevice
 
     GUID                        guid;
     DSCAPS                      drvcaps;
-    DWORD                       priolevel;
+    DWORD                       priolevel, sleeptime;
     PWAVEFORMATEX               pwfx, primary_pwfx;
-    UINT                        timerID, playing_offs_bytes, in_mmdev_bytes, prebuf;
+    UINT                        playing_offs_bytes, in_mmdev_bytes, prebuf;
     DWORD                       fraglen;
     LPBYTE                      buffer;
     DWORD                       writelead, buflen, state, playpos, mixpos;
@@ -97,6 +97,7 @@ struct DirectSoundDevice
     IAudioStreamVolume *volume;
     IAudioRenderClient *render;
 
+    HANDLE sleepev, thread;
     struct list entry;
 };
 
@@ -226,7 +227,7 @@ void DSOUND_AmpFactorToVolPan(PDSVOLUMEPAN volpan) DECLSPEC_HIDDEN;
 void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb) DECLSPEC_HIDDEN;
 DWORD DSOUND_secpos_to_bufpos(const IDirectSoundBufferImpl *dsb, DWORD secpos, DWORD secmixpos, float *overshot) DECLSPEC_HIDDEN;
 
-void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2) DECLSPEC_HIDDEN;
+DWORD CALLBACK DSOUND_mixthread(void *ptr) DECLSPEC_HIDDEN;
 
 /* sound3d.c */
 
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index 84e029b..282808e 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -820,7 +820,7 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
 			DSOUND_PrimaryStop(device);
 		}
 
-	} else {
+	} else if (device->state != STATE_STOPPED) {
 
 		DSOUND_WaveQueue(device, TRUE);
 
@@ -843,22 +843,29 @@ static void DSOUND_PerformMix(DirectSoundDevice *device)
 	/* **** */
 }
 
-void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
-                           DWORD_PTR dw1, DWORD_PTR dw2)
+DWORD CALLBACK DSOUND_mixthread(void *p)
 {
-	DirectSoundDevice * device = (DirectSoundDevice*)dwUser;
-	DWORD start_time =  GetTickCount();
-	DWORD end_time;
-	TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2);
-	TRACE("entering at %d\n", start_time);
-
-	RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE);
-
-	if (device->ref)
-		DSOUND_PerformMix(device);
+	DirectSoundDevice *dev = p;
+	TRACE("(%p)\n", dev);
 
-	RtlReleaseResource(&(device->buffer_list_lock));
+	while (dev->ref) {
+		DWORD ret;
 
-	end_time = GetTickCount();
-	TRACE("completed processing at %d, duration = %d\n", end_time, end_time - start_time);
+		/*
+		 * Some audio drivers are retarded and won't fire after being
+		 * stopped, add a timeout to handle this.
+		 */
+		ret = WaitForSingleObject(dev->sleepev, dev->sleeptime);
+		if (ret == WAIT_FAILED)
+			WARN("wait returned error %u %08x!\n", GetLastError(), GetLastError());
+		else if (ret != WAIT_OBJECT_0)
+			WARN("wait returned %08x!\n", ret);
+		if (!dev->ref)
+			break;
+
+		RtlAcquireResourceShared(&(dev->buffer_list_lock), TRUE);
+		DSOUND_PerformMix(dev);
+		RtlReleaseResource(&(dev->buffer_list_lock));
+	}
+	return 0;
 }
diff --git a/dlls/dsound/primary.c b/dlls/dsound/primary.c
index 3ac928b..dd810fd 100644
--- a/dlls/dsound/primary.c
+++ b/dlls/dsound/primary.c
@@ -152,6 +152,8 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
     REFERENCE_TIME prebuf_rt;
     WAVEFORMATEX *wfx = NULL;
     HRESULT hres;
+    REFERENCE_TIME period;
+    DWORD period_ms;
 
     TRACE("(%p, %d)\n", device, forcewave);
 
@@ -192,14 +194,15 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
     prebuf_rt = (10000000 * (UINT64)prebuf_frames) / device->pwfx->nSamplesPerSec;
 
     hres = IAudioClient_Initialize(device->client,
-            AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST,
-            prebuf_rt, 0, device->pwfx, NULL);
+            AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_NOPERSIST |
+            AUDCLNT_STREAMFLAGS_EVENTCALLBACK, prebuf_rt, 0, device->pwfx, NULL);
     if(FAILED(hres)){
         IAudioClient_Release(device->client);
         device->client = NULL;
         WARN("Initialize failed: %08x\n", hres);
         return hres;
     }
+    IAudioClient_SetEventHandle(device->client, device->sleepev);
 
     hres = IAudioClient_GetService(device->client, &IID_IAudioRenderClient,
             (void**)&device->render);
@@ -234,6 +237,24 @@ HRESULT DSOUND_ReopenDevice(DirectSoundDevice *device, BOOL forcewave)
         return hres;
     }
 
+    /* Now kick off the timer so the event fires periodically */
+    hres = IAudioClient_Start(device->client);
+    if (FAILED(hres))
+        WARN("starting failed with %08x\n", hres);
+
+    hres = IAudioClient_GetStreamLatency(device->client, &period);
+    if (FAILED(hres)) {
+        WARN("GetStreamLatency failed with %08x\n", hres);
+        period_ms = 10;
+    } else
+        period_ms = (period + 9999) / 10000;
+    TRACE("period %u ms fraglen %u prebuf %u\n", period_ms, device->fraglen, device->prebuf);
+
+    if (period_ms < 3)
+        device->sleeptime = 5;
+    else
+        device->sleeptime = period_ms * 5 / 2;
+
     return S_OK;
 }
 
@@ -379,7 +400,7 @@ HRESULT DSOUND_PrimaryPlay(DirectSoundDevice *device)
     TRACE("(%p)\n", device);
 
     hr = IAudioClient_Start(device->client);
-    if(FAILED(hr)){
+    if(FAILED(hr) && hr != AUDCLNT_E_NOT_STOPPED){
         WARN("Start failed: %08x\n", hr);
         return hr;
     }




More information about the wine-cvs mailing list