[9/11] wineesd.drv: Improve the accuracy of wodUpdatePlayedTotal().

Francois Gouget fgouget at codeweavers.com
Sat Mar 28 05:48:38 CDT 2009


We do so by querying the EsounD latency and estimating dwPlayedTotal based on the elapsed time since the last write.
This benefits the accuracy of the completion notifications and of wodGetPosition().
---

Unfortunately the Windows API does not seem to have a way to report the 
latency to the application. At least now the completion notifications 
should give them a better idea of when the sound data actually hits the 
speakers.

 dlls/wineesd.drv/audio.c |   47 ++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 41 insertions(+), 6 deletions(-)

diff --git a/dlls/wineesd.drv/audio.c b/dlls/wineesd.drv/audio.c
index 4a01b83..283466e 100644
--- a/dlls/wineesd.drv/audio.c
+++ b/dlls/wineesd.drv/audio.c
@@ -179,6 +179,8 @@ typedef struct {
 
     DWORD			dwPlayedTotal;		/* number of bytes actually played since opening */
     DWORD                       dwWrittenTotal;         /* number of bytes written to the audio device since opening */
+    DWORD			dwLastWrite;		/* Time of last write */
+    DWORD			dwLatency;		/* Num of milliseconds between when data is sent to the server and when it is played */
 
     /* synchronization stuff */
     HANDLE			hStartUpEvent;
@@ -672,13 +674,31 @@ static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD_PTR dwParam1,
 /**************************************************************************
  * 				wodUpdatePlayedTotal	[internal]
  *
+ * dwPlayedTotal is used for wodPlayer_NotifyCompletions() and
+ * wodGetPosition(), so a byte must only be reported as played once it has
+ * reached the speakers. So give our best estimate based on the latency
+ * reported by the esd server, and on the elapsed time since the last byte
+ * was sent to the server.
  */
-static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo)
+static void wodUpdatePlayedTotal(WINE_WAVEOUT* wwo)
 {
-    /* total played is the bytes written less the bytes to write ;-) */
-    wwo->dwPlayedTotal = wwo->dwWrittenTotal;
+    DWORD elapsed;
 
-    return TRUE;
+    if (wwo->dwPlayedTotal == wwo->dwWrittenTotal)
+        return;
+
+    /* GetTickCount() wraps every now and then, but these being all unsigned it's ok */
+    elapsed = GetTickCount() - wwo->dwLastWrite;
+    if (elapsed < wwo->dwLatency)
+    {
+        wwo->dwPlayedTotal = wwo->dwWrittenTotal - (wwo->dwLatency - elapsed) * wwo->waveFormat.Format.nAvgBytesPerSec / 1000;
+        TRACE("written=%u - elapsed=%u -> played=%u\n", wwo->dwWrittenTotal, elapsed, wwo->dwPlayedTotal);
+    }
+    else
+    {
+        wwo->dwPlayedTotal = wwo->dwWrittenTotal;
+        TRACE("elapsed=%u -> played=written=%u\n", elapsed, wwo->dwPlayedTotal);
+    }
 }
 
 /**************************************************************************
@@ -783,6 +803,7 @@ static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo)
 {
     DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
     int written;
+    DWORD now;
 
     TRACE("Writing wavehdr %p.%u[%u]\n",
           wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength);
@@ -794,8 +815,10 @@ static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo)
         TRACE("write(%u) failed, errno=%d\n", dwLength, errno);
         return 0;
     }
-    TRACE("Wrote %d bytes out of %u\n", written, dwLength);
+    now = GetTickCount();
+    TRACE("Wrote %d bytes out of %u, %ums since last\n", written, dwLength, now-wwo->dwLastWrite);
 
+    wwo->dwLastWrite = now;
     wwo->dwWrittenTotal += written; /* update stats on this wave device */
     if (written == dwLength)
     {
@@ -1154,10 +1177,22 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
 
     wwo->stream_name = get_stream_name("out", wDevID);
     wwo->stream_id = 0;
-    wwo->esd_fd = -1;
     wwo->dwPlayedTotal = 0;
     wwo->dwWrittenTotal = 0;
 
+    wwo->esd_fd = esd_open_sound(NULL);
+    if (wwo->esd_fd >= 0)
+    {
+        wwo->dwLatency = 1000 * esd_get_latency(wwo->esd_fd) * 4 / wwo->waveFormat.Format.nAvgBytesPerSec;
+    }
+    else
+    {
+        WARN("esd_open_sound() failed");
+        /* just do a rough guess at the latency and continue anyway */
+        wwo->dwLatency = 1000 * (2 * ESD_BUF_SIZE) / out_rate;
+    }
+    TRACE("dwLatency = %ums\n", wwo->dwLatency);
+
     /* ESD_BUF_SIZE is the socket buffer size in samples. Set dwSleepTime
      * to a fraction of that so it never get empty.
      */
-- 
1.6.2




More information about the wine-patches mailing list