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

📄 snd_dsp.cpp

📁 hl2 source code. Do not use it illegal.
💻 CPP
📖 第 1 页 / 共 5 页
字号:
		*i -= D + 1;		// when *pi = D + 1, it wraps around to *pi = 0
	
	if ( *i < 0 )
		*i += D + 1;		// when *pi = - 1, it wraps around to *pi = D
}

// set initial update value - fstep can have no more than 8 bits of integer and 20 bits of fract
// D is array max dimension w[0...D] (ie: size D+1)
// w is ptr to array
// p is ptr to pos_t to initialize

inline void POS_Init( pos_t *p, int D, float fstep )
{
	float step = fstep;

	// make sure int part of step is capped at fix20_intmax

	if ((int)step > FIX20_INTMAX)
		step = (step - (int)step) + FIX20_INTMAX;

	p->step = FLOAT_TO_FIX20(step);	// convert fstep to fixed point
	p->cstep = 0;			
	p->pos = 0;							// current update value

	p->D = D;							// always init to end value, in case we're stepping backwards
}

// change step value - this is an instantaneous change, not smoothed.

inline void POS_ChangeVal( pos_t *p, float fstepnew )
{
	p->step = FLOAT_TO_FIX20( fstepnew );	// convert fstep to fixed point
}

// return current integer position, then update internal position value

inline int POS_GetNext ( pos_t *p )
{
	
	//float f = FIX20_TO_FLOAT(p->cstep);
	//int i1 = FIX20_INTPART(p->cstep);
	//float f1 = FIX20_TO_FLOAT(FIX20_FRACPART(p->cstep));
	//float f2 = FIX20_TO_FLOAT(p->step);

	p->cstep += p->step;						// update accumulated fraction step value (fixed point)
	p->pos += FIX20_INTPART( p->cstep );		// update pos with integer part of accumulated step
	p->cstep = FIX20_FRACPART( p->cstep );		// throw away the integer part of accumulated step

	// wrap pos around either end of buffer if needed

	POS_Wrap(p->D, &(p->pos));

	// make sure returned position is within array bounds

	Assert (p->pos <= p->D);

	return p->pos;
}

// oneshot position within wav 
struct pos_one_t
{
	pos_t p;				// pos_t
	bool fhitend;			// flag indicating we hit end of oneshot wav
};

// set initial update value - fstep can have no more than 8 bits of integer and 20 bits of fract
// one shot position - play only once, don't wrap, when hit end of buffer, return last position

inline void POS_ONE_Init( pos_one_t *p1, int D, float fstep )
{
	POS_Init( &p1->p, D, fstep ) ;
	
	p1->fhitend = false;
}

// return current integer position, then update internal position value

inline int POS_ONE_GetNext ( pos_one_t *p1 )
{
	int pos;
	pos_t *p0;

	pos = p1->p.pos;							// return current position
	
	if (p1->fhitend)
		return pos;

	p0 = &(p1->p);
	p0->cstep += p0->step;						// update accumulated fraction step value (fixed point)
	p0->pos += FIX20_INTPART( p0->cstep );		// update pos with integer part of accumulated step
	//p0->cstep = SIGN(p0->cstep) * FIX20_FRACPART( p0->cstep );
	p0->cstep = FIX20_FRACPART( p0->cstep );		// throw away the integer part of accumulated step

	// if we wrapped, stop updating, always return last position
	// if step value is 0, return hit end

	if (!p0->step || p0->pos < 0 || p0->pos >= p0->D )
		p1->fhitend = true;
	else
		pos = p0->pos;

	// make sure returned value is within array bounds

	Assert ( pos <= p0->D );

	return pos;
}


/////////////////////
// Reverbs and delays
/////////////////////

#define CDLYS			128				// max delay lines active. Also used for lfos.

#define DLY_PLAIN		0				// single feedback loop
#define DLY_ALLPASS		1				// feedback and feedforward loop - flat frequency response (diffusor)
#define DLY_LOWPASS		2				// lowpass filter in feedback loop
#define DLY_LINEAR		3				// linear delay, no feedback, unity gain
#define DLY_MAX			DLY_LINEAR

// delay line 

struct dly_t
{
	
	bool fused;						// true if dly is in use
	int type;						// delay type

	int D;							// delay size, in samples
	int t;							// current tap, <= D
	int D0;							// original delay size (only relevant if calling DLY_ChangeVal)
	int *p;							// circular buffer pointer
	int *w;							// array of samples

	int a;							// feedback value 0..PMAX,normalized to 0-1.0
	int b;							// gain value 0..PMAX, normalized to 0-1.0

	flt_t *pflt;					// pointer to filter, if type DLY_LOWPASS

	HANDLE h;						// memory handle for sample array	
};

dly_t dlys[CDLYS];					// delay lines

void DLY_Init ( dly_t *pdly ) {	if ( pdly )	Q_memset( pdly, 0, sizeof (dly_t)); }
void DLY_InitAll ( void ) {	for (int i = 0 ; i < CDLYS; i++) DLY_Init ( &dlys[i] ); }
void DLY_Free ( dly_t *pdly )
{
	// free memory buffer

	if ( pdly )
	{
		FLT_Free ( pdly->pflt );

		if ( pdly->w )
		{
			GlobalUnlock( pdly->h );
			GlobalFree( pdly->h );
		}
		
		// free dly slot

		Q_memset ( pdly, 0, sizeof (dly_t) );
	}
}


void DLY_FreeAll ( void ) {	for (int i = 0; i < CDLYS; i++ ) DLY_Free ( &dlys[i] ); }

// set up 'b' gain parameter of feedback delay to
// compensate for gain caused by feedback.  

void DLY_SetNormalizingGain ( dly_t *pdly )
{
	// compute normalized gain, set as output gain

	// calculate gain of delay line with feedback, and use it to
	// reduce output.  ie: force delay line with feedback to unity gain

	// for constant input x with feedback fb:

	// out = x + x*fb + x * fb^2 + x * fb^3...
	// gain = out/x
	// so gain = 1 + fb + fb^2 + fb^3...
	// which, by the miracle of geometric series, equates to 1/1-fb
	// thus, gain = 1/(1-fb)
	
	float fgain = 0;
	float gain;
	int b;

	// if b is 0, set b to PMAX (1)

	b = pdly->b ? pdly->b : PMAX;

	// fgain = b * (1.0 / (1.0 - (float)pdly->a / (float)PMAX)) / (float)PMAX;

	fgain = (1.0 / (1.0 - (float)pdly->a / (float)PMAX));

	// compensating gain -  multiply rva output by gain then >> PBITS

	gain = (int)((1.0 / fgain) * PMAX);	

	gain = gain * 4;	// compensate for fact that gain calculation is for +/- 32767 amplitude wavs
						// ie: ok to allow a bit more gain because most wavs are not at theoretical peak amplitude at all times

	gain = min (gain, PMAX);	// cap at PMAX
	
	gain = ((float)b/(float)PMAX) * gain;	// scale final gain by pdly->b.

	pdly->b = (int)gain;
}

// allocate a new delay line
// D number of samples to delay
// a feedback value (0-PMAX normalized to 0.0-1.0)
// b gain value (0-PMAX normalized to 0.0-1.0)
// if DLY_LOWPASS:
//		L - numerator order of filter
//		M - denominator order of filter
//		fb - numerator params, M+1 
//		fa - denominator params, L+1

dly_t * DLY_AllocLP ( int D, int a, int b, int type, int M, int L, int *fa, int *fb )
{
	HANDLE h;
	int cb;
	int *w;
	int i;
	dly_t *pdly = NULL;

	// find open slot

	for (i = 0; i < CDLYS; i++)
	{
		if (!dlys[i].fused)
		{
			pdly = &dlys[i];
			DLY_Init( pdly );
			break;
		}
	}
	
	if ( i == CDLYS )
	{
		DevMsg ("DSP: Warning, failed to allocate delay line.\n" );
		return NULL;					// all delay lines in use
	}

	cb = (D + 1) * sizeof ( int );		// assume all samples are signed integers

	if (type == DLY_LOWPASS)
	{
		// alloc lowpass fir_filter
	
		pdly->pflt = FLT_Alloc( M, L, fa, fb );
		if ( !pdly->pflt )
		{
			DevMsg ("DSP: Warning, failed to allocate filter for delay line.\n" );
			return NULL;
		}
	}

	// alloc delay memory
	
	h = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, cb); 
	if (!h) 
	{ 
		g_pSoundServices->ConSafePrint ("Sound DSP: Out of memory.\n");
		FLT_Free ( pdly->pflt );
		return NULL; 
	}
	
	// lock delay memory

	w = (int *)GlobalLock(h);

	if ( !w )
	{ 
		g_pSoundServices->ConSafePrint ("Sound DSP: Failed to lock.\n");
		GlobalFree(h);
		FLT_Free ( pdly->pflt );
		return NULL; 
	}
	
	// clear delay array

	Q_memset (w, 0, cb);
	
	// init values

	pdly->type = type;
	pdly->D = D;
	pdly->t = D;		// set delay tap to full delay
	pdly->D0 = D;
	pdly->p = w;		// init circular pointer to head of buffer
	pdly->w = w;
	pdly->h = h;
	pdly->a = min( a, PMAX );		// do not allow 100% feedback
	pdly->b = b;
	pdly->fused = true;

	if ( type == DLY_LINEAR ) 
	{
		// linear delay has no feedback and unity gain

		pdly->a = 0;
		pdly->b = PMAX;
	}
	else
	{
		// adjust b to compensate for feedback gain

		DLY_SetNormalizingGain( pdly );	
	}

	return (pdly);
}

// allocate lowpass or allpass delay

dly_t * DLY_Alloc( int D, int a, int b, int type )
{
	return DLY_AllocLP( D, a, b, type, 0, 0, 0, 0 );
}


// Allocate new delay, convert from float params in prc preset to internal parameters
// Uses filter params in prc if delay is type lowpass

// delay parameter order

typedef enum {

 dly_idtype,		// NOTE: first 8 params must match those in mdy_e
 dly_idelay,		
 dly_ifeedback,		
 dly_igain,		

 dly_iftype,		
 dly_icutoff,	
 dly_iqwidth,		
 dly_iquality, 
	
 dly_cparam

} dly_e;


// delay parameter ranges

prm_rng_t dly_rng[] = {

	{dly_cparam,	0, 0},			// first entry is # of parameters
		
	// delay params

	{dly_idtype,	0, DLY_MAX},		// delay type DLY_PLAIN, DLY_LOWPASS, DLY_ALLPASS	
	{dly_idelay,	0.0, 1000.0},		// delay in milliseconds
	{dly_ifeedback,	0.0, 0.99},			// feedback 0-1.0
	{dly_igain,	    0.0, 1.0},			// final gain of output stage, 0-1.0

	// filter params if dly type DLY_LOWPASS

	{dly_iftype,	0, FTR_MAX},			
	{dly_icutoff,	10.0, 22050.0},
	{dly_iqwidth,	100.0, 11025.0},
	{dly_iquality,	0, QUA_MAX},
};

dly_t * DLY_Params ( prc_t *pprc )
{
	dly_t *pdly = NULL;
	int D, a, b;
	
	float delay		= pprc->prm[dly_idelay];
	float feedback	= pprc->prm[dly_ifeedback];
	float gain		= pprc->prm[dly_igain];
	int type		= pprc->prm[dly_idtype];

	float ftype 	= pprc->prm[dly_iftype];
	float cutoff	= pprc->prm[dly_icutoff];
	float qwidth	= pprc->prm[dly_iqwidth];
	float qual		= pprc->prm[dly_iquality];

	D = MSEC_TO_SAMPS(delay);					// delay samples
	a = feedback * PMAX;						// feedback
	b = gain * PMAX;							// gain
	
	switch ( (int) type )
	{
	case DLY_PLAIN:
	case DLY_ALLPASS:
	case DLY_LINEAR:
		pdly = DLY_Alloc( D, a, b, type );
		break;

	case DLY_LOWPASS: 
		{
		// set up dummy lowpass filter to convert params

		prc_t prcf;

		prcf.prm[flt_iquality]	= qual;	// 0,1,2 - high, medium, low (low quality implies faster execution time)
		prcf.prm[flt_icutoff]	= cutoff;
		prcf.prm[flt_iftype]	= ftype;
		prcf.prm[flt_iqwidth]	= qwidth;
	
		flt_t *pflt = (flt_t *)FLT_Params ( &prcf );
		
		if ( !pflt )
		{
			DevMsg ("DSP: Warning, failed to allocate filter.\n" );
			return NULL;
		}

		pdly = DLY_AllocLP ( D, a, b, type, pflt->M, pflt->L, pflt->a, pflt->b );

		FLT_Free ( pflt );
		break;
		}
	}

	return pdly;
}

inline void * DLY_VParams ( void *p ) 
{
	PRC_CheckParams( (prc_t *)p, dly_rng );
	return (void *) DLY_Params ((prc_t *)p); 
}

// get next value from delay line, move x into delay line

int DLY_GetNext ( dly_t *pdly, int x )
{
	switch (pdly->type)
	{
	default:
	case DLY_PLAIN:
		return dly_plain( pdly->D, pdly->t, pdly->w, &pdly->p, pdly->a, pdly->b, x );
	case DLY_ALLPASS:
		return dly_allpass( pdly->D, pdly->t, pdly->w, &pdly->p, pdly->a, pdly->b, x );
	case DLY_LOWPASS:
		return dly_lowpass( pdly->D, pdly->t, pdly->w, &(pdly->p), pdly->a, pdly->b, pdly->pflt->M, pdly->pflt->a, pdly->pflt->L, pdly->pflt->b, pdly->pflt->w, x );
	case DLY_LINEAR:
		return dly_linear( pdly->D, pdly->t, pdly->w, &pdly->p, x );
	}		
}

// batch version for performance

void DLY_GetNextN( dly_t *pdly, portable_samplepair_t *pbuffer, int SampleCount, int op )
{
	int count = SampleCount;
	portable_samplepair_t *pb = pbuffer;
	
	switch (op)
	{
	default:
	case OP_LEFT:
		while (count--)
		{
			pb->left = DLY_GetNext( pdly, pb->left );
			pb++;
		}
		return;
	case OP_RIGHT:
		while (count--)
		{
			pb->right = DLY_GetNext( pdly, pb->right );
			pb++;
		}
		return;
	case OP_LEFT_DUPLICATE:
		while (count--)
		{
			pb->left = pb->right = DLY_GetNext( pdly, pb->left );
			pb++;
		}
		return;
	}
}

// get tap on t'th sample in delay - don't update buffer pointers, this is done via DLY_GetNext

inline int DLY_GetTap ( dly_t *pdly, int t )
{
	return tap (pdly->D, pdly->w, pdly->p, t );
}


// make instantaneous change to new delay value D.
// t tap value must be <= original D (ie: we don't do any reallocation here)

void DLY_ChangeVal ( dly_t *pdly, int t )
{
	// never set delay > original delay

	pdly->t = min ( t, pdly->D0 );
}

// ignored - use MDY_ for modulatable delay

inline void DLY_Mod ( void *p, float v ) { return; }


///////////////////

⌨️ 快捷键说明

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