mixer.c

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

C
1,178
字号
	if (len % dsb->dsound->device->pwfx->nBlockAlign) {		INT nBlockAlign = dsb->dsound->device->pwfx->nBlockAlign;		ERR("length not a multiple of block size, len = %d, block size = %d\n", len, nBlockAlign);		len = (len / nBlockAlign) * nBlockAlign;	/* data alignment */	}	if ((buf = ibuf = DSOUND_tmpbuffer(dsb->dsound->device, len)) == NULL)		return 0;	TRACE("MixInBuffer (%p) len = %d, dest = %ld\n", dsb, len, writepos);	ilen = DSOUND_MixerNorm(dsb, ibuf, len);	if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||	    (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) ||	    (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))		DSOUND_MixerVol(dsb, ibuf, len);	if (dsb->dsound->device->pwfx->wBitsPerSample == 8) {		BYTE	*obuf = dsb->dsound->device->buffer + writepos;		if ((writepos + len) <= dsb->dsound->device->buflen)			todo = len;		else			todo = dsb->dsound->device->buflen - writepos;		for (i = 0; i < todo; i++) {			/* 8-bit WAV is unsigned */			field = (*ibuf++ - 128);			field += (*obuf - 128);			if (field > 127) field = 127;			else if (field < -128) field = -128;			*obuf++ = field + 128;		} 		if (todo < len) {			todo = len - todo;			obuf = dsb->dsound->device->buffer;			for (i = 0; i < todo; i++) {				/* 8-bit WAV is unsigned */				field = (*ibuf++ - 128);				field += (*obuf - 128);				if (field > 127) field = 127;				else if (field < -128) field = -128;				*obuf++ = field + 128;			}		}        } else {		INT16	*ibufs, *obufs;		ibufs = (INT16 *) ibuf;		obufs = (INT16 *)(dsb->dsound->device->buffer + writepos);		if ((writepos + len) <= dsb->dsound->device->buflen)			todo = len / 2;		else			todo = (dsb->dsound->device->buflen - writepos) / 2;		for (i = 0; i < todo; i++) {			/* 16-bit WAV is signed */			field = *ibufs++;			field += *obufs;			if (field > 32767) field = 32767;			else if (field < -32768) field = -32768;			*obufs++ = field;		}		if (todo < (len / 2)) {			todo = (len / 2) - todo;			obufs = (INT16 *)dsb->dsound->device->buffer;			for (i = 0; i < todo; i++) {				/* 16-bit WAV is signed */				field = *ibufs++;				field += *obufs;				if (field > 32767) field = 32767;				else if (field < -32768) field = -32768;				*obufs++ = field;			}		}        }	if (dsb->leadin && (dsb->startpos > dsb->buf_mixpos) && (dsb->startpos <= dsb->buf_mixpos + ilen)) {		/* HACK... leadin should be reset when the PLAY position reaches the startpos,		 * not the MIX position... but if the sound buffer is bigger than our prebuffering		 * (which must be the case for the streaming buffers that need this hack anyway)		 * plus DS_HEL_MARGIN or equivalent, then this ought to work anyway. */		dsb->leadin = FALSE;	}	dsb->buf_mixpos += ilen;	if (dsb->buf_mixpos >= dsb->buflen) {		if (dsb->playflags & DSBPLAY_LOOPING) {			/* wrap */			dsb->buf_mixpos %= dsb->buflen;			if (dsb->leadin && (dsb->startpos <= dsb->buf_mixpos))				dsb->leadin = FALSE; /* HACK: see above */		} else if (dsb->buf_mixpos > dsb->buflen) {			ERR("Mixpos (%lu) past buflen (%lu), capping...\n", dsb->buf_mixpos, dsb->buflen);			dsb->buf_mixpos = dsb->buflen;		}	}	return len;}static void DSOUND_PhaseCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, DWORD len){	INT     ilen, field;	UINT    i, todo;	BYTE	*buf, *ibuf;	TRACE("(%p,%ld,%ld)\n",dsb,writepos,len);	if (len % dsb->dsound->device->pwfx->nBlockAlign) {		INT nBlockAlign = dsb->dsound->device->pwfx->nBlockAlign;		ERR("length not a multiple of block size, len = %ld, block size = %d\n", len, nBlockAlign);		len = (len / nBlockAlign) * nBlockAlign;	/* data alignment */	}	if ((buf = ibuf = DSOUND_tmpbuffer(dsb->dsound->device, len)) == NULL)		return;	TRACE("PhaseCancel (%p) len = %ld, dest = %ld\n", dsb, len, writepos);	ilen = DSOUND_MixerNorm(dsb, ibuf, len);	if ((dsb->dsbd.dwFlags & DSBCAPS_CTRLPAN) ||	    (dsb->dsbd.dwFlags & DSBCAPS_CTRLVOLUME) ||	    (dsb->dsbd.dwFlags & DSBCAPS_CTRL3D))		DSOUND_MixerVol(dsb, ibuf, len);	/* subtract instead of add, to phase out premixed data */	if (dsb->dsound->device->pwfx->wBitsPerSample == 8) {		BYTE	*obuf = dsb->dsound->device->buffer + writepos;		if ((writepos + len) <= dsb->dsound->device->buflen)			todo = len;		else			todo = dsb->dsound->device->buflen - writepos;		for (i = 0; i < todo; i++) {			/* 8-bit WAV is unsigned */			field = (*ibuf++ - 128);			field -= (*obuf - 128);			if (field > 127) field = 127;			else if (field < -128) field = -128;			*obuf++ = field + 128;		} 		if (todo < len) {			todo = len - todo;			obuf = dsb->dsound->device->buffer;			for (i = 0; i < todo; i++) {				/* 8-bit WAV is unsigned */				field = (*ibuf++ - 128);				field -= (*obuf - 128);				if (field > 127) field = 127;				else if (field < -128) field = -128;				*obuf++ = field + 128;			}		}        } else {		INT16	*ibufs, *obufs;		ibufs = (INT16 *) ibuf;		obufs = (INT16 *)(dsb->dsound->device->buffer + writepos);		if ((writepos + len) <= dsb->dsound->device->buflen)			todo = len / 2;		else			todo = (dsb->dsound->device->buflen - writepos) / 2;		for (i = 0; i < todo; i++) {			/* 16-bit WAV is signed */			field = *ibufs++;			field -= *obufs;			if (field > 32767) field = 32767;			else if (field < -32768) field = -32768;			*obufs++ = field;		}		if (todo < (len / 2)) {			todo = (len / 2) - todo;			obufs = (INT16 *)dsb->dsound->device->buffer;			for (i = 0; i < todo; i++) {				/* 16-bit WAV is signed */				field = *ibufs++;				field -= *obufs;				if (field > 32767) field = 32767;				else if (field < -32768) field = -32768;				*obufs++ = field;			}		}        }}static void DSOUND_MixCancel(IDirectSoundBufferImpl *dsb, DWORD writepos, BOOL cancel){	DWORD   size, flen, len, npos, nlen;	INT	iAdvance = dsb->pwfx->nBlockAlign;	INT	oAdvance = dsb->dsound->device->pwfx->nBlockAlign;	/* determine amount of premixed data to cancel */	DWORD primary_done =		((dsb->primary_mixpos < writepos) ? dsb->dsound->device->buflen : 0) +		dsb->primary_mixpos - writepos;	TRACE("(%p, %ld), buf_mixpos=%ld\n", dsb, writepos, dsb->buf_mixpos);	/* backtrack the mix position */	size = primary_done / oAdvance;	flen = size * dsb->freqAdjust;	len = (flen >> DSOUND_FREQSHIFT) * iAdvance;	flen &= (1<<DSOUND_FREQSHIFT)-1;	while (dsb->freqAcc < flen) {		len += iAdvance;		dsb->freqAcc += 1<<DSOUND_FREQSHIFT;	}	len %= dsb->buflen;	npos = ((dsb->buf_mixpos < len) ? dsb->buflen : 0) +		dsb->buf_mixpos - len;	if (dsb->leadin && (dsb->startpos > npos) && (dsb->startpos <= npos + len)) {		/* stop backtracking at startpos */		npos = dsb->startpos;		len = ((dsb->buf_mixpos < npos) ? dsb->buflen : 0) +			dsb->buf_mixpos - npos;		flen = dsb->freqAcc;		nlen = len / dsb->pwfx->nBlockAlign;		nlen = ((nlen << DSOUND_FREQSHIFT) + flen) / dsb->freqAdjust;		nlen *= dsb->dsound->device->pwfx->nBlockAlign;		writepos =			((dsb->primary_mixpos < nlen) ? dsb->dsound->device->buflen : 0) +			dsb->primary_mixpos - nlen;	}	dsb->freqAcc -= flen;	dsb->buf_mixpos = npos;	dsb->primary_mixpos = writepos;	TRACE("new buf_mixpos=%ld, primary_mixpos=%ld (len=%ld)\n",	      dsb->buf_mixpos, dsb->primary_mixpos, len);	if (cancel) DSOUND_PhaseCancel(dsb, writepos, len);}void DSOUND_MixCancelAt(IDirectSoundBufferImpl *dsb, DWORD buf_writepos){#if 0	DWORD   i, size, flen, len, npos, nlen;	INT	iAdvance = dsb->pwfx->nBlockAlign;	INT	oAdvance = dsb->dsound->device->pwfx->nBlockAlign;	/* determine amount of premixed data to cancel */	DWORD buf_done =		((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +		dsb->buf_mixpos - buf_writepos;#endif	WARN("(%p, %ld), buf_mixpos=%ld\n", dsb, buf_writepos, dsb->buf_mixpos);	/* since this is not implemented yet, just cancel *ALL* prebuffering for now	 * (which is faster anyway when there's only a single secondary buffer) */	dsb->dsound->device->need_remix = TRUE;}void DSOUND_ForceRemix(IDirectSoundBufferImpl *dsb){	TRACE("(%p)\n",dsb);	EnterCriticalSection(&dsb->lock);	if (dsb->state == STATE_PLAYING)		dsb->dsound->device->need_remix = TRUE;	LeaveCriticalSection(&dsb->lock);}static DWORD DSOUND_MixOne(IDirectSoundBufferImpl *dsb, DWORD playpos, DWORD writepos, DWORD mixlen){	DWORD len, slen;	/* determine this buffer's write position */	DWORD buf_writepos = DSOUND_CalcPlayPosition(dsb, writepos, writepos);	/* determine how much already-mixed data exists */	DWORD buf_done =		((dsb->buf_mixpos < buf_writepos) ? dsb->buflen : 0) +		dsb->buf_mixpos - buf_writepos;	DWORD primary_done =		((dsb->primary_mixpos < writepos) ? dsb->dsound->device->buflen : 0) +		dsb->primary_mixpos - writepos;	DWORD adv_done =		((dsb->dsound->device->mixpos < writepos) ? dsb->dsound->device->buflen : 0) +		dsb->dsound->device->mixpos - writepos;	DWORD played =		((buf_writepos < dsb->playpos) ? dsb->buflen : 0) +		buf_writepos - dsb->playpos;	DWORD buf_left = dsb->buflen - buf_writepos;	int still_behind;	TRACE("(%p,%ld,%ld,%ld)\n",dsb,playpos,writepos,mixlen);	TRACE("buf_writepos=%ld, primary_writepos=%ld\n", buf_writepos, writepos);	TRACE("buf_done=%ld, primary_done=%ld\n", buf_done, primary_done);	TRACE("buf_mixpos=%ld, primary_mixpos=%ld, mixlen=%ld\n", dsb->buf_mixpos, dsb->primary_mixpos,	      mixlen);	TRACE("looping=%ld, startpos=%ld, leadin=%ld\n", dsb->playflags, dsb->startpos, dsb->leadin);	/* check for notification positions */	if (dsb->dsbd.dwFlags & DSBCAPS_CTRLPOSITIONNOTIFY &&	    dsb->state != STATE_STARTING) {		DSOUND_CheckEvent(dsb, played);	}	/* save write position for non-GETCURRENTPOSITION2... */	dsb->playpos = buf_writepos;	/* check whether CalcPlayPosition detected a mixing underrun */	if ((buf_done == 0) && (dsb->primary_mixpos != writepos)) {		/* it did, but did we have more to play? */		if ((dsb->playflags & DSBPLAY_LOOPING) ||		    (dsb->buf_mixpos < dsb->buflen)) {			/* yes, have to recover */			ERR("underrun on sound buffer %p\n", dsb);			TRACE("recovering from underrun: primary_mixpos=%ld\n", writepos);		}		dsb->primary_mixpos = writepos;		primary_done = 0;	}	/* determine how far ahead we should mix */	if (((dsb->playflags & DSBPLAY_LOOPING) ||	     (dsb->leadin && (dsb->probably_valid_to != 0))) &&	    !(dsb->dsbd.dwFlags & DSBCAPS_STATIC)) {		/* if this is a streaming buffer, it typically means that		 * we should defer mixing past probably_valid_to as long		 * as we can, to avoid unnecessary remixing */		/* the heavy-looking calculations shouldn't be that bad,		 * as any game isn't likely to be have more than 1 or 2		 * streaming buffers in use at any time anyway... */		DWORD probably_valid_left =			(dsb->probably_valid_to == (DWORD)-1) ? dsb->buflen :			((dsb->probably_valid_to < buf_writepos) ? dsb->buflen : 0) +			dsb->probably_valid_to - buf_writepos;		/* check for leadin condition */		if ((probably_valid_left == 0) &&		    (dsb->probably_valid_to == dsb->startpos) &&		    dsb->leadin)			probably_valid_left = dsb->buflen;		TRACE("streaming buffer probably_valid_to=%ld, probably_valid_left=%ld\n",		      dsb->probably_valid_to, probably_valid_left);		/* check whether the app's time is already up */		if (probably_valid_left < dsb->writelead) {			WARN("probably_valid_to now within writelead, possible streaming underrun\n");			/* once we pass the point of no return,			 * no reason to hold back anymore */			dsb->probably_valid_to = (DWORD)-1;			/* we just have to go ahead and mix what we have,			 * there's no telling what the app is thinking anyway */		} else {			/* adjust for our frequency and our sample size */			probably_valid_left = MulDiv(probably_valid_left,						     1 << DSOUND_FREQSHIFT,						     dsb->pwfx->nBlockAlign * dsb->freqAdjust) *				              dsb->dsound->device->pwfx->nBlockAlign;			/* check whether to clip mix_len */			if (probably_valid_left < mixlen) {				TRACE("clipping to probably_valid_left=%ld\n", probably_valid_left);				mixlen = probably_valid_left;			}		}	}	/* cut mixlen with what's already been mixed */	if (mixlen < primary_done) {		/* huh? and still CalcPlayPosition didn't		 * detect an underrun? */		FIXME("problem with underrun detection (mixlen=%ld < primary_done=%ld)\n", mixlen, primary_done);		return 0;	}	len = mixlen - primary_done;	TRACE("remaining mixlen=%ld\n", len);	if (len < dsb->dsound->device->fraglen) {		/* smaller than a fragment, wait until it gets larger		 * before we take the mixing overhead */		TRACE("mixlen not worth it, deferring mixing\n");		still_behind = 1;		goto post_mix;	}	/* ok, we know how much to mix, let's go */	still_behind = (adv_done > primary_done);	while (len) {		slen = dsb->dsound->device->buflen - dsb->primary_mixpos;		if (slen > len) slen = len;		slen = DSOUND_MixInBuffer(dsb, dsb->primary_mixpos, slen);		if ((dsb->primary_mixpos < dsb->dsound->device->mixpos) &&		    (dsb->primary_mixpos + slen >= dsb->dsound->device->mixpos))

⌨️ 快捷键说明

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