Module: wine
Branch: master
Commit: 14d2c98834f9f50263229f7d1718def8c4aa6953
URL: http://source.winehq.org/git/wine.git/?a=commit;h=14d2c98834f9f50263229f7d1…
Author: Francois Gouget <fgouget(a)codeweavers.com>
Date: Sat Mar 28 11:48:38 2009 +0100
wineesd.drv: Improve the accuracy of wodUpdatePlayedTotal().
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().
---
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 eefc624..9fde808 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)
{
@@ -1159,10 +1182,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.
*/
Module: wine
Branch: master
Commit: 623586433a040b0fff427074277837882e3c0dde
URL: http://source.winehq.org/git/wine.git/?a=commit;h=623586433a040b0fff4270742…
Author: Francois Gouget <fgouget(a)codeweavers.com>
Date: Sat Mar 28 11:46:08 2009 +0100
wineesd.drv: Simplify wodPlayer_WriteMaxFrags() and wodPlayer_FeedDSP().
We don't know how much free space we have in the EsounD buffer, so
just write until it is full. Sleep for a fraction of the time it will
take for the buffer to drain so we can refill it in time.
---
dlls/wineesd.drv/audio.c | 122 +++++++++++++++++++---------------------------
1 files changed, 50 insertions(+), 72 deletions(-)
diff --git a/dlls/wineesd.drv/audio.c b/dlls/wineesd.drv/audio.c
index e15b2fd..eefc624 100644
--- a/dlls/wineesd.drv/audio.c
+++ b/dlls/wineesd.drv/audio.c
@@ -774,44 +774,39 @@ static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
/**************************************************************************
* wodPlayer_WriteMaxFrags [internal]
- * Writes the maximum number of bytes possible to the DSP and returns
- * the number of bytes written.
+ *
+ * Esdlib will have set the stream socket buffer size to an appropriate
+ * value, so now our job is to keep it full. So write what we can, and
+ * return 1 if more can be written and 0 otherwise.
*/
-static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
+static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo)
{
- /* Only attempt to write to free bytes */
DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
- int toWrite = min(dwLength, *bytes);
int written;
TRACE("Writing wavehdr %p.%u[%u]\n",
wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength);
- /* send the audio data to esd for playing */
- TRACE("toWrite == %d\n", toWrite);
- written = write(wwo->stream_fd, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toWrite);
-
- TRACE("written = %d\n", written);
-
- if (written <= 0)
+ written = write(wwo->stream_fd, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, dwLength);
+ if (written <= 0)
{
- *bytes = 0; /* apparently esd is actually full */
- return written; /* if we wrote nothing just return */
+ /* the esd buffer is full or some error occurred */
+ TRACE("write(%u) failed, errno=%d\n", dwLength, errno);
+ return 0;
}
-
- if (written >= dwLength)
- wodPlayer_PlayPtrNext(wwo); /* If we wrote all current wavehdr, skip to the next one */
- else
- wwo->dwPartialOffset += written; /* Remove the amount written */
-
- if (written < toWrite)
- *bytes = 0;
- else
- *bytes -= written;
+ TRACE("Wrote %d bytes out of %u\n", written, dwLength);
wwo->dwWrittenTotal += written; /* update stats on this wave device */
+ if (written == dwLength)
+ {
+ /* We're done with this wavehdr, skip to the next one */
+ wodPlayer_PlayPtrNext(wwo);
+ return 1;
+ }
- return written; /* return the number of bytes written */
+ /* Remove the amount written and wait a bit before trying to write more */
+ wwo->dwPartialOffset += written;
+ return 0;
}
@@ -1002,58 +997,32 @@ static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
*/
static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
{
- DWORD availInQ;
-
wodUpdatePlayedTotal(wwo);
- /* better way to set availInQ? */
- availInQ = ESD_BUF_SIZE;
- TRACE("availInQ = %d\n", availInQ);
-
- /* input queue empty */
- if (!wwo->lpPlayPtr) {
- TRACE("Run out of wavehdr:s... flushing\n");
- return INFINITE;
- }
-
-#if 0
- /* no more room... no need to try to feed */
- if(!availInQ)
- {
- TRACE("no more room, no need to try to feed\n");
- return wwo->dwSleepTime;
- }
-#endif
-
- /* Feed from partial wavehdr */
- if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0)
- {
- TRACE("feeding from partial wavehdr\n");
- wodPlayer_WriteMaxFrags(wwo, &availInQ);
- }
/* Feed wavehdrs until we run out of wavehdrs or DSP space */
- if (!wwo->dwPartialOffset)
+ while (wwo->lpPlayPtr)
{
- while(wwo->lpPlayPtr && availInQ)
- {
- TRACE("feeding waveheaders until we run out of space\n");
- /* note the value that dwPlayedTotal will return when this wave finishes playing */
- wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
- TRACE("reserved=(%ld) dwWrittenTotal=(%d) dwBufferLength=(%d)\n",
- wwo->lpPlayPtr->reserved,
- wwo->dwWrittenTotal,
- wwo->lpPlayPtr->dwBufferLength
- );
- wodPlayer_WriteMaxFrags(wwo, &availInQ);
- }
- }
-
- if (!wwo->lpPlayPtr) {
- TRACE("Ran out of wavehdrs\n");
- return INFINITE;
+ if (wwo->dwPartialOffset != 0)
+ TRACE("feeding from partial wavehdr\n");
+ else
+ {
+ /* Note the value that dwPlayedTotal will return when this
+ * wavehdr finishes playing, for the completion notifications.
+ */
+ wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
+ TRACE("new wavehdr: reserved=(%ld) dwWrittenTotal=(%d) dwBufferLength=(%d)\n",
+ wwo->lpPlayPtr->reserved, wwo->dwWrittenTotal,
+ wwo->lpPlayPtr->dwBufferLength);
+ }
+ if (!wodPlayer_WriteMaxFrags(wwo))
+ {
+ /* the buffer is full, wait a bit */
+ return wwo->dwSleepTime;
+ }
}
- return wwo->dwSleepTime;
+ TRACE("Ran out of wavehdrs or nothing to play\n");
+ return INFINITE;
}
@@ -1118,6 +1087,7 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
int out_bits = ESD_BITS8, out_channels = ESD_MONO, out_rate;
int out_mode = ESD_STREAM, out_func = ESD_PLAY;
esd_format_t out_format;
+ int mode;
TRACE("(%u, %p, %08X);\n", wDevID, lpDesc, dwFlags);
if (lpDesc == NULL) {
@@ -1193,7 +1163,15 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
wwo->dwPlayedTotal = 0;
wwo->dwWrittenTotal = 0;
- wwo->dwSleepTime = (1024 * 1000 * BUFFER_REFILL_THRESHOLD) / wwo->waveFormat.Format.nAvgBytesPerSec;
+ /* ESD_BUF_SIZE is the socket buffer size in samples. Set dwSleepTime
+ * to a fraction of that so it never get empty.
+ */
+ wwo->dwSleepTime = 1000 * ESD_BUF_SIZE / out_rate / 3;
+
+ /* Set the stream socket to O_NONBLOCK, so we can stop playing smoothly */
+ mode = fcntl(wwo->stream_fd, F_GETFL);
+ mode |= O_NONBLOCK;
+ fcntl(wwo->stream_fd, F_SETFL, mode);
ESD_InitRingMessage(&wwo->msgRing);