mixer.c

来自「一个类似windows」· C语言 代码 · 共 1,178 行 · 第 1/3 页

C
1,178
字号
			still_behind = FALSE;		dsb->primary_mixpos += slen; len -= slen;		dsb->primary_mixpos %= dsb->dsound->device->buflen;		if ((dsb->state == STATE_STOPPED) || !slen) break;	}	TRACE("new primary_mixpos=%ld, primary_advbase=%ld\n", dsb->primary_mixpos, dsb->dsound->device->mixpos);	TRACE("mixed data len=%ld, still_behind=%d\n", mixlen-len, still_behind);post_mix:	/* check if buffer should be considered complete */	if (buf_left < dsb->writelead &&	    !(dsb->playflags & DSBPLAY_LOOPING)) {		dsb->state = STATE_STOPPED;		dsb->playpos = 0;		dsb->last_playpos = 0;		dsb->buf_mixpos = 0;		dsb->leadin = FALSE;		dsb->need_remix = FALSE;		DSOUND_CheckEvent(dsb, buf_left);	}	/* return how far we think the primary buffer can	 * advance its underrun detector...*/	if (still_behind) return 0;	if ((mixlen - len) < primary_done) return 0;	slen = ((dsb->primary_mixpos < dsb->dsound->device->mixpos) ?		dsb->dsound->device->buflen : 0) + dsb->primary_mixpos -		dsb->dsound->device->mixpos;	if (slen > mixlen) {		/* the primary_done and still_behind checks above should have worked */		FIXME("problem with advancement calculation (advlen=%ld > mixlen=%ld)\n", slen, mixlen);		slen = 0;	}	return slen;}static DWORD DSOUND_MixToPrimary(DirectSoundDevice *device, DWORD playpos, DWORD writepos, DWORD mixlen, BOOL recover){	INT			i, len, maxlen = 0;	IDirectSoundBufferImpl	*dsb;	TRACE("(%ld,%ld,%ld,%d)\n", playpos, writepos, mixlen, recover);	for (i = 0; i < device->nrofbuffers; i++) {		dsb = device->buffers[i];		if (dsb->buflen && dsb->state && !dsb->hwbuf) {			TRACE("Checking %p, mixlen=%ld\n", dsb, mixlen);			EnterCriticalSection(&(dsb->lock));			if (dsb->state == STATE_STOPPING) {				DSOUND_MixCancel(dsb, writepos, TRUE);				dsb->state = STATE_STOPPED;				DSOUND_CheckEvent(dsb, 0);			} else {				if ((dsb->state == STATE_STARTING) || recover) {					dsb->primary_mixpos = writepos;					dsb->cvolpan = dsb->volpan;					dsb->need_remix = FALSE;				}				else if (dsb->need_remix) {					DSOUND_MixCancel(dsb, writepos, TRUE);					dsb->cvolpan = dsb->volpan;					dsb->need_remix = FALSE;				}				len = DSOUND_MixOne(dsb, playpos, writepos, mixlen);				if (dsb->state == STATE_STARTING)					dsb->state = STATE_PLAYING;				maxlen = (len > maxlen) ? len : maxlen;			}			LeaveCriticalSection(&(dsb->lock));		}	}	return maxlen;}static void DSOUND_MixReset(DirectSoundDevice *device, DWORD writepos){	INT			i;	IDirectSoundBufferImpl	*dsb;	int nfiller;	TRACE("(%p,%ld)\n", device, writepos);	/* the sound of silence */	nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0;	/* reset all buffer mix positions */	for (i = 0; i < device->nrofbuffers; i++) {		dsb = device->buffers[i];		if (dsb->buflen && dsb->state && !dsb->hwbuf) {			TRACE("Resetting %p\n", dsb);			EnterCriticalSection(&(dsb->lock));			if (dsb->state == STATE_STOPPING) {				dsb->state = STATE_STOPPED;			}			else if (dsb->state == STATE_STARTING) {				/* nothing */			} else {				DSOUND_MixCancel(dsb, writepos, FALSE);				dsb->cvolpan = dsb->volpan;				dsb->need_remix = FALSE;			}			LeaveCriticalSection(&(dsb->lock));		}	}	/* wipe out premixed data */	if (device->mixpos < writepos) {		FillMemory(device->buffer + writepos, device->buflen - writepos, nfiller);		FillMemory(device->buffer, device->mixpos, nfiller);	} else {		FillMemory(device->buffer + writepos, device->mixpos - writepos, nfiller);	}	/* reset primary mix position */	device->mixpos = writepos;}static void DSOUND_CheckReset(DirectSoundDevice *device, DWORD writepos){	TRACE("(%p,%ld)\n",device,writepos);	if (device->need_remix) {		DSOUND_MixReset(device, writepos);		device->need_remix = FALSE;		/* maximize Half-Life performance */		device->prebuf = ds_snd_queue_min;		device->precount = 0;	} else {		device->precount++;		if (device->precount >= 4) {			if (device->prebuf < ds_snd_queue_max)				device->prebuf++;			device->precount = 0;		}	}	TRACE("premix adjust: %d\n", device->prebuf);}void DSOUND_WaveQueue(DirectSoundDevice *device, DWORD mixq){	TRACE("(%p,%ld)\n", device, mixq);	if (mixq + device->pwqueue > ds_hel_queue) mixq = ds_hel_queue - device->pwqueue;	TRACE("queueing %ld buffers, starting at %d\n", mixq, device->pwwrite);	for (; mixq; mixq--) {		waveOutWrite(device->hwo, device->pwave[device->pwwrite], sizeof(WAVEHDR));		device->pwwrite++;		if (device->pwwrite >= DS_HEL_FRAGS) device->pwwrite = 0;		device->pwqueue++;	}}/* #define SYNC_CALLBACK */void DSOUND_PerformMix(DirectSoundDevice *device){	int nfiller;	BOOL forced;	HRESULT hres;	TRACE("(%p)\n", device);	/* the sound of silence */	nfiller = device->pwfx->wBitsPerSample == 8 ? 128 : 0;	/* whether the primary is forced to play even without secondary buffers */	forced = ((device->state == STATE_PLAYING) || (device->state == STATE_STARTING));	if (device->priolevel != DSSCL_WRITEPRIMARY) {		BOOL paused = ((device->state == STATE_STOPPED) || (device->state == STATE_STARTING));		/* FIXME: document variables */ 		DWORD playpos, writepos, inq, maxq, frag; 		if (device->hwbuf) {			hres = IDsDriverBuffer_GetPosition(device->hwbuf, &playpos, &writepos);			if (hres) {			    WARN("IDsDriverBuffer_GetPosition failed\n");			    return;			}			/* Well, we *could* do Just-In-Time mixing using the writepos,			 * but that's a little bit ambitious and unnecessary... */			/* rather add our safety margin to the writepos, if we're playing */			if (!paused) {				writepos += device->writelead;				writepos %= device->buflen;			} else writepos = playpos;		} else { 			playpos = device->pwplay * device->fraglen; 			writepos = playpos; 			if (!paused) {	 			writepos += ds_hel_margin * device->fraglen; 				writepos %= device->buflen;	 		}		}		TRACE("primary playpos=%ld, writepos=%ld, clrpos=%ld, mixpos=%ld, buflen=%ld\n",		      playpos,writepos,device->playpos,device->mixpos,device->buflen);		assert(device->playpos < device->buflen);		/* wipe out just-played sound data */		if (playpos < device->playpos) {			FillMemory(device->buffer + device->playpos, device->buflen - device->playpos, nfiller);			FillMemory(device->buffer, playpos, nfiller);		} else {			FillMemory(device->buffer + device->playpos, playpos - device->playpos, nfiller);		}		device->playpos = playpos;		EnterCriticalSection(&(device->mixlock));		/* reset mixing if necessary */		DSOUND_CheckReset(device, writepos);		/* check how much prebuffering is left */		inq = device->mixpos;		if (inq < writepos)			inq += device->buflen;		inq -= writepos;		/* find the maximum we can prebuffer */		if (!paused) {			maxq = playpos;			if (maxq < writepos)				maxq += device->buflen;			maxq -= writepos;		} else maxq = device->buflen;		/* clip maxq to device->prebuf */		frag = device->prebuf * device->fraglen;		if (maxq > frag) maxq = frag;		/* check for consistency */		if (inq > maxq) {			/* the playback position must have passed our last			 * mixed position, i.e. it's an underrun, or we have			 * nothing more to play */			TRACE("reached end of mixed data (inq=%ld, maxq=%ld)\n", inq, maxq);			inq = 0;			/* stop the playback now, to allow buffers to refill */			if (device->state == STATE_PLAYING) {				device->state = STATE_STARTING;			}			else if (device->state == STATE_STOPPING) {				device->state = STATE_STOPPED;			}			else {				/* how can we have an underrun if we aren't playing? */				WARN("unexpected primary state (%ld)\n", device->state);			}#ifdef SYNC_CALLBACK			/* DSOUND_callback may need this lock */			LeaveCriticalSection(&(device->mixlock));#endif			if (DSOUND_PrimaryStop(device) != DS_OK)				WARN("DSOUND_PrimaryStop failed\n");#ifdef SYNC_CALLBACK			EnterCriticalSection(&(device->mixlock));#endif			if (device->hwbuf) {				/* the Stop is supposed to reset play position to beginning of buffer */				/* unfortunately, OSS is not able to do so, so get current pointer */				hres = IDsDriverBuffer_GetPosition(device->hwbuf, &playpos, NULL);				if (hres) {					LeaveCriticalSection(&(device->mixlock));					WARN("IDsDriverBuffer_GetPosition failed\n");					return;				}			} else {	 			playpos = device->pwplay * device->fraglen;			}			writepos = playpos;			device->playpos = playpos;			device->mixpos = writepos;			inq = 0;			maxq = device->buflen;			if (maxq > frag) maxq = frag;			FillMemory(device->buffer, device->buflen, nfiller);			paused = TRUE;		}		/* do the mixing */		frag = DSOUND_MixToPrimary(device, playpos, writepos, maxq, paused);		if (forced) frag = maxq - inq;		device->mixpos += frag;		device->mixpos %= device->buflen;		if (frag) {			/* buffers have been filled, restart playback */			if (device->state == STATE_STARTING) {				device->state = STATE_PLAYING;			}			else if (device->state == STATE_STOPPED) {				/* the dsound is supposed to play if there's something to play				 * even if it is reported as stopped, so don't let this confuse you */				device->state = STATE_STOPPING;			}			LeaveCriticalSection(&(device->mixlock));			if (paused) {				if (DSOUND_PrimaryPlay(device) != DS_OK)					WARN("DSOUND_PrimaryPlay failed\n");				else					TRACE("starting playback\n");			}		}		else			LeaveCriticalSection(&(device->mixlock));	} else {		/* in the DSSCL_WRITEPRIMARY mode, the app is totally in charge... */		if (device->state == STATE_STARTING) {			if (DSOUND_PrimaryPlay(device) != DS_OK)				WARN("DSOUND_PrimaryPlay failed\n");			else				device->state = STATE_PLAYING;		}		else if (device->state == STATE_STOPPING) {			if (DSOUND_PrimaryStop(device) != DS_OK)				WARN("DSOUND_PrimaryStop failed\n");			else				device->state = STATE_STOPPED;		}	}}void CALLBACK DSOUND_timer(UINT timerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2){        DirectSoundDevice * device = (DirectSoundDevice*)dwUser;	DWORD start_time =  GetTickCount();        DWORD end_time;	TRACE("(%d,%d,0x%lx,0x%lx,0x%lx)\n",timerID,msg,dwUser,dw1,dw2);        TRACE("entering at %ld\n", start_time);	if (DSOUND_renderer[device->drvdesc.dnDevNode] != device) {		ERR("dsound died without killing us?\n");		timeKillEvent(timerID);		timeEndPeriod(DS_TIME_RES);		return;	}	RtlAcquireResourceShared(&(device->buffer_list_lock), TRUE);	if (device->ref)		DSOUND_PerformMix(device);	RtlReleaseResource(&(device->buffer_list_lock));	end_time = GetTickCount();	TRACE("completed processing at %ld, duration = %ld\n", end_time, end_time - start_time);}void CALLBACK DSOUND_callback(HWAVEOUT hwo, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2){        DirectSoundDevice * device = (DirectSoundDevice*)dwUser;	TRACE("(%p,%x,%lx,%lx,%lx)\n",hwo,msg,dwUser,dw1,dw2);	TRACE("entering at %ld, msg=%08x(%s)\n", GetTickCount(), msg, 		msg==MM_WOM_DONE ? "MM_WOM_DONE" : msg==MM_WOM_CLOSE ? "MM_WOM_CLOSE" : 		msg==MM_WOM_OPEN ? "MM_WOM_OPEN" : "UNKNOWN");	if (msg == MM_WOM_DONE) {		DWORD inq, mixq, fraglen, buflen, pwplay, playpos, mixpos;		if (device->pwqueue == (DWORD)-1) {			TRACE("completed due to reset\n");			return;		}/* it could be a bad idea to enter critical section here... if there's lock contention, * the resulting scheduling delays might obstruct the winmm player thread */#ifdef SYNC_CALLBACK		EnterCriticalSection(&(device->mixlock));#endif		/* retrieve current values */		fraglen = device->fraglen;		buflen = device->buflen;		pwplay = device->pwplay;		playpos = pwplay * fraglen;		mixpos = device->mixpos;		/* check remaining mixed data */		inq = ((mixpos < playpos) ? buflen : 0) + mixpos - playpos;		mixq = inq / fraglen;		if ((inq - (mixq * fraglen)) > 0) mixq++;		/* complete the playing buffer */		TRACE("done playing primary pos=%ld\n", playpos);		pwplay++;		if (pwplay >= DS_HEL_FRAGS) pwplay = 0;		/* write new values */		device->pwplay = pwplay;		device->pwqueue--;		/* queue new buffer if we have data for it */		if (inq>1) DSOUND_WaveQueue(device, inq-1);#ifdef SYNC_CALLBACK		LeaveCriticalSection(&(device->mixlock));#endif	}	TRACE("completed\n");}

⌨️ 快捷键说明

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