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; } }
On Wed, Mar 07, 2007 at 02:41:42AM -0500, Chris Bandy wrote:
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.
The winmm wave testcase does pause.
Jan