mirror of
git://source.winehq.org/git/wine.git
synced 2024-11-05 18:01:34 +00:00
Cleaned up the wodPlayer functionality to make it easier to follow.
Changed notification to be calculated from bytes played rather than time elapsed.
This commit is contained in:
parent
1160c58c62
commit
78bae03241
1 changed files with 389 additions and 236 deletions
|
@ -43,6 +43,8 @@ DEFAULT_DEBUG_CHANNEL(wave);
|
|||
/* Allow 1% deviation for sample rates (some ES137x cards) */
|
||||
#define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
|
||||
|
||||
#define REFILL_BUFFER_WHEN 3/4 /* fill the buffer when it's 3/4 empty */
|
||||
|
||||
#ifdef HAVE_OSS
|
||||
|
||||
#define SOUND_DEV "/dev/dsp"
|
||||
|
@ -93,20 +95,31 @@ typedef struct {
|
|||
int unixdev;
|
||||
volatile int state; /* one of the WINE_WS_ manifest constants */
|
||||
DWORD dwFragmentSize; /* size of OSS buffer fragment */
|
||||
DWORD dwBufferSize; /* size of whole OSS buffer in bytes
|
||||
* used to compute dwPlayedTotal from dwWrittenTotal and
|
||||
* ioctl GETOSPACE info
|
||||
*/
|
||||
WORD uWaitForFragments; /* The number of OSS buffer fragments we would like to be free
|
||||
* before trying to write to the DSP
|
||||
*/
|
||||
DWORD dwMillisPerFragment; /* The number of milliseconds of sound in each OSS buffer
|
||||
* fragment
|
||||
*/
|
||||
WAVEOPENDESC waveDesc;
|
||||
WORD wFlags;
|
||||
PCMWAVEFORMAT format;
|
||||
LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
|
||||
LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
|
||||
|
||||
/* info on current lpPlayPtr->lpWaveHdr */
|
||||
LPSTR lpPartialData; /* Data still to write on current wavehdr */
|
||||
DWORD dwPartialBytes; /* number of bytes to write to end current wavehdr */
|
||||
|
||||
LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
|
||||
DWORD dwLoops; /* private copy of loop counter */
|
||||
|
||||
DWORD dwLastFragDone; /* time in ms, when last played fragment will be actually played */
|
||||
DWORD dwPlayedTotal; /* number of bytes played since opening */
|
||||
|
||||
/* info on current lpQueueHdr->lpWaveHdr */
|
||||
DWORD dwOffCurrHdr; /* offset in lpPlayPtr->lpData for fragments */
|
||||
DWORD dwRemain; /* number of bytes to write to end the current fragment */
|
||||
DWORD dwWrittenTotal; /* number of bytes written since opening */
|
||||
|
||||
/* synchronization stuff */
|
||||
HANDLE hThread;
|
||||
|
@ -147,6 +160,14 @@ static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
|
|||
static WINE_WAVEIN WInDev [MAX_WAVEINDRV ];
|
||||
|
||||
static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
|
||||
|
||||
/* These strings used only for tracing */
|
||||
static const char *wodPlayerCmdString[] = {
|
||||
"WINE_WM_PAUSING",
|
||||
"WINE_WM_RESTARTING",
|
||||
"WINE_WM_RESETTING",
|
||||
"WINE_WM_CLOSING",
|
||||
"WINE_WM_HEADER" };
|
||||
|
||||
/*======================================================================*
|
||||
* Low level WAVE implementation *
|
||||
|
@ -405,122 +426,153 @@ static DWORD OSS_NotifyClient(UINT wDevID, WORD wMsg, DWORD dwParam1, DWORD dwPa
|
|||
*======================================================================*/
|
||||
|
||||
/**************************************************************************
|
||||
* wodPlayer_WriteFragments [internal]
|
||||
* updatePlayedTotal [internal]
|
||||
*
|
||||
* wodPlayer helper. Writes as many fragments as it can to unixdev.
|
||||
* Returns TRUE in case of buffer underrun.
|
||||
* Calculates wwo->dwPlayed total from wwo->dwWrittenTotal and the amount
|
||||
* still remaining in the OSS buffer.
|
||||
*/
|
||||
static BOOL wodPlayer_WriteFragments(WINE_WAVEOUT* wwo)
|
||||
static DWORD updatePlayedTotal( WINE_WAVEOUT* wwo )
|
||||
{
|
||||
LPWAVEHDR lpWaveHdr;
|
||||
LPBYTE lpData;
|
||||
int count;
|
||||
audio_buf_info info;
|
||||
|
||||
for (;;) {
|
||||
if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) {
|
||||
ERR("ioctl failed (%s)\n", strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
TRACE("info={frag=%d fsize=%d ftotal=%d bytes=%d}\n", info.fragments, info.fragsize, info.fragstotal, info.bytes);
|
||||
if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) {
|
||||
ERR("ioctl failed (%s)\n", strerror(errno));
|
||||
return wwo->dwPlayedTotal;
|
||||
}
|
||||
|
||||
if (!info.fragments) /* output queue is full, wait a bit */
|
||||
return FALSE;
|
||||
wwo->dwPlayedTotal=wwo->dwWrittenTotal-( wwo->dwBufferSize - info.bytes );
|
||||
return wwo->dwPlayedTotal;
|
||||
}
|
||||
|
||||
lpWaveHdr = wwo->lpPlayPtr;
|
||||
if (!lpWaveHdr) {
|
||||
if (wwo->dwRemain > 0 && /* still data to send to complete current fragment */
|
||||
wwo->dwLastFragDone && /* first fragment has been played */
|
||||
info.fragments + 2 > info.fragstotal) { /* done with all waveOutWrite()' fragments */
|
||||
/* FIXME: should do better handling here */
|
||||
WARN("Oooch, buffer underrun !\n");
|
||||
return TRUE; /* force resetting of waveOut device */
|
||||
}
|
||||
return FALSE; /* wait a bit */
|
||||
}
|
||||
|
||||
if (wwo->dwOffCurrHdr == 0) {
|
||||
TRACE("Starting a new wavehdr %p of %ld bytes\n", lpWaveHdr, lpWaveHdr->dwBufferLength);
|
||||
if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
|
||||
if (wwo->lpLoopPtr) {
|
||||
WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
|
||||
} else {
|
||||
TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
|
||||
wwo->lpLoopPtr = lpWaveHdr;
|
||||
/* Windows does not touch WAVEHDR.dwLoops,
|
||||
* so we need to make an internal copy */
|
||||
wwo->dwLoops = lpWaveHdr->dwLoops;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lpData = lpWaveHdr->lpData;
|
||||
|
||||
/* finish current wave hdr ? */
|
||||
if (wwo->dwOffCurrHdr + wwo->dwRemain >= lpWaveHdr->dwBufferLength) {
|
||||
DWORD toWrite = lpWaveHdr->dwBufferLength - wwo->dwOffCurrHdr;
|
||||
|
||||
/* write end of current wave hdr */
|
||||
count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, toWrite);
|
||||
TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);
|
||||
|
||||
if (count > 0 || toWrite == 0) {
|
||||
DWORD tc = GetTickCount();
|
||||
/**************************************************************************
|
||||
* wodPlayer_BeginWaveHdr [internal]
|
||||
*
|
||||
* Makes the specified lpWaveHdr the currently playing wave header.
|
||||
* If the specified wave header is a begin loop and we're not already in
|
||||
* a loop, setup the loop.
|
||||
*/
|
||||
static void wodPlayer_BeginWaveHdr( WINE_WAVEOUT* wwo,
|
||||
LPWAVEHDR lpWaveHdr )
|
||||
{
|
||||
wwo->lpPlayPtr=lpWaveHdr;
|
||||
|
||||
if (wwo->dwLastFragDone /* + guard time ?? */ < tc)
|
||||
wwo->dwLastFragDone = tc;
|
||||
wwo->dwLastFragDone += (toWrite * 1000) / wwo->format.wf.nAvgBytesPerSec;
|
||||
if( !lpWaveHdr ) return;
|
||||
|
||||
lpWaveHdr->reserved = wwo->dwLastFragDone;
|
||||
TRACE("Tagging hdr %p with %08lx\n", lpWaveHdr, wwo->dwLastFragDone);
|
||||
|
||||
/* WAVEHDR written, go to next one */
|
||||
if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
|
||||
if (--wwo->dwLoops > 0) {
|
||||
wwo->lpPlayPtr = wwo->lpLoopPtr;
|
||||
} else {
|
||||
/* last one played */
|
||||
if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
|
||||
FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
|
||||
/* shall we consider the END flag for the closing loop or for
|
||||
* the opening one or for both ???
|
||||
* code assumes for closing loop only
|
||||
*/
|
||||
wwo->lpLoopPtr = lpWaveHdr;
|
||||
} else {
|
||||
wwo->lpLoopPtr = NULL;
|
||||
}
|
||||
wwo->lpPlayPtr = lpWaveHdr->lpNext;
|
||||
}
|
||||
} else {
|
||||
wwo->lpPlayPtr = lpWaveHdr->lpNext;
|
||||
}
|
||||
wwo->dwOffCurrHdr = 0;
|
||||
if ((wwo->dwRemain -= count) == 0) {
|
||||
wwo->dwRemain = wwo->dwFragmentSize;
|
||||
}
|
||||
}
|
||||
continue; /* try to go to use next wavehdr */
|
||||
} else {
|
||||
count = write(wwo->unixdev, lpData + wwo->dwOffCurrHdr, wwo->dwRemain);
|
||||
TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, wwo->dwRemain, count);
|
||||
if (count > 0) {
|
||||
DWORD tc = GetTickCount();
|
||||
|
||||
if (wwo->dwLastFragDone /* + guard time ?? */ < tc)
|
||||
wwo->dwLastFragDone = tc;
|
||||
wwo->dwLastFragDone += (wwo->dwRemain * 1000) / wwo->format.wf.nAvgBytesPerSec;
|
||||
|
||||
TRACE("Tagging frag with %08lx\n", wwo->dwLastFragDone);
|
||||
|
||||
wwo->dwOffCurrHdr += count;
|
||||
wwo->dwRemain = wwo->dwFragmentSize;
|
||||
}
|
||||
if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
|
||||
if (wwo->lpLoopPtr) {
|
||||
WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
|
||||
} else {
|
||||
TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
|
||||
wwo->lpLoopPtr = lpWaveHdr;
|
||||
/* Windows does not touch WAVEHDR.dwLoops,
|
||||
* so we need to make an internal copy */
|
||||
wwo->dwLoops = lpWaveHdr->dwLoops;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* wodPlayer_PlayPtrNext [internal]
|
||||
*
|
||||
* Advance the play pointer to the next waveheader, looping if required.
|
||||
*/
|
||||
static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
|
||||
{
|
||||
LPWAVEHDR lpWaveHdr=wwo->lpPlayPtr;
|
||||
|
||||
if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
|
||||
/* We're at the end of a loop, loop if required */
|
||||
if (--wwo->dwLoops > 0) {
|
||||
wwo->lpPlayPtr = wwo->lpLoopPtr;
|
||||
} else {
|
||||
/* Handle overlapping loops correctly */
|
||||
if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
|
||||
FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
|
||||
/* shall we consider the END flag for the closing loop or for
|
||||
* the opening one or for both ???
|
||||
* code assumes for closing loop only
|
||||
*/
|
||||
wwo->lpLoopPtr = NULL;
|
||||
wodPlayer_BeginWaveHdr( wwo, lpWaveHdr );
|
||||
} else {
|
||||
wwo->lpLoopPtr = NULL;
|
||||
wodPlayer_BeginWaveHdr( wwo, lpWaveHdr=lpWaveHdr->lpNext );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* We're not in a loop. Advance to the next wave header */
|
||||
wodPlayer_BeginWaveHdr( wwo, lpWaveHdr=lpWaveHdr->lpNext );
|
||||
}
|
||||
return lpWaveHdr;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* wodPlayer_DSPWait [internal]
|
||||
* Returns the number of milliseconds to wait for the DSP buffer to clear.
|
||||
* This is based on the number of fragments we want to be clear before
|
||||
* writing and the number of free fragments we already have.
|
||||
*/
|
||||
static DWORD wodPlayer_DSPWait( WINE_WAVEOUT *wwo, WORD uFreeFragments )
|
||||
{
|
||||
return (uFreeFragments>wwo->uWaitForFragments)?1:
|
||||
wwo->dwMillisPerFragment*
|
||||
(wwo->uWaitForFragments-uFreeFragments);
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* wodPlayer_NotifyWait [internal]
|
||||
* Returns the number of milliseconds to wait before attempting to notify
|
||||
* completion of the specified wavehdr.
|
||||
* This is based on the number of bytes remaining to be written in the
|
||||
* wave.
|
||||
*/
|
||||
static DWORD wodPlayer_NotifyWait( WINE_WAVEOUT *wwo, LPWAVEHDR lpWaveHdr )
|
||||
{
|
||||
DWORD dwMillis;
|
||||
|
||||
if( lpWaveHdr->reserved<wwo->dwPlayedTotal ) {
|
||||
dwMillis=1;
|
||||
} else {
|
||||
dwMillis=(lpWaveHdr->reserved-wwo->dwPlayedTotal) *
|
||||
wwo->dwMillisPerFragment / wwo->dwFragmentSize;
|
||||
TRACE( "wait for %lu bytes = %lu\n",
|
||||
(lpWaveHdr->reserved-wwo->dwPlayedTotal),
|
||||
dwMillis );
|
||||
if( dwMillis < 1 )
|
||||
dwMillis=1;
|
||||
}
|
||||
|
||||
return dwMillis;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* wodPlayer_WriteMaxFrags [internal]
|
||||
* Writes the maximum number of bytes possible to the DSP and returns
|
||||
* the number of bytes written. Also updates fragments in dspspace.
|
||||
*/
|
||||
static DWORD wodPlayer_WriteMaxFrags( WINE_WAVEOUT *wwo, LPSTR lpData,
|
||||
DWORD dwLength,
|
||||
audio_buf_info* dspspace )
|
||||
{
|
||||
/* Only attempt to write to free fragments */
|
||||
int maxWrite=dspspace->fragments*dspspace->fragsize;
|
||||
int toWrite=min(dwLength,maxWrite);
|
||||
|
||||
int written=write(wwo->unixdev, lpData, toWrite);
|
||||
|
||||
TRACE("wrote %d of %lu bytes\n",
|
||||
written, dwLength );
|
||||
if( written > 0 ) {
|
||||
/* Keep a count of the total bytes written to the DSP */
|
||||
wwo->dwWrittenTotal+=written;
|
||||
/* reduce the number of free fragments */
|
||||
dspspace->fragments -= (written/dspspace->fragsize)+(written%dspspace->fragsize>0);
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
|
||||
int wodPlayer_Message(WINE_WAVEOUT *wwo, int msg, DWORD param)
|
||||
{
|
||||
|
@ -565,24 +617,29 @@ int wodPlayer_RetrieveMessage(WINE_WAVEOUT *wwo, int *msg, DWORD *param)
|
|||
}
|
||||
|
||||
/**************************************************************************
|
||||
* wodPlayer_Notify [internal]
|
||||
* wodPlayer_NotifyCompletions [internal]
|
||||
*
|
||||
* wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content
|
||||
* have been played (actually to speaker, not to unixdev fd).
|
||||
* Notifies and remove from queue all wavehdrs which have been played to
|
||||
* the speaker (ie. they have cleared the OSS buffer). 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 void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
|
||||
static DWORD wodPlayer_NotifyCompletions( WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
|
||||
{
|
||||
LPWAVEHDR lpWaveHdr;
|
||||
DWORD tc = GetTickCount();
|
||||
|
||||
while (wwo->lpQueuePtr &&
|
||||
updatePlayedTotal(wwo);
|
||||
/* 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
|
||||
*/
|
||||
while( (lpWaveHdr=wwo->lpQueuePtr) &&
|
||||
(force ||
|
||||
(wwo->lpQueuePtr != wwo->lpPlayPtr && wwo->lpQueuePtr != wwo->lpLoopPtr))) {
|
||||
lpWaveHdr = wwo->lpQueuePtr;
|
||||
|
||||
if (lpWaveHdr->reserved > tc && !force) break;
|
||||
(lpWaveHdr != wwo->lpPlayPtr &&
|
||||
lpWaveHdr != wwo->lpLoopPtr &&
|
||||
lpWaveHdr->reserved <= wwo->dwPlayedTotal ))) {
|
||||
|
||||
wwo->dwPlayedTotal += lpWaveHdr->dwBufferLength;
|
||||
wwo->lpQueuePtr = lpWaveHdr->lpNext;
|
||||
|
||||
lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
|
||||
|
@ -593,6 +650,11 @@ static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
|
|||
WARN("can't notify client !\n");
|
||||
}
|
||||
}
|
||||
return ( lpWaveHdr &&
|
||||
lpWaveHdr != wwo->lpPlayPtr &&
|
||||
lpWaveHdr != wwo->lpLoopPtr ) ?
|
||||
wodPlayer_NotifyWait( wwo, lpWaveHdr ) :
|
||||
0 ;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
|
@ -603,7 +665,7 @@ static void wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force)
|
|||
static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
|
||||
{
|
||||
/* updates current notify list */
|
||||
wodPlayer_Notify(wwo, uDevID, FALSE);
|
||||
wodPlayer_NotifyCompletions(wwo, uDevID, FALSE);
|
||||
|
||||
/* flush all possible output */
|
||||
if (ioctl(wwo->unixdev, SNDCTL_DSP_RESET, 0) == -1) {
|
||||
|
@ -613,15 +675,18 @@ static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
|
|||
ExitThread(-1);
|
||||
}
|
||||
|
||||
wwo->dwOffCurrHdr = 0;
|
||||
wwo->dwRemain = wwo->dwFragmentSize;
|
||||
/* Clear partial wavehdr */
|
||||
wwo->dwPartialBytes=0;
|
||||
wwo->lpPartialData=NULL;
|
||||
|
||||
if (reset) {
|
||||
/* empty notify list */
|
||||
wodPlayer_Notify(wwo, uDevID, TRUE);
|
||||
wodPlayer_NotifyCompletions(wwo, uDevID, TRUE);
|
||||
|
||||
wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
|
||||
wwo->state = WINE_WS_STOPPED;
|
||||
wwo->dwPlayedTotal = 0;
|
||||
wwo->dwWrittenTotal = 0;
|
||||
} else {
|
||||
/* FIXME: this is not accurate when looping, but can be do better ? */
|
||||
wwo->lpPlayPtr = (wwo->lpLoopPtr) ? wwo->lpLoopPtr : wwo->lpQueuePtr;
|
||||
|
@ -629,119 +694,199 @@ static void wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset)
|
|||
}
|
||||
}
|
||||
|
||||
#define MIN_SLEEP_TIME 100 /* millis. NB: <100 millis appears instant to a
|
||||
* human
|
||||
*/
|
||||
/**************************************************************************
|
||||
* wodPlayer_AwaitEvent [internal]
|
||||
* Wait for a command to be sent to the wodPlayer or for time to pass
|
||||
* until wodPlayer needs to do something again.
|
||||
*/
|
||||
static void wodPlayer_AwaitEvent( WINE_WAVEOUT* wwo,
|
||||
DWORD dwNextFeedTime,
|
||||
DWORD dwNextNotifyTime )
|
||||
{
|
||||
DWORD dwSleepTime;
|
||||
|
||||
/** Wait for the shortest time before an action is required. If there
|
||||
* are no pending actions, wait forever for a command.
|
||||
*/
|
||||
if( dwNextFeedTime == 0 ) {
|
||||
if( dwNextNotifyTime == 0 )
|
||||
dwSleepTime=INFINITE;
|
||||
else
|
||||
dwSleepTime=dwNextNotifyTime;
|
||||
} else {
|
||||
if( dwNextNotifyTime == 0 )
|
||||
dwSleepTime=dwNextFeedTime;
|
||||
else
|
||||
dwSleepTime=min( dwNextFeedTime, dwNextNotifyTime );
|
||||
}
|
||||
if( dwSleepTime != INFINITE && dwSleepTime < MIN_SLEEP_TIME )
|
||||
dwSleepTime=MIN_SLEEP_TIME;
|
||||
TRACE( "waiting %lu millis (%lu,%lu)\n", dwSleepTime,
|
||||
dwNextFeedTime,dwNextNotifyTime );
|
||||
WaitForSingleObject(wwo->msg_event, dwSleepTime);
|
||||
TRACE( "wait returned\n");
|
||||
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* wodPlayer_ProcessMessages [internal]
|
||||
*/
|
||||
static void wodPlayer_ProcessMessages( WINE_WAVEOUT* wwo, WORD uDevID )
|
||||
{
|
||||
LPWAVEHDR lpWaveHdr;
|
||||
int msg;
|
||||
DWORD param;
|
||||
|
||||
while (wodPlayer_RetrieveMessage(wwo, &msg, ¶m)) {
|
||||
TRACE( "Received %s %lx\n",
|
||||
wodPlayerCmdString[msg-WM_USER-1], param );
|
||||
switch (msg) {
|
||||
case WINE_WM_PAUSING:
|
||||
wodPlayer_Reset(wwo, uDevID, FALSE);
|
||||
wwo->state = WINE_WS_PAUSED;
|
||||
SetEvent(wwo->hEvent);
|
||||
break;
|
||||
case WINE_WM_RESTARTING:
|
||||
wwo->state = WINE_WS_PLAYING;
|
||||
SetEvent(wwo->hEvent);
|
||||
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) wwo->lpPlayPtr = lpWaveHdr;
|
||||
if (wwo->state == WINE_WS_STOPPED)
|
||||
wwo->state = WINE_WS_PLAYING;
|
||||
break;
|
||||
case WINE_WM_RESETTING:
|
||||
wodPlayer_Reset(wwo, uDevID, TRUE);
|
||||
SetEvent(wwo->hEvent);
|
||||
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(wwo->hEvent);
|
||||
ExitThread(0);
|
||||
/* shouldn't go here */
|
||||
default:
|
||||
FIXME("unknown message %d\n", msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
* 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_WAVEOUT* wwo )
|
||||
{
|
||||
LPWAVEHDR lpWaveHdr=wwo->lpPlayPtr;
|
||||
DWORD written=0;
|
||||
DWORD bytesToWrite;
|
||||
audio_buf_info dspspace;
|
||||
|
||||
/* Read output space info so we know how much writing to do */
|
||||
if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &dspspace) < 0) {
|
||||
ERR("IOCTL can't 'SNDCTL_DSP_GETOSPACE' !\n");
|
||||
}
|
||||
|
||||
TRACE( "fragments=%d, fragsize=%d, fragstotal=%d, bytes=%d\n",
|
||||
dspspace.fragments, dspspace.fragsize,
|
||||
dspspace.fragstotal, dspspace.bytes );
|
||||
|
||||
/* Do nothing if the DSP isn't hungry */
|
||||
if( dspspace.fragments==0 ) {
|
||||
return wodPlayer_DSPWait(wwo,dspspace.fragments);
|
||||
}
|
||||
|
||||
/* If there's a partially written wavehdr, feed more of it
|
||||
* -------------------------------------------------------
|
||||
*/
|
||||
bytesToWrite=wwo->dwPartialBytes;
|
||||
if( bytesToWrite > 0 ) {
|
||||
TRACE("partial write %d bytes at %p\n",
|
||||
wwo->dwPartialBytes,
|
||||
wwo->lpPartialData );
|
||||
written=wodPlayer_WriteMaxFrags( wwo, wwo->lpPartialData,
|
||||
bytesToWrite,
|
||||
&dspspace );
|
||||
if( written >= bytesToWrite ) {
|
||||
/* If we wrote it all, skip to the next header */
|
||||
wwo->dwPartialBytes=0;
|
||||
wwo->lpPartialData=NULL;
|
||||
lpWaveHdr=wodPlayer_PlayPtrNext( wwo );
|
||||
} else {
|
||||
/* Remove the amount written */
|
||||
wwo->lpPartialData+=written;
|
||||
wwo->dwPartialBytes-=written;
|
||||
}
|
||||
}
|
||||
|
||||
/* Feed wavehdrs until we run out of wavehdrs or DSP space
|
||||
* -------------------------------------------------------
|
||||
*/
|
||||
while( lpWaveHdr && dspspace.fragments > 0 ) {
|
||||
TRACE( "Writing wavehdr %p %lu bytes\n", lpWaveHdr,
|
||||
lpWaveHdr->dwBufferLength );
|
||||
|
||||
/* note the value that dwPlayedTotal will be when this
|
||||
* wave finishes playing
|
||||
*/
|
||||
lpWaveHdr->reserved=wwo->dwWrittenTotal+lpWaveHdr->dwBufferLength;
|
||||
|
||||
written=wodPlayer_WriteMaxFrags( wwo, lpWaveHdr->lpData,
|
||||
lpWaveHdr->dwBufferLength,
|
||||
&dspspace );
|
||||
/* If it's all written, on to the next one, else remember the
|
||||
* partial write for next FeedDSP call.
|
||||
*/
|
||||
if( written >= lpWaveHdr->dwBufferLength ) {
|
||||
lpWaveHdr=wodPlayer_PlayPtrNext( wwo );
|
||||
} else {
|
||||
wwo->dwPartialBytes=lpWaveHdr->dwBufferLength-written;
|
||||
wwo->lpPartialData=lpWaveHdr->lpData+written;
|
||||
}
|
||||
}
|
||||
|
||||
return lpWaveHdr ? wodPlayer_DSPWait(wwo,dspspace.fragments) : 0 ;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************
|
||||
* wodPlayer [internal]
|
||||
*/
|
||||
static DWORD CALLBACK wodPlayer(LPVOID pmt)
|
||||
{
|
||||
WORD uDevID = (DWORD)pmt;
|
||||
WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
|
||||
WAVEHDR* lpWaveHdr;
|
||||
DWORD dwSleepTime;
|
||||
int msg;
|
||||
DWORD param;
|
||||
DWORD tc;
|
||||
BOOL had_msg;
|
||||
WORD uDevID = (DWORD)pmt;
|
||||
WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
|
||||
DWORD dwNextFeedTime=0; /* Time before DSP needs feeding */
|
||||
DWORD dwNextNotifyTime=0; /* Time before next wave completion */
|
||||
|
||||
wwo->state = WINE_WS_STOPPED;
|
||||
wwo->state=WINE_WS_STOPPED;
|
||||
SetEvent( wwo->hEvent );
|
||||
|
||||
wwo->dwLastFragDone = 0;
|
||||
wwo->dwOffCurrHdr = 0;
|
||||
wwo->dwRemain = wwo->dwFragmentSize;
|
||||
wwo->lpQueuePtr = wwo->lpPlayPtr = wwo->lpLoopPtr = NULL;
|
||||
wwo->dwPlayedTotal = 0;
|
||||
|
||||
TRACE("imhere[0]\n");
|
||||
SetEvent(wwo->hEvent);
|
||||
|
||||
for (;;) {
|
||||
/* wait for dwSleepTime or an event in thread's queue
|
||||
* FIXME:
|
||||
* - is wait time calculation optimal ?
|
||||
* - these 100 ms parts should be changed, but Eric reports
|
||||
* that the wodPlayer thread might lock up if we use INFINITE
|
||||
* (strange !), so I better don't change that now... */
|
||||
if (wwo->state != WINE_WS_PLAYING)
|
||||
dwSleepTime = 100;
|
||||
else
|
||||
{
|
||||
tc = GetTickCount();
|
||||
if (tc < wwo->dwLastFragDone)
|
||||
{
|
||||
/* calculate sleep time depending on when the last fragment
|
||||
will be played */
|
||||
dwSleepTime = (wwo->dwLastFragDone - tc)*7/10;
|
||||
if (dwSleepTime > 100)
|
||||
dwSleepTime = 100;
|
||||
}
|
||||
else
|
||||
dwSleepTime = 0;
|
||||
}
|
||||
|
||||
TRACE("imhere[1] tc = %08lx\n", GetTickCount());
|
||||
if (dwSleepTime)
|
||||
WaitForSingleObject(wwo->msg_event, dwSleepTime);
|
||||
TRACE("imhere[2] (q=%p p=%p) tc = %08lx\n", wwo->lpQueuePtr,
|
||||
wwo->lpPlayPtr, GetTickCount());
|
||||
had_msg = FALSE;
|
||||
while (wodPlayer_RetrieveMessage(wwo, &msg, ¶m)) {
|
||||
had_msg = TRUE;
|
||||
switch (msg) {
|
||||
case WINE_WM_PAUSING:
|
||||
wodPlayer_Reset(wwo, uDevID, FALSE);
|
||||
wwo->state = WINE_WS_PAUSED;
|
||||
SetEvent(wwo->hEvent);
|
||||
break;
|
||||
case WINE_WM_RESTARTING:
|
||||
wwo->state = WINE_WS_PLAYING;
|
||||
SetEvent(wwo->hEvent);
|
||||
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) wwo->lpPlayPtr = lpWaveHdr;
|
||||
if (wwo->state == WINE_WS_STOPPED)
|
||||
wwo->state = WINE_WS_PLAYING;
|
||||
break;
|
||||
case WINE_WM_RESETTING:
|
||||
wodPlayer_Reset(wwo, uDevID, TRUE);
|
||||
SetEvent(wwo->hEvent);
|
||||
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(wwo->hEvent);
|
||||
ExitThread(0);
|
||||
/* shouldn't go here */
|
||||
default:
|
||||
FIXME("unknown message %d\n", msg);
|
||||
break;
|
||||
}
|
||||
if (wwo->state == WINE_WS_PLAYING) {
|
||||
wodPlayer_WriteFragments(wwo);
|
||||
}
|
||||
wodPlayer_Notify(wwo, uDevID, FALSE);
|
||||
}
|
||||
|
||||
if (!had_msg) { /* if we've received a msg we've just done this so we
|
||||
won't repeat it */
|
||||
if (wwo->state == WINE_WS_PLAYING) {
|
||||
wodPlayer_WriteFragments(wwo);
|
||||
}
|
||||
wodPlayer_Notify(wwo, uDevID, FALSE);
|
||||
for(;;) {
|
||||
wodPlayer_AwaitEvent(wwo,dwNextFeedTime,dwNextNotifyTime);
|
||||
wodPlayer_ProcessMessages(wwo,uDevID);
|
||||
if( wwo->state== WINE_WS_PLAYING ) {
|
||||
dwNextFeedTime=wodPlayer_FeedDSP(wwo);
|
||||
dwNextNotifyTime=wodPlayer_NotifyCompletions(wwo,uDevID,FALSE);
|
||||
} else {
|
||||
dwNextFeedTime=dwNextNotifyTime=0;
|
||||
}
|
||||
}
|
||||
ExitThread(0);
|
||||
/* just for not generating compilation warnings... should never be executed */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**************************************************************************
|
||||
|
@ -771,9 +916,9 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
|
|||
int format;
|
||||
int sample_rate;
|
||||
int dsp_stereo;
|
||||
int fragment_size;
|
||||
int audio_fragment;
|
||||
WINE_WAVEOUT* wwo;
|
||||
audio_buf_info info;
|
||||
|
||||
TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
|
||||
if (lpDesc == NULL) {
|
||||
|
@ -872,22 +1017,30 @@ static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
|
|||
ERR("Can't set sample_rate to %lu (%d)\n",
|
||||
wwo->format.wf.nSamplesPerSec, sample_rate);
|
||||
|
||||
/* even if we set fragment size above, read it again, just in case */
|
||||
IOCTL(audio, SNDCTL_DSP_GETBLKSIZE, fragment_size);
|
||||
if (fragment_size == -1) {
|
||||
ERR("IOCTL can't 'SNDCTL_DSP_GETBLKSIZE' !\n");
|
||||
/* Read output space info for future reference */
|
||||
if (ioctl(wwo->unixdev, SNDCTL_DSP_GETOSPACE, &info) < 0) {
|
||||
ERR("IOCTL can't 'SNDCTL_DSP_GETOSPACE' !\n");
|
||||
close(audio);
|
||||
wwo->unixdev = -1;
|
||||
return MMSYSERR_NOTENABLED;
|
||||
}
|
||||
if ((fragment_size > 1024) && (LOWORD(audio_fragment) <= 10)) {
|
||||
|
||||
/* Check that fragsize is correct per our settings above */
|
||||
if ((info.fragsize > 1024) && (LOWORD(audio_fragment) <= 10)) {
|
||||
/* we've tried to set 1K fragments or less, but it didn't work */
|
||||
ERR("fragment size set failed, size is now %d\n", fragment_size);
|
||||
ERR("fragment size set failed, size is now %d\n", info.fragsize);
|
||||
MESSAGE("Your Open Sound System driver did not let us configure small enough sound fragments.\n");
|
||||
MESSAGE("This may cause delays and other problems in audio playback with certain applications.\n");
|
||||
}
|
||||
wwo->dwFragmentSize = fragment_size;
|
||||
|
||||
/* Remember fragsize and total buffer size for future use */
|
||||
wwo->dwFragmentSize = info.fragsize;
|
||||
wwo->dwBufferSize = info.fragstotal * info.fragsize;
|
||||
wwo->dwMillisPerFragment = info.fragsize * 1000 / wwo->format.wf.nAvgBytesPerSec;
|
||||
wwo->uWaitForFragments = info.fragstotal * REFILL_BUFFER_WHEN;
|
||||
TRACE( "wait for %d fragments at %lu millis/fragment\n",
|
||||
wwo->uWaitForFragments,
|
||||
wwo->dwMillisPerFragment );
|
||||
wwo->msg_toget = 0;
|
||||
wwo->msg_tosave = 0;
|
||||
wwo->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
|
||||
|
@ -1121,13 +1274,13 @@ static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
|
|||
if (lpTime == NULL) return MMSYSERR_INVALPARAM;
|
||||
|
||||
wwo = &WOutDev[wDevID];
|
||||
val = wwo->dwPlayedTotal;
|
||||
val = updatePlayedTotal(wwo);
|
||||
|
||||
TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
|
||||
lpTime->wType, wwo->format.wBitsPerSample,
|
||||
wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
|
||||
wwo->format.wf.nAvgBytesPerSec);
|
||||
TRACE("dwTotalPlayed=%lu\n", val);
|
||||
TRACE("dwPlayedTotal=%lu\n", val);
|
||||
|
||||
switch (lpTime->wType) {
|
||||
case TIME_BYTES:
|
||||
|
|
Loading…
Reference in a new issue