/************************************************************************** * wodPlayer_Reset [internal] * * wodPlayer helper. Resets current output stream. */ static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset) { wodUpdatePlayedTotal(wwo); wodPlayer_NotifyCompletions(wwo, FALSE); /* updates current notify list */ /* we aren't able to flush any data that has already been written */ /* to nas, otherwise we would do the flushing here */ nas_free(wwo); if (reset) { enum win_wm_message msg; DWORD_PTR param; HANDLE ev; /* remove any buffer */ wodPlayer_NotifyCompletions(wwo, TRUE); wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL; wwo->state = WINE_WS_STOPPED; wwo->PlayedTotal = wwo->WrittenTotal = 0; /* remove any existing message in the ring */ EnterCriticalSection(&wwo->msgRing.msg_crst); /* return all pending headers in queue */ while (NAS_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) { TRACE("flushing msg\n"); if (msg != WINE_WM_HEADER) { FIXME("shouldn't have headers left\n"); SetEvent(ev); continue; } ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE; ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE; wodNotifyClient(wwo, WOM_DONE, param, 0); } ResetEvent(wwo->msgRing.msg_event); 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, expec strange results\n"); wwo->lpPlayPtr = wwo->lpLoopPtr; wwo->WrittenTotal = wwo->PlayedTotal; /* this is wrong !!! */ } else { /* the data already written is going to be played, so take */ /* this fact into account here */ wwo->PlayedTotal = wwo->WrittenTotal; } wwo->state = WINE_WS_PAUSED; } }
/************************************************************************** * wodPlayer [internal] */ static DWORD CALLBACK wodPlayer(LPVOID pmt) { WORD uDevID = (DWORD_PTR)pmt; WINE_WAVEOUT* wwo = &WOutDev[uDevID]; wwo->state = WINE_WS_STOPPED; SetEvent(wwo->hStartUpEvent); for (;;) { if (wwo->FlowStarted) { AuHandleEvents(wwo->AuServ); if (wwo->state == WINE_WS_PLAYING && wwo->freeBytes && wwo->BufferUsed) nas_send_buffer(wwo); } if (wwo->BufferUsed <= FRAG_SIZE && wwo->writeBytes > 0) wodPlayer_NotifyCompletions(wwo, FALSE); WaitForSingleObject(wwo->msgRing.msg_event, 20); wodPlayer_ProcessMessages(wwo); while(wwo->lpPlayPtr) { wwo->lpPlayPtr->reserved = wwo->WrittenTotal + wwo->lpPlayPtr->dwBufferLength; nas_add_buffer(wwo); wodPlayer_PlayPtrNext(wwo); } return 0; } }
/************************************************************************** * wodPlayer [internal] */ static DWORD CALLBACK wodPlayer(LPVOID pmt) { WORD uDevID = (DWORD_PTR)pmt; WINE_WAVEDEV* wwo = &WOutDev[uDevID]; DWORD dwNextFeedTime = INFINITE; /* Time before DSP needs feeding */ DWORD dwNextNotifyTime = INFINITE; /* Time before next wave completion */ DWORD dwSleepTime; wwo->state = WINE_WS_STOPPED; SetEvent(wwo->hStartUpEvent); for (;;) { /** Wait for the shortest time before an action is required. If there * are no pending actions, wait forever for a command. */ dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime); TRACE("waiting %ums (%u,%u)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime); ALSA_WaitRingMessage(&wwo->msgRing, dwSleepTime); wodPlayer_ProcessMessages(wwo); if (wwo->state == WINE_WS_PLAYING) { dwNextFeedTime = wodPlayer_FeedDSP(wwo); dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE); if (dwNextFeedTime == INFINITE) { /* FeedDSP ran out of data, but before giving up, */ /* check that a notification didn't give us more */ wodPlayer_ProcessMessages(wwo); if (wwo->lpPlayPtr) { TRACE("recovering\n"); dwNextFeedTime = wodPlayer_FeedDSP(wwo); } } } else { dwNextFeedTime = dwNextNotifyTime = INFINITE; } } return 0; }
/************************************************************************** * wodPlayer_Reset [internal] * * wodPlayer helper. Resets current output stream. */ static void wodPlayer_Reset(WINE_WAVEDEV* wwo, BOOL reset) { int err; TRACE("(%p)\n", wwo); wodUpdatePlayedTotal(wwo, NULL); /* updates current notify list */ wodPlayer_NotifyCompletions(wwo, FALSE); if ( (err = snd_pcm_drop(wwo->pcm)) < 0) { FIXME("flush: %s\n", snd_strerror(err)); wwo->hThread = 0; wwo->state = WINE_WS_STOPPED; ExitThread(-1); } if ( (err = snd_pcm_prepare(wwo->pcm)) < 0 ) ERR("pcm prepare failed: %s\n", snd_strerror(err)); if (reset) { enum win_wm_message msg; DWORD_PTR 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; /* Clear partial wavehdr */ wwo->dwPartialOffset = 0; /* remove any existing message in the ring */ EnterCriticalSection(&wwo->msgRing.msg_crst); /* return all pending headers in queue */ while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) { if (msg != WINE_WM_HEADER) { FIXME("shouldn't have headers left\n"); SetEvent(ev); continue; } ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE; ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE; 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, expect strange results\n"); wwo->lpPlayPtr = wwo->lpLoopPtr; wwo->dwPartialOffset = 0; wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */ } else { LPWAVEHDR ptr; DWORD sz = wwo->dwPartialOffset; /* 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; } /* 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->state = WINE_WS_PAUSED; } }