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

📄 audio.c

📁 Wine-20031016
💻 C
📖 第 1 页 / 共 5 页
字号:
/************************************************************************** * 				wodPlayer_NotifyCompletions	[internal] * * 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 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, NULL);    /* updates current notify list */    wodPlayer_NotifyCompletions(wwo, FALSE);    /* flush all possible output */    if (OSS_ResetDevice(wwo->ossdev) != MMSYSERR_NOERROR)    {	wwo->hThread = 0;	wwo->state = WINE_WS_STOPPED;	ExitThread(-1);    }    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;        /* 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 (OSS_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &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);        }        RESET_OMR(&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, except 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;    }}/************************************************************************** * 		      wodPlayer_ProcessMessages			[internal] */static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo){    LPWAVEHDR           lpWaveHdr;    enum win_wm_message	msg;    DWORD		param;    HANDLE		ev;    while (OSS_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:            if (wwo->state == WINE_WS_PAUSED)            {                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, NULL);	    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){    audio_buf_info dspspace;    DWORD       availInQ;    wodUpdatePlayedTotal(wwo, &dspspace);    availInQ = dspspace.bytes;    TRACE("fragments=%d/%d, fragsize=%d, bytes=%d\n",	  dspspace.fragments, dspspace.fragstotal, dspspace.fragsize, dspspace.bytes);    /* input queue empty and output buffer with less than one fragment to play      * actually some cards do not play the fragment before the last if this one is partially feed     * so we need to test for full the availability of 2 fragments     */    if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + 2 * wwo->dwFragmentSize &&         !wwo->bNeedPost) {	TRACE("Run out of wavehdr:s...\n");        return INFINITE;    }    /* no more room... no need to try to feed */    if (dspspace.fragments != 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 %lu\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);        }        if (wwo->bNeedPost) {            /* OSS doesn't start before it gets either 2 fragments or a SNDCTL_DSP_POST;             * if it didn't get one, we give it the other */            if (wwo->dwBufferSize < availInQ + 2 * wwo->dwFragmentSize)                ioctl(wwo->ossdev->fd, SNDCTL_DSP_POST, 0);            wwo->bNeedPost = FALSE;        }    }    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);	WAIT_OMR(&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 flushing, */		/* check that a notification didn't give us more */		wodPlayer_ProcessMessages(wwo);		if (!wwo->lpPlayPtr) {		    TRACE("flushing\n");		    ioctl(wwo->ossdev->fd, SNDCTL_DSP_SYNC, 0);		    wwo->dwPlayedTotal = wwo->dwWrittenTotal;                    dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);		} else {		    TRACE("recovering\n");		    dwNextFeedTime = wodPlayer_FeedDSP(wwo);		}	    }	} 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 >= numOutDev) {	TRACE("numOutDev reached !\n");	return MMSYSERR_BADDEVICEID;    }    memcpy(lpCaps, &WOutDev[wDevID].ossdev->out_caps, min(dwSize, sizeof(*lpCaps)));    return MMSYSERR_NOERROR;}/************************************************************************** * 				wodOpen				[internal] */static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags){    int			audio_fragment;    WINE_WAVEOUT*	wwo;    audio_buf_info      info;    DWORD               ret;    TRACE("(%u, %p[cb=%08lx], %08lX);\n", wDevID, lpDesc, lpDesc->dwCallback, dwFlags);    if (lpDesc == NULL) {	WARN("Invalid Parameter !\n");	return MMSYSERR_INVALPARAM;    }    if (wDevID >= numOutDev) {	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->ossdev->out_caps.dwSupport & WAVECAPS_DIRECTSOUND))	/* not supported, ignore it */	dwFlags &= ~WAVE_DIRECTSOUND;    if (dwFlags & WAVE_DIRECTSOUND) {        if (wwo->ossdev->out_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;    }    if (wwo->state != WINE_WS_CLOSED) return MMSYSERR_ALLOCATED;    /* we want to be able to mmap() the device, which means it must be opened readable,     * otherwise mmap() will fail (at least under Linux) */    ret = OSS_OpenDevice(wwo->ossdev,                         (dwFlags & WAVE_DIRECTSOUND) ? O_RDWR : O_WRONLY,                         &audio_fragment,                         (dwFlags & WAVE_DIRECTSOUND) ? 0 : 1,                         lpDesc->lpFormat->nSamplesPerSec,                         (lpDesc->lpFormat->nChannels > 1) ? 1 : 0,                         (lpDesc->lpFormat->wBitsPerSample == 16)                             ? AFMT_S16_LE : AFMT_U8);    if ((ret==MMSYSERR_NOERROR) && (dwFlags & WAVE_DIRECTSOUND)) {        lpDesc->lpFormat->nSamplesPerSec=wwo->ossdev->sample_rate;        lpDesc->lpFormat->nChannels=(wwo->ossdev->stereo ? 2 : 1);        lpDesc->lpFormat->wBitsPerSample=(wwo->ossdev->format == AFMT_U8 ? 8 : 16);        lpDesc->lpFormat->nBlockAlign=lpDesc->lpFormat->nChannels*lpDesc->lpFormat->wBitsPerSample/8;        lpDesc->lpFormat->nAvgBytesPerSec=lpDesc->lpFormat->nSamplesPerSec*lpDesc->lpFormat->nBlockAlign;        TRACE("OSS_OpenDevice returned this format: %ldx%dx%d\n",              lpDesc->lpFormat->nSamplesPerSec,              lpDesc->lpFormat->wBitsPerSample,              lpDesc->lpFormat->nChannels);    }    if (ret != 0) return ret;    wwo->state = WINE_WS_STOPPED;    wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);    memcpy(&wwo->waveDesc, lpDesc, 	     sizeof(WAVEOPENDESC));    memcpy(&wwo->format,   lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));

⌨️ 快捷键说明

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