audio.c

来自「Wine-20031016」· C语言 代码 · 共 2,103 行 · 第 1/5 页

C
2,103
字号
	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 = AudioIOWrite(lpData + wwo->dwOffCurrHdr, toWrite);	    TRACE("write(%p[%5lu], %5lu) => %d\n", lpData, wwo->dwOffCurrHdr, toWrite, count);	    if (count > 0 || toWrite == 0) {		DWORD	tc = GetTickCount();		if (wwo->dwLastFragDone /* + guard time ?? */ < tc)		    wwo->dwLastFragDone = tc;		wwo->dwLastFragDone += (toWrite * 1000) / wwo->format.wf.nAvgBytesPerSec;		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 = AudioIOWrite( 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;	    }	}    }}int wodPlayer_Message(WINE_WAVEOUT *wwo, int msg, DWORD param){    TRACE("wodPlayerMessage sammplerate= %d  msg=%d\n",spec[PLAYBACK].rate,msg);    EnterCriticalSection(&wwo->msg_crst);    if ((wwo->msg_tosave == wwo->msg_toget) /* buffer overflow ? */    &&  (wwo->messages[wwo->msg_toget].msg))    {	ERR("buffer overflow !?\n");        LeaveCriticalSection(&wwo->msg_crst);	return 0;    }    wwo->messages[wwo->msg_tosave].msg = msg;    wwo->messages[wwo->msg_tosave].param = param;    wwo->msg_tosave++;    if (wwo->msg_tosave > WWO_RING_BUFFER_SIZE-1)	wwo->msg_tosave = 0;    LeaveCriticalSection(&wwo->msg_crst);    /* signal a new message */    SetEvent(wwo->msg_event);    return 1;}int wodPlayer_RetrieveMessage(WINE_WAVEOUT *wwo, int *msg, DWORD *param){    EnterCriticalSection(&wwo->msg_crst);    if (wwo->msg_toget == wwo->msg_tosave) /* buffer empty ? */    {        LeaveCriticalSection(&wwo->msg_crst);	return 0;    }    *msg = wwo->messages[wwo->msg_toget].msg;    wwo->messages[wwo->msg_toget].msg = 0;    *param = wwo->messages[wwo->msg_toget].param;    wwo->msg_toget++;    if (wwo->msg_toget > WWO_RING_BUFFER_SIZE-1)	wwo->msg_toget = 0;    LeaveCriticalSection(&wwo->msg_crst);    return 1;}/************************************************************************** * 				wodPlayer_Notify		[internal] * * wodPlayer helper. Notifies (and remove from queue) all the wavehdr which content * have been played (actually to speaker, not to unixdev fd). */static	void	wodPlayer_Notify(WINE_WAVEOUT* wwo, WORD uDevID, BOOL force){    LPWAVEHDR		lpWaveHdr;    DWORD		tc = GetTickCount();    while (wwo->lpQueuePtr &&	   (force ||	    (wwo->lpQueuePtr != wwo->lpPlayPtr && wwo->lpQueuePtr != wwo->lpLoopPtr))) {	lpWaveHdr = wwo->lpQueuePtr;	if (lpWaveHdr->reserved > tc && !force) break;	wwo->dwPlayedTotal += lpWaveHdr->dwBufferLength;	wwo->lpQueuePtr = lpWaveHdr->lpNext;	lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;	lpWaveHdr->dwFlags |= WHDR_DONE;	TRACE("Notifying client with %p\n", lpWaveHdr);	if (LIBAUDIOIO_NotifyClient(uDevID, WOM_DONE, (DWORD)lpWaveHdr, 0) != MMSYSERR_NOERROR) {	    WARN("can't notify client !\n");	}    }}/************************************************************************** * 				wodPlayer_Reset			[internal] * * wodPlayer helper. Resets current output stream. */static	void	wodPlayer_Reset(WINE_WAVEOUT* wwo, WORD uDevID, BOOL reset){    /* updates current notify list */    wodPlayer_Notify(wwo, uDevID, FALSE);    /* flush all possible output *//* *FIXME In the original code I think this aborted IO. With Libaudioio you have to wait * The following function just blocks untill I/O is complete * There is possibly a way to abort the blocks buffered in streams * but this approach seems to work OK */ 	AudioIOFlush();    wwo->dwOffCurrHdr = 0;    wwo->dwRemain = wwo->dwFragmentSize;    if (reset) {	/* empty notify list */	wodPlayer_Notify(wwo, uDevID, TRUE);	wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;	wwo->state = WINE_WS_STOPPED;	wwo->dwPlayedTotal = 0;    } else {	/* FIXME: this is not accurate when looping, but can be do better ? */	wwo->lpPlayPtr = (wwo->lpLoopPtr) ? wwo->lpLoopPtr : wwo->lpQueuePtr;	wwo->state = WINE_WS_PAUSED;    }}/************************************************************************** * 				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;    wwo->state = WINE_WS_STOPPED;    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, &param)) {	    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);	}    }    ExitThread(0);    /* just for not generating compilation warnings... should never be executed */    return 0;}/************************************************************************** * 			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){    int 	 	audio;    int			format;    int			sample_rate;    int			dsp_stereo;    int			fragment_size;    int			audio_fragment;    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;    }    /* only PCM format is supported so far... */    if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||	lpDesc->lpFormat->nChannels == 0 ||	lpDesc->lpFormat->nSamplesPerSec == 0) {	WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",	     lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,	     lpDesc->lpFormat->nSamplesPerSec);	return WAVERR_BADFORMAT;    }    if (dwFlags & WAVE_FORMAT_QUERY) {	TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",	     lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,	     lpDesc->lpFormat->nSamplesPerSec);	return MMSYSERR_NOERROR;    }    wwo = &WOutDev[wDevID];    if ((dwFlags & WAVE_DIRECTSOUND) && !(wwo->caps.dwSupport & WAVECAPS_DIRECTSOUND))	/* not supported, ignore it */	dwFlags &= ~WAVE_DIRECTSOUND;    if (access(SOUND_DEV, 0) != 0)	return MMSYSERR_NOTENABLED;	audio = AudioIOOpenX( O_WRONLY|O_NDELAY,&spec[PLAYBACK],&spec[PLAYBACK]);    if (audio == -1) {	WARN("can't open sound device %s (%s)!\n", SOUND_DEV, strerror(errno));	return MMSYSERR_ALLOCATED;    } /*   fcntl(audio, F_SETFD, 1); *//* set close on exec flag - Dunno about this (RL)*/    wwo->unixdev = audio;    wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);    memcpy(&wwo->waveDesc, lpDesc, 	     sizeof(WAVEOPENDESC));    memcpy(&wwo->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));    if (wwo->format.wBitsPerSample == 0) {	WARN("Resetting zeroed wBitsPerSample\n");	wwo->format.wBitsPerSample = 8 *	    (wwo->format.wf.nAvgBytesPerSec /	     wwo->format.wf.nSamplesPerSec) /	    wwo->format.wf.nChannels;    }    if (dwFlags & WAVE_DIRECTSOUND) {        if (wwo->caps.dwSupport & WAVECAPS_SAMPLEACCURATE)	    /* we have realtime DirectSound, fragments just waste our time,	     * but a large buffer is good, so choose 64KB (32 * 2^11) */	    audio_fragment = 0x0020000B;	else	    /* to approximate realtime, we must use small fragments,	     * let's try to fragment the above 64KB (256 * 2^8) */	    audio_fragment = 0x01000008;    } else {	/* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec	 * thus leading to 46ms per fragment, and a turnaround time of 185ms	 */	/* 16 fragments max, 2^10=1024 bytes per fragment */	audio_fragment = 0x000F000A;    }    sample_rate = wwo->format.wf.nSamplesPerSec;    dsp_stereo = (wwo->format.wf.nChannels > 1) ? 1 : 0;    /*Set the sample rate*/    spec[PLAYBACK].rate=sample_rate;    /*And the size and signedness*/

⌨️ 快捷键说明

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