This patch should fix the incorrect handling of WaveHdr loops when
pausing. Like the non-loop case written before, this sums the buffer
lengths between dwPlayedTotal and dwWrittenTotal to calculate the new
dwPartialOffset. Instead of blindly moving from one buffer to the next,
however, this traverses the list using BeginWaveHdr and PlayPtrNext.
This has added benefit of correctly initializing all pointers and
counters for playback.
It looks correct on paper, but I don't seem to have any applications
that even attempt to pause. Can anyone suggest an application to test
this with? All feedback welcome.
Note: this branch in the function is a 'software' pause. To test you
may need to override the check for ALSA's hardware pause at line 418
(snd_pcm_hw_params_can_pause)
-- Chris
diff --git a/dlls/winealsa.drv/waveout.c b/dlls/winealsa.drv/waveout.c
index 6a8a435..4ac50c6 100644
--- a/dlls/winealsa.drv/waveout.c
+++ b/dlls/winealsa.drv/waveout.c
@@ -319,9 +319,9 @@ static DWORD wodPlayer_NotifyCompletions(WINE_WAVEDEV* wwo, BOOL force)
*
* wodPlayer helper. Resets current output stream.
*/
-static void wodPlayer_Reset(WINE_WAVEDEV* wwo, BOOL reset)
+static void wodPlayer_Reset(WINE_WAVEDEV* wwo, BOOL reset)
{
- int err;
+ int err;
TRACE("(%p)\n", wwo);
/* flush all possible output */
@@ -329,7 +329,7 @@ static void wodPlayer_Reset(WINE_WAVEDEV* wwo, BOOL reset)
wodUpdatePlayedTotal(wwo, NULL);
/* updates current notify list */
- wodPlayer_NotifyCompletions(wwo, FALSE);
+ wodPlayer_NotifyCompletions(wwo, reset);
if ( (err = snd_pcm_drop(wwo->pcm)) < 0) {
FIXME("flush: %s\n", snd_strerror(err));
@@ -345,9 +345,6 @@ static void wodPlayer_Reset(WINE_WAVEDEV* wwo, BOOL reset)
DWORD param;
HANDLE ev;
- /* remove any buffer */
- wodPlayer_NotifyCompletions(wwo, TRUE);
-
wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
wwo->state = WINE_WS_STOPPED;
wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
@@ -368,32 +365,34 @@ static void wodPlayer_Reset(WINE_WAVEDEV* wwo, BOOL reset)
((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
- wodNotifyClient(wwo, WOM_DONE, param, 0);
+ wodNotifyClient(wwo, WOM_DONE, param, 0);
}
ALSA_ResetRingMessage(&wwo->msgRing);
LeaveCriticalSection(&wwo->msgRing.msg_crst);
} else {
- if (wwo->lpLoopPtr) {
- /* complicated case, not handled yet (could imply modifying the loop counter */
- FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
- wwo->lpPlayPtr = wwo->lpLoopPtr;
- wwo->dwPartialOffset = 0;
- wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
- } else {
- LPWAVEHDR ptr;
- DWORD sz = wwo->dwPartialOffset;
+ DWORD diff = wwo->dwWrittenTotal - wwo->dwPlayedTotal;
+ DWORD sz;
+
+ /* no pointers or counters need to change */
+ if (diff <= wwo->dwPartialOffset)
+ wwo->dwPartialOffset -= diff;
+ else {
+ wwo->lpLoopPtr = NULL;
+ wodPlayer_BeginWaveHdr(wwo, wwo->lpQueuePtr);
- /* reset all the data as if we had written only up to lpPlayedTotal bytes */
- /* compute the max size playable from lpQueuePtr */
- for (ptr = wwo->lpQueuePtr; ptr != wwo->lpPlayPtr; ptr = ptr->lpNext) {
- sz += ptr->dwBufferLength;
+ diff -= wwo->dwPartialOffset;
+ sz = wwo->lpPlayPtr->dwBufferLength;
+
+ while (sz < diff) {
+ wodPlayer_PlayPtrNext(wwo);
+ sz += wwo->lpPlayPtr->dwBufferLength;
}
- /* because the reset lpPlayPtr will be lpQueuePtr */
- if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("grin\n");
- wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal);
- wwo->dwWrittenTotal = wwo->dwPlayedTotal;
- wwo->lpPlayPtr = wwo->lpQueuePtr;
+
+ wwo->dwPartialOffset = sz - diff;
+ wodPlayer_BeginWaveHdr(wwo, wwo->lpQueuePtr);
}
+
+ wwo->dwWrittenTotal = wwo->dwPlayedTotal;
wwo->state = WINE_WS_PAUSED;
}
}