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

📄 audio.c

📁 Wine-20031016
💻 C
📖 第 1 页 / 共 5 页
字号:
	/* this will be used to check if the given wave header has been fully played or not... */        lpWaveHdr->reserved = lpWaveHdr->dwBufferLength;        /* If we wrote all current wavehdr, skip to the next one */        wodPlayer_PlayPtrNext(wwo);    }    *frames -= written;    wwo->dwPlayedTotal += snd_pcm_frames_to_bytes(wwo->p_handle, written);    return written;}/************************************************************************** * 				wodPlayer_NotifyCompletions	[internal] * * Notifies and remove from queue all wavehdrs which have been played to * the speaker (ie. they have cleared the ALSA 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 == lpWaveHdr->dwBufferLength))) {	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;}void wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count){    unsigned short revents;    if (snd_pcm_state(handle) != SND_PCM_STATE_RUNNING)	return;    while (1) {	poll(ufds, count, -1);	snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents);	if (revents & POLLERR)	    return;	/*if (revents & POLLOUT)		return 0;*/    }}/************************************************************************** * 				wodPlayer_Reset			[internal] * * wodPlayer helper. Resets current output stream. */static	void	wodPlayer_Reset(WINE_WAVEOUT* wwo){    enum win_wm_message	msg;    DWORD		        param;    HANDLE		        ev;    int                         err;    /* flush all possible output */    wait_for_poll(wwo->p_handle, wwo->ufds, wwo->count);    /* updates current notify list */    wodPlayer_NotifyCompletions(wwo, FALSE);    if ( (err = snd_pcm_drop(wwo->p_handle)) < 0) {	FIXME("flush: %s\n", snd_strerror(err));	wwo->hThread = 0;	wwo->state = WINE_WS_STOPPED;	ExitThread(-1);    }    if ( (err = snd_pcm_prepare(wwo->p_handle)) < 0 )        ERR("pcm prepare failed: %s\n", snd_strerror(err));    /* remove any buffer */    wodPlayer_NotifyCompletions(wwo, TRUE);    wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;    wwo->state = WINE_WS_STOPPED;    /* remove any existing message in the ring */    EnterCriticalSection(&wwo->msgRing.msg_crst);    /* return all pending headers in queue */    while (ALSA_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);    }    ResetEvent(wwo->msgRing.msg_event);    LeaveCriticalSection(&wwo->msgRing.msg_crst);}/************************************************************************** * 		      wodPlayer_ProcessMessages			[internal] */static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo){    LPWAVEHDR           lpWaveHdr;    enum win_wm_message	msg;    DWORD		param;    HANDLE		ev;    int                 err;    while (ALSA_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev)) {    /* TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param); */	switch (msg) {	case WINE_WM_PAUSING:	    if ( snd_pcm_state(wwo->p_handle) == SND_PCM_STATE_RUNNING )	     {		err = snd_pcm_pause(wwo->p_handle, 1);		if ( err < 0 )		    ERR("pcm_pause failed: %s\n", snd_strerror(err));	     }	    wwo->state = WINE_WS_PAUSED;	    SetEvent(ev);	    break;	case WINE_WM_RESTARTING:            if (wwo->state == WINE_WS_PAUSED)            {		if ( snd_pcm_state(wwo->p_handle) == SND_PCM_STATE_PAUSED )		 {		    err = snd_pcm_pause(wwo->p_handle, 0);		    if ( err < 0 )		        ERR("pcm_pause failed: %s\n", snd_strerror(err));		 }                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);	    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){    DWORD               availInQ = snd_pcm_avail_update(wwo->p_handle);    /* no more room... no need to try to feed */    while (wwo->lpPlayPtr && availInQ > 0)        if ( wodPlayer_WriteMaxFrags(wwo, &availInQ) < 0 )	    break;    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;    snd_pcm_hw_params_t *       hw_params;    snd_pcm_sw_params_t *       sw_params;    snd_pcm_access_t            access;    snd_pcm_format_t            format;    int                         rate;    unsigned int                buffer_time = 500000;    unsigned int                period_time = 10000;    int                         buffer_size;    snd_pcm_uframes_t           period_size;    int                         flags;    snd_pcm_t *                 pcm;    int                         err;    snd_pcm_hw_params_alloca(&hw_params);    snd_pcm_sw_params_alloca(&sw_params);    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;    wwo->p_handle = 0;    flags = SND_PCM_NONBLOCK;    if ( dwFlags & WAVE_DIRECTSOUND )    	flags |= SND_PCM_ASYNC;    if (snd_pcm_open(&pcm, wwo->device, SND_PCM_STREAM_PLAYBACK, dwFlags))    {        ERR("Error open: %s\n", snd_strerror(errno));	return MMSYSERR_NOTENABLED;    }    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;    }    snd_pcm_hw_params_any(pcm, hw_params);#define EXIT_ON_ERROR(f,e,txt) do \{ \    int err; \    if ( (err = (f) ) < 0) \    { \	ERR(txt ": %s\n", snd_strerror(err)); \	snd_pcm_close(pcm); \	return e; \    } \} while(0)    access = SND_PCM_ACCESS_MMAP_INTERLEAVED;    if ( ( err = snd_pcm_hw_params_set_access(pcm, hw_params, access ) ) < 0) {        WARN("mmap not available. switching to standard write.\n");        access = SND_PCM_ACCESS_RW_INTERLEAVED;	EXIT_ON_ERROR( snd_pcm_hw_params_set_access(pcm, hw_params, access ), MMSYSERR_INVALPARAM, "unable to set access for playback");	wwo->write = snd_pcm_writei;    }    else	wwo->write = snd_pcm_mmap_writei;    EXIT_ON_ERROR( snd_pcm_hw_params_set_channels(pcm, hw_params, wwo->format.wf.nChannels), MMSYSERR_INVALPARAM, "unable to set required channels");    format = (wwo->format.wBitsPerSample == 16) ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_U8;    EXIT_ON_ERROR( snd_pcm_hw_params_set_format(pcm, hw_params, format), MMSYSERR_INVALPARAM, "unable to set required format");    rate = snd_pcm_hw_params_set_rate_near(pcm, hw_params, wwo->format.wf.nSamplesPerSec, 0);    if (rate < 0) {	ERR("Rate %ld Hz not available for playback: %s\n", wwo->format.wf.nSamplesPerSec, snd_strerror(rate));	snd_pcm_close(pcm);        return WAVERR_BADFORMAT;    }    if (rate != wwo->format.wf.nSamplesPerSec) {	ERR("Rate doesn't match (requested %ld Hz, got %d Hz)\n", wwo->format.wf.nSamplesPerSec, rate);	snd_pcm_close(pcm);        return WAVERR_BADFORMAT;    }        EXIT_ON_ERROR( snd_pcm_hw_params_set_buffer_time_near(pcm, hw_params, buffer_time, 0), MMSYSERR_INVALPARAM, "unable to set buffer time");    EXIT_ON_ERROR( snd_pcm_hw_params_set_period_time_near(pcm, hw_params, period_time, 0), MMSYSERR_INVALPARAM, "unable to set period time");    EXIT_ON_ERROR( snd_pcm_hw_params(pcm, hw_params), MMSYSERR_INVALPARAM, "unable to set hw params for playback");        period_size = snd_pcm_hw_params_get_period_size(hw_params, 0);    buffer_size = snd_pcm_hw_params_get_buffer_size(hw_params);    snd_pcm_sw_params_current(pcm, sw_params);    EXIT_ON_ERROR( snd_pcm_sw_params_set_start_threshold(pcm, sw_params, dwFlags & WAVE_DIRECTSOUND ? INT_MAX : 1 ), MMSYSERR_ERROR, "unable to set start threshold");    EXIT_ON_ERROR( snd_pcm_sw_params_set_silence_size(pcm, sw_params, 0), MMSYSERR_ERROR, "unable to set silence size");

⌨️ 快捷键说明

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