⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 audio.c

📁 Wine-20031016
💻 C
📖 第 1 页 / 共 3 页
字号:
 */static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo){    /* total played is the bytes written less the bytes to write ;-) */    wwo->dwPlayedTotal = wwo->dwWrittenTotal -	(wwo->dwBufferSize -	arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SPACE));    return TRUE;}/************************************************************************** * 				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 (!lpWaveHdr) return;    if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {	if (wwo->lpLoopPtr) {	    WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);	    TRACE("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;	}    }    wwo->dwPartialOffset = 0;}/************************************************************************** * 				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;    wwo->dwPartialOffset = 0;    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		 */	    } else {                lpWaveHdr = lpWaveHdr->lpNext;            }            wwo->lpLoopPtr = NULL;            wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);	}    } 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(const WINE_WAVEOUT *wwo){	int waitvalue = (wwo->dwBufferSize - arts_stream_get(wwo->play_stream,		ARTS_P_BUFFER_SPACE)) / ((wwo->format.wf.nSamplesPerSec *		wwo->format.wBitsPerSample * wwo->format.wf.nChannels)		/1000);	TRACE("wait value of %d\n", waitvalue);        /* return the time left to play the buffer */	return waitvalue;}/************************************************************************** * 			     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(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr){    DWORD dwMillis;    if(lpWaveHdr->reserved < wwo->dwPlayedTotal)    {	dwMillis = 1;    }    else    {        dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;	if(!dwMillis) dwMillis = 1;    }    TRACE("dwMillis = %ld\n", dwMillis);    return dwMillis;}/************************************************************************** * 			     wodPlayer_WriteMaxFrags            [internal] * Writes the maximum number of bytes possible to the DSP and returns * the number of bytes written. */static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes){    /* Only attempt to write to free bytes */    DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;    int toWrite = min(dwLength, *bytes);    int written;    TRACE("Writing wavehdr %p.%lu[%lu]\n",          wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength);    /* see if our buffer isn't large enough for the data we are writing */    if(wwo->buffer_size < toWrite)    {      if(wwo->sound_buffer)        HeapFree(GetProcessHeap(), 0, wwo->sound_buffer);    }    /* if we don't have a buffer then get one */    if(!wwo->sound_buffer)    {      /* allocate some memory for the buffer */      wwo->sound_buffer = HeapAlloc(GetProcessHeap(), 0, toWrite);      wwo->buffer_size = toWrite;    }    /* if we don't have a buffer then error out */    if(!wwo->sound_buffer)    {      ERR("error allocating sound_buffer memory\n");      return 0;    }    TRACE("toWrite == %d\n", toWrite);    /* apply volume to the bits */    /* for single channel audio streams we only use the LEFT volume */    if(wwo->format.wBitsPerSample == 16)    {      /* apply volume to the buffer we are about to send */      /* divide toWrite(bytes) by 2 as volume processes by 16 bits */      volume_effect16(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,                wwo->sound_buffer, toWrite>>1, wwo->volume_left,		wwo->volume_right, wwo->format.wf.nChannels);    } else if(wwo->format.wBitsPerSample == 8)    {      /* apply volume to the buffer we are about to send */      volume_effect8(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,                wwo->sound_buffer, toWrite, wwo->volume_left,		wwo->volume_right, wwo->format.wf.nChannels);    } else    {      FIXME("unsupported wwo->format.wBitsPerSample of %d\n",        wwo->format.wBitsPerSample);    }    /* send the audio data to arts for playing */    written = arts_write(wwo->play_stream, wwo->sound_buffer, toWrite);    TRACE("written = %d\n", written);    if (written <= 0) return written; /* if we wrote nothing just return */    if (written >= dwLength)        wodPlayer_PlayPtrNext(wwo);   /* If we wrote all current wavehdr, skip to the next one */    else        wwo->dwPartialOffset += written;    /* Remove the amount written */    *bytes -= written;    wwo->dwWrittenTotal += written; /* update stats on this wave device */    return written; /* return the number of bytes written */}/************************************************************************** * 				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     */    while ((lpWaveHdr = wwo->lpQueuePtr) &&           (force ||            (lpWaveHdr != wwo->lpPlayPtr &&             lpWaveHdr != wwo->lpLoopPtr &&             lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {	wwo->lpQueuePtr = lpWaveHdr->lpNext;	lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;	lpWaveHdr->dwFlags |= WHDR_DONE;	wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);    }    return  (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?        wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;}/************************************************************************** * 				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 arts, otherwise we would do the flushing here */    if (reset) {        enum win_wm_message     msg;        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;        wwo->dwPartialOffset = 0;        /* Clear partial wavehdr */        /* remove any existing message in the ring */        EnterCriticalSection(&wwo->msgRing.msg_crst);        /* return all pending headers in queue */        while (ARTS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &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, except strange results\n");            wwo->lpPlayPtr = wwo->lpLoopPtr;            wwo->dwPartialOffset = 0;            wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */        } else {	    /* the data already written is going to be played, so take */	    /* this fact into account here */	    wwo->dwPlayedTotal = wwo->dwWrittenTotal;        }	wwo->state = WINE_WS_PAUSED;    }}/************************************************************************** * 		      wodPlayer_ProcessMessages			[internal] */static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo){    LPWAVEHDR           lpWaveHdr;    enum win_wm_message	msg;    DWORD		param;    HANDLE		ev;    while (ARTS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &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_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){    DWORD       availInQ;    wodUpdatePlayedTotal(wwo);    availInQ = arts_stream_get(wwo->play_stream, ARTS_P_BUFFER_SPACE);    TRACE("availInQ = %ld\n", availInQ);    /* input queue empty and output buffer with no space */    if (!wwo->lpPlayPtr && availInQ) {        TRACE("Run out of wavehdr:s... flushing\n");        wwo->dwPlayedTotal = wwo->dwWrittenTotal;        return INFINITE;    }    /* no more room... no need to try to feed */    if(!availInQ)    {	TRACE("no more room, no need to try to feed\n");	return wodPlayer_DSPWait(wwo);    }    /* Feed from partial wavehdr */    if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0)    {        TRACE("feeding from partial wavehdr\n");        wodPlayer_WriteMaxFrags(wwo, &availInQ);    }    /* Feed wavehdrs until we run out of wavehdrs or DSP space */    if (!wwo->dwPartialOffset)    { 	 while(wwo->lpPlayPtr && availInQ > SPACE_THRESHOLD)	 {	        TRACE("feeding waveheaders until we run out of space\n");		/* note the value that dwPlayedTotal will return when this wave finishes playing */		wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;		wodPlayer_WriteMaxFrags(wwo, &availInQ);	    }    }    return wodPlayer_DSPWait(wwo);}/************************************************************************** * 				wodPlayer			[internal] */static	DWORD	CALLBACK	wodPlayer(LPVOID pmt){    WORD	  uDevID = (DWORD)pmt;    WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&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 %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);        WaitForSingleObject(wwo->msgRing.msg_event, dwSleepTime);	wodPlayer_ProcessMessages(wwo);	if (wwo->state == WINE_WS_PLAYING) {	    dwNextFeedTime = wodPlayer_FeedDSP(wwo);	    dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);	} else {	    dwNextFeedTime = dwNextNotifyTime = INFINITE;	}    }}/************************************************************************** * 			wodGetDevCaps				[internal] */static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize){    TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);    if (lpCaps == NULL) return MMSYSERR_NOTENABLED;    if (wDevID >= MAX_WAVEOUTDRV) {	TRACE("MAX_WAVOUTDRV reached !\n");	return MMSYSERR_BADDEVICEID;    }    memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));    return MMSYSERR_NOERROR;}/************************************************************************** * 				wodOpen				[internal] */static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags){    WINE_WAVEOUT*	wwo;    TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);    if (lpDesc == NULL) {	WARN("Invalid Parameter !\n");	return MMSYSERR_INVALPARAM;    }    if (wDevID >= MAX_WAVEOUTDRV) {	TRACE("MAX_WAVOUTDRV reached !\n");	return MMSYSERR_BADDEVICEID;    }    /* if this device is already open tell the app that it is allocated */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -