/************************************************************************** * wodPlayer_FeedDSP [internal] * Feed as much sound data as we can into the DSP and return the number of * milliseconds before it will be necessary to feed the DSP again. */ static DWORD wodPlayer_FeedDSP(WINE_WAVEDEV* wwo) { DWORD availInQ; wodUpdatePlayedTotal(wwo, NULL); availInQ = snd_pcm_avail_update(wwo->pcm); /* no more room... no need to try to feed */ if (availInQ > 0) { /* Feed from partial wavehdr */ if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) { wodPlayer_WriteMaxFrags(wwo, &availInQ); } /* Feed wavehdrs until we run out of wavehdrs or DSP space */ if (wwo->dwPartialOffset == 0 && wwo->lpPlayPtr) { do { TRACE("Setting time to elapse for %p to %u\n", wwo->lpPlayPtr, wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength); /* note the value that dwPlayedTotal will return when this wave finishes playing */ wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength; } while (wodPlayer_WriteMaxFrags(wwo, &availInQ) && wwo->lpPlayPtr && availInQ > 0); } } return wodPlayer_DSPWait(wwo); }
/************************************************************************** * 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_NotifyCompletions [internal] * * Notifies and remove from queue all wavehdrs which have been played to * the speaker (ie. they have cleared the audio device). If force is true, * we notify all wavehdrs and remove them all from the queue even if they * are unplayed or part of a loop. */ static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force) { LPWAVEHDR lpWaveHdr; /* Start from lpQueuePtr and keep notifying until: * - we hit an unwritten wavehdr * - we hit the beginning of a running loop * - we hit a wavehdr which hasn't finished playing */ wodUpdatePlayedTotal(wwo); while ((lpWaveHdr = wwo->lpQueuePtr) && (force || (lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr && lpWaveHdr->reserved <= wwo->PlayedTotal))) { wwo->lpQueuePtr = lpWaveHdr->lpNext; lpWaveHdr->dwFlags &= ~WHDR_INQUEUE; lpWaveHdr->dwFlags |= WHDR_DONE; wodNotifyClient(wwo, WOM_DONE, (DWORD_PTR)lpWaveHdr, 0); } return (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ? 1 : 1; }
/************************************************************************** * wodPlayer_ProcessMessages [internal] */ static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo) { LPWAVEHDR lpWaveHdr; enum win_wm_message msg; DWORD_PTR param; HANDLE ev; while (NAS_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) { TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param); switch (msg) { case WINE_WM_PAUSING: wodPlayer_Reset(wwo, FALSE); SetEvent(ev); break; case WINE_WM_RESTARTING: wwo->state = WINE_WS_PLAYING; SetEvent(ev); break; case WINE_WM_HEADER: lpWaveHdr = (LPWAVEHDR)param; /* insert buffer at the end of queue */ { LPWAVEHDR* wh; for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext)); *wh = lpWaveHdr; } if (!wwo->lpPlayPtr) wodPlayer_BeginWaveHdr(wwo,lpWaveHdr); if (wwo->state == WINE_WS_STOPPED) wwo->state = WINE_WS_PLAYING; break; case WINE_WM_RESETTING: wodPlayer_Reset(wwo, TRUE); SetEvent(ev); break; case WINE_WM_UPDATE: wodUpdatePlayedTotal(wwo); SetEvent(ev); break; case WINE_WM_BREAKLOOP: if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) { /* ensure exit at end of current loop */ wwo->dwLoops = 1; } SetEvent(ev); break; case WINE_WM_CLOSING: /* sanity check: this should not happen since the device must have been reset before */ if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n"); wwo->hThread = 0; wwo->state = WINE_WS_CLOSED; SetEvent(ev); ExitThread(0); /* shouldn't go here */ default: FIXME("unknown message %d\n", msg); break; } } }
/************************************************************************** * 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; } }