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

📄 audio.c

📁 Wine-20031016
💻 C
📖 第 1 页 / 共 3 页
字号:
    if (mr->msg_toget == mr->msg_tosave) /* buffer empty ? */    {        LeaveCriticalSection(&mr->msg_crst);	return 0;    }    *msg = mr->messages[mr->msg_toget].msg;    mr->messages[mr->msg_toget].msg = 0;    *param = mr->messages[mr->msg_toget].param;    *hEvent = mr->messages[mr->msg_toget].hEvent;    mr->msg_toget = (mr->msg_toget + 1) % mr->ring_buffer_size;    LeaveCriticalSection(&mr->msg_crst);    return 1;}/*======================================================================* *                  Low level WAVE OUT implementation			* *======================================================================*//************************************************************************** * 			wodNotifyClient			[internal] */static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2){    TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, dwParam1, dwParam2);    switch (wMsg) {    case WOM_OPEN:    case WOM_CLOSE:    case WOM_DONE:	if (wwo->wFlags != DCB_NULL &&	    !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags, (HDRVR)wwo->waveDesc.hWave,			    wMsg, wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {	    WARN("can't notify client !\n");	    return MMSYSERR_ERROR;	}	break;    default:	FIXME("Unknown callback message %u\n", wMsg);        return MMSYSERR_INVALPARAM;    }    return MMSYSERR_NOERROR;}/************************************************************************** * 				wodUpdatePlayedTotal	[internal] * */static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo){    wwo->PlayedTotal = wwo->WrittenTotal;    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;	}    }}/************************************************************************** * 				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		 */	    } 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_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     */    wodUpdatePlayedTotal(wwo);    while ((lpWaveHdr = wwo->lpQueuePtr) && (force || (lpWaveHdr != wwo->lpPlayPtr &&            lpWaveHdr != wwo->lpLoopPtr && lpWaveHdr->reserved <= wwo->PlayedTotal))) {	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) ?            1 : 1;}/************************************************************************** * 				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 nas, otherwise we would do the flushing here */    nas_free(wwo);    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->PlayedTotal = wwo->WrittenTotal = 0;        /* remove any existing message in the ring */        EnterCriticalSection(&wwo->msgRing.msg_crst);        /* return all pending headers in queue */        while (NAS_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->WrittenTotal = wwo->PlayedTotal; /* this is wrong !!! */        } else {	    /* the data already written is going to be played, so take */	    /* this fact into account here */	    wwo->PlayedTotal = wwo->WrittenTotal;        }	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 (NAS_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			[internal] */static	DWORD	CALLBACK	wodPlayer(LPVOID pmt){    WORD	  uDevID = (DWORD)pmt;    WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];    wwo->state = WINE_WS_STOPPED;    SetEvent(wwo->hStartUpEvent);    for (;;) {        if (wwo->FlowStarted) {           AuHandleEvents(wwo->AuServ);           if (wwo->state == WINE_WS_PLAYING && wwo->freeBytes && wwo->BufferUsed)              nas_send_buffer(wwo);        }        if (wwo->BufferUsed <= FRAG_SIZE && wwo->writeBytes > 0)           wodPlayer_NotifyCompletions(wwo, FALSE);        WaitForSingleObject(wwo->msgRing.msg_event, 20);        wodPlayer_ProcessMessages(wwo);        while(wwo->lpPlayPtr) {           wwo->lpPlayPtr->reserved = wwo->WrittenTotal + wwo->lpPlayPtr->dwBufferLength;           nas_add_buffer(wwo);           wodPlayer_PlayPtrNext(wwo);        }    }}/************************************************************************** * 			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("wodOpen (%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 */    wwo = &WOutDev[wDevID];    if(wwo->open)    {      TRACE("device already allocated\n");      return MMSYSERR_ALLOCATED;    }    /* 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;    }    /* direct sound not supported, ignore the flag */    dwFlags &= ~WAVE_DIRECTSOUND;    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 (!nas_open(wwo))       return MMSYSERR_ALLOCATED;    NAS_InitRingMessage(&wwo->msgRing);    /* create player thread */    if (!(dwFlags & WAVE_DIRECTSOUND)) {	wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);	wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));	WaitForSingleObject(wwo->hStartUpEvent, INFINITE);	CloseHandle(wwo->hStartUpEvent);    } else {	wwo->hThread = INVALID_HANDLE_VALUE;	wwo->dwThreadID = 0;    }    wwo->hStartUpEvent = INVALID_HANDLE_VALUE;    TRACE("stream=0x%lx, BufferSize=%ld\n", (long)wwo->AuServ, wwo->BufferSize);    TRACE("wBitsPerSample=%u nAvgBytesPerSec=%lu nSamplesPerSec=%lu nChannels=%u nBlockAlign=%u\n",	  wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,	  wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,	  wwo->format.wf.nBlockAlign);    return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);}/************************************************************************** * 				wodClose			[internal] */static DWORD wodClose(WORD wDevID){    DWORD		ret = MMSYSERR_NOERROR;    WINE_WAVEOUT*	wwo;    TRACE("(%u);\n", wDevID);    if (wDevID >= MAX_WAVEOUTDRV || AuServ  == NULL)    {	WARN("bad device ID !\n");	return MMSYSERR_BADDEVICEID;    }    wwo = &WOutDev[wDevID];    if (wwo->lpQueuePtr) {	WARN("buffers still playing !\n");	ret = WAVERR_STILLPLAYING;    } else {	TRACE("imhere[3-close]\n");	if (wwo->hThread != INVALID_HANDLE_VALUE) {	    NAS_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);	}        NAS_DestroyRingMessage(&wwo->msgRing);	NAS_CloseDevice(wwo);	/* close the stream and clean things up */	ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);    }    return ret;}/************************************************************************** * 				wodWrite			[internal] * */static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize){    TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);    /* first, do the sanity checks... */    if (wDevID >= MAX_WAVEOUTDRV || AuServ == NULL)    {        WARN("bad dev ID !\n");	return MMSYSERR_BADDEVICEID;    }    if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))    {	TRACE("unprepared\n");	return WAVERR_UNPREPARED;    }    if (lpWaveHdr->dwFlags & WHDR_INQUEUE)    {	TRACE("still playing\n");	return WAVERR_STILLPLAYING;    }    lpWaveHdr->dwFlags &= ~WHDR_DONE;    lpWaveHdr->dwFlags |= WHDR_INQUEUE;    lpWaveHdr->lpNext = 0;    TRACE("adding ring message\n");    NAS_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);    return MMSYSERR_NOERROR;}/************************************************************************** * 				wodPrepare			[internal] */static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize){    TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);    if (wDevID >= MAX_WAVEOUTDRV) {	WARN("bad device ID !\n");	return MMSYSERR_BADDEVICEID;    }    if (lpWaveHdr->dwFlags & WHDR_INQUEUE)	return WAVERR_STILLPLAYING;    lpWaveHdr->dwFlags |= WHDR_PREPARED;    lpWaveHdr->dwFlags &= ~WHDR_DONE;    return MMSYSERR_NOERROR;}/************************************************************************** * 				wodUnprepare			[internal]

⌨️ 快捷键说明

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