📄 snd_dsp.cpp
字号:
// Parallel reverbs
///////////////////
// Reverb A
// M parallel reverbs, mixed to mono output
#define CRVAS 64 // max number of parallel series reverbs active
#define CRVA_DLYS 12 // max number of delays making up reverb_a
struct rva_t
{
bool fused;
int m; // number of parallel plain or lowpass delays
int fparallel; // true if filters in parallel with delays, otherwise single output filter
flt_t *pflt;
dly_t *pdlys[CRVA_DLYS]; // array of pointers to delays
};
rva_t rvas[CRVAS];
void RVA_Init ( rva_t *prva ) { if ( prva ) Q_memset (prva, 0, sizeof (rva_t)); }
void RVA_InitAll( void ) { for (int i = 0; i < CRVAS; i++) RVA_Init ( &rvas[i] ); }
// free parallel series reverb
void RVA_Free( rva_t *prva )
{
if ( prva )
{
// free all delays
for (int i = 0; i < CRVA_DLYS; i++)
DLY_Free ( prva->pdlys[i] );
FLT_Free( prva->pflt );
Q_memset( prva, 0, sizeof (rva_t) );
}
}
void RVA_FreeAll( void ) { for (int i = 0; i < CRVAS; i++) RVA_Free( &rvas[i] ); }
// create parallel reverb - m parallel reverbs summed
// D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples)
// a array of reverb feedback parms for parallel reverbs (CRVB_P_DLYS)
// b array of CRVB_P_DLYS - mix params for parallel reverbs
// m - number of parallel delays
// pflt - filter template, to be used by all parallel delays
// fparallel - true if filter operates in parallel with delays, otherwise filter output only
rva_t * RVA_Alloc ( int *D, int *a, int *b, int m, flt_t *pflt, int fparallel )
{
int i;
rva_t *prva;
flt_t *pflt2 = NULL;
// find open slot
for ( i = 0; i < CRVAS; i++ )
{
if ( !rvas[i].fused )
break;
}
// return null if no free slots
if (i == CRVAS)
{
DevMsg ("DSP: Warning, failed to allocate reverb.\n" );
return NULL;
}
prva = &rvas[i];
// if series filter specified, alloc
if ( pflt && !fparallel)
{
// use filter data as template for a filter on output
pflt2 = FLT_Alloc (pflt->M, pflt->L, pflt->a, pflt->b );
if (!pflt2)
{
DevMsg ("DSP: Warning, failed to allocate flt for reverb.\n" );
return NULL;
}
}
// alloc parallel reverbs
if ( pflt && fparallel )
{
// use this filter data as a template to alloc a filter for each parallel delay
for (i = 0; i < m; i++)
prva->pdlys[i] = DLY_AllocLP( D[i], a[i], b[i], DLY_LOWPASS, pflt->M, pflt->L, pflt->a, pflt->b );
}
else
{
// no filter specified, use plain delays in parallel sections
for (i = 0; i < m; i++)
prva->pdlys[i] = DLY_Alloc( D[i], a[i], b[i], DLY_PLAIN );
}
// if we failed to alloc any reverb, free all, return NULL
for (i = 0; i < m; i++)
{
if ( !prva->pdlys[i])
{
FLT_Free( pflt2 );
RVA_Free( prva );
DevMsg ("DSP: Warning, failed to allocate delay for reverb.\n" );
return NULL;
}
}
prva->fused = true;
prva->m = m;
prva->fparallel = fparallel;
prva->pflt = pflt2;
return prva;
}
// parallel reverberator
//
// for each input sample x do:
// x0 = plain(D0,w0,&p0,a0,x)
// x1 = plain(D1,w1,&p1,a1,x)
// x2 = plain(D2,w2,&p2,a2,x)
// x3 = plain(D3,w3,&p3,a3,x)
// y = b0*x0 + b1*x1 + b2*x2 + b3*x3
//
// rgdly - array of 6 delays:
// D - Delay values (typical - 29, 37, 44, 50, 27, 31)
// w - array of delayed values
// p - array of pointers to circular delay line pointers
// a - array of 6 feedback values (typical - all equal, like 0.75 * PMAX)
// b - array of 6 gain values for plain reverb outputs (1, .9, .8, .7)
// xin - input value
// if fparallel, filters are built into delays,
// otherwise, filter output
inline int RVA_GetNext( rva_t *prva, int x )
{
int m = prva->m;
int sum;
int y;
sum = 0;
for (int i = 0; i < m; i++ )
sum += DLY_GetNext( prva->pdlys[i], x );
// m is clamped between RVA_BASEM & CRVA_DLYS
if (m)
y = sum/m;
else
y = x;
#if 0
// PERFORMANCE:
// UNDONE: build as array
int mm;
switch (m)
{
case 12: mm = (PMAX/12); break;
case 11: mm = (PMAX/11); break;
case 10: mm = (PMAX/10); break;
case 9: mm = (PMAX/9); break;
case 8: mm = (PMAX/8); break;
case 7: mm = (PMAX/7); break;
case 6: mm = (PMAX/6); break;
case 5: mm = (PMAX/5); break;
case 4: mm = (PMAX/4); break;
case 3: mm = (PMAX/3); break;
case 2: mm = (PMAX/2); break;
default:
case 1: mm = (PMAX/1); break;
}
y = (sum * mm) >> PBITS;
#endif // 0
// run series filter if present
if ( prva->pflt && !prva->fparallel )
y = FLT_GetNext( prva->pflt, y);
return y;
}
// batch version for performance
inline void RVA_GetNextN( rva_t *prva, 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 = RVA_GetNext( prva, pb->left );
pb++;
}
return;
case OP_RIGHT:
while (count--)
{
pb->right = RVA_GetNext( prva, pb->right );
pb++;
}
return;
case OP_LEFT_DUPLICATE:
while (count--)
{
pb->left = pb->right = RVA_GetNext( prva, pb->left );
pb++;
}
return;
}
}
#define RVA_BASEM 3 // base number of parallel delays
// nominal delay and feedback values
//float rvadlys[] = {29, 37, 44, 50, 62, 75, 96, 118, 127, 143, 164, 175};
float rvadlys[] = {18, 23, 28, 36, 47, 21, 26, 33, 40, 49, 45, 38};
float rvafbs[] = {0.7, 0.7, 0.7, 0.8, 0.8, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9, 0.9};
// reverb parameter order
typedef enum
{
// parameter order
rva_isize,
rva_idensity,
rva_idecay,
rva_iftype,
rva_icutoff,
rva_iqwidth,
rva_ifparallel,
rva_cparam // # of params
} rva_e;
// filter parameter ranges
prm_rng_t rva_rng[] = {
{rva_cparam, 0, 0}, // first entry is # of parameters
// reverb params
{rva_isize, 0.0, 2.0}, // 0-2.0 scales nominal delay parameters (starting at approx 20ms)
{rva_idensity, 0.0, 2.0}, // 0-2.0 density of reverbs (room shape) - controls # of parallel or series delays
{rva_idecay, 0.0, 2.0}, // 0-2.0 scales feedback parameters (starting at approx 0.15)
// filter params for each parallel reverb (quality set to 0 for max execution speed)
{rva_iftype, 0, FTR_MAX},
{rva_icutoff, 10, 22050},
{rva_iqwidth, 100, 11025},
{rva_ifparallel, 0, 1} // if 1, then all filters operate in parallel with delays. otherwise filter output only
};
rva_t * RVA_Params ( prc_t *pprc )
{
rva_t *prva;
int i;
float size = pprc->prm[rva_isize]; // 0-2.0 controls scale of delay parameters
float density = pprc->prm[rva_idensity]; // 0-2.0 density of reverbs (room shape) - controls # of parallel delays
float decay = pprc->prm[rva_idecay]; // 0-1.0 controls feedback parameters
float ftype = pprc->prm[rva_iftype];
float cutoff = pprc->prm[rva_icutoff];
float qwidth = pprc->prm[rva_iqwidth];
float fparallel = pprc->prm[rva_ifparallel];
// D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples)
// a array of reverb feedback parms for parallel delays
// b array of CRVB_P_DLYS - mix params for parallel reverbs
// m - number of parallel delays
int D[CRVA_DLYS];
int a[CRVA_DLYS];
int b[CRVA_DLYS];
int m = RVA_BASEM;
m = density * CRVA_DLYS / 2;
// limit # delays 3-12
m = clamp (m, RVA_BASEM, CRVA_DLYS);
// average time sound takes to travel from most distant wall
// (cap at 1000 ft room)
for ( i = 0; i < m; i++ )
{
// delays of parallel reverb
D[i] = MSEC_TO_SAMPS( rvadlys[i] * size );
// feedback and gain of parallel reverb
a[i] = (int) min (0.9 * PMAX, rvafbs[i] * (float)PMAX * decay);
b[i] = PMAX;
}
// add filter
flt_t *pflt = NULL;
if ( cutoff )
{
// set up dummy lowpass filter to convert params
prc_t prcf;
prcf.prm[flt_iquality] = QUA_LO; // force filter to low quality for faster execution time
prcf.prm[flt_icutoff] = cutoff;
prcf.prm[flt_iftype] = ftype;
prcf.prm[flt_iqwidth] = qwidth;
pflt = (flt_t *)FLT_Params ( &prcf );
}
prva = RVA_Alloc ( D, a, b, m, pflt, fparallel );
FLT_Free( pflt );
return prva;
}
inline void * RVA_VParams ( void *p )
{
PRC_CheckParams ( (prc_t *)p, rva_rng );
return (void *) RVA_Params ((prc_t *)p);
}
inline void RVA_Mod ( void *p, float v ) { return; }
////////////
// Diffusor
///////////
// (N series allpass reverbs)
#define CDFRS 64 // max number of series reverbs active
#define CDFR_DLYS 16 // max number of delays making up diffusor
struct dfr_t
{
bool fused;
int n; // series allpass delays
int w[CDFR_DLYS]; // internal state array for series allpass filters
dly_t *pdlys[CDFR_DLYS]; // array of pointers to delays
};
dfr_t dfrs[CDFRS];
void DFR_Init ( dfr_t *pdfr ) { if ( pdfr ) Q_memset (pdfr, 0, sizeof (dfr_t)); }
void DFR_InitAll( void ) { for (int i = 0; i < CDFRS; i++) DFR_Init ( &dfrs[i] ); }
// free parallel series reverb
void DFR_Free( dfr_t *pdfr )
{
if ( pdfr )
{
// free all delays
for (int i = 0; i < CDFR_DLYS; i++)
DLY_Free ( pdfr->pdlys[i] );
Q_memset( pdfr, 0, sizeof (dfr_t) );
}
}
void DFR_FreeAll( void ) { for (int i = 0; i < CDFRS; i++) DFR_Free( &dfrs[i] ); }
// create n series allpass reverbs
// D array of CRVB_DLYS reverb delay sizes max sample index w[0...D] (ie: D+1 samples)
// a array of reverb feedback parms for series delays
// b array of gain params for parallel reverbs
// n - number of series delays
dfr_t * DFR_Alloc ( int *D, int *a, int *b, int n )
{
int i;
dfr_t *pdfr;
// find open slot
for (i = 0; i < CDFRS; i++)
{
if (!dfrs[i].fused)
break;
}
// return null if no free slots
if (i == CDFRS)
{
DevMsg ("DSP: Warning, failed to allocate diffusor.\n" );
return NULL;
}
pdfr = &dfrs[i];
DFR_Init( pdfr );
// alloc reverbs
for (i = 0; i < n; i++)
pdfr->pdlys[i] = DLY_Alloc( D[i], a[i], b[i], DLY_ALLPASS );
// if we failed to alloc any reverb, free all, return NULL
for (i = 0; i < n; i++)
{
if ( !pdfr->pdlys[i])
{
DFR_Free( pdfr );
DevMsg ("DSP: Warning, failed to allocate delay for diffusor.\n" );
return NULL;
}
}
pdfr->fused = true;
pdfr->n = n;
return pdfr;
}
// series reverberator
inline int DFR_GetNext( dfr_t *pdfr, int x )
{
int i;
int n = pdfr->n;
int y;
y = x;
for (i = 0; i < n; i++)
y = DLY_GetNext(pdfr->pdlys[i], y);
return y;
#if 0
// alternate method, using internal state - causes PREDELAY = sum of delay times
int *v = pdfr->w; // intermediate results
v[0] = x;
// reverse evaluate series delays
// w[0] w[1] w[2] w[n-1] w[n]
// x---->D[0]--->D[1]--->D[2]...-->D[n-1]--->out
//
for (i = n; i > 0; i--)
v[i] = DLY_GetNext(pdfr->pdlys[i-1], v[i-1]);
return v[n];
#endif
}
// batch version for performance
inline void DFR_GetNextN( dfr_t *pdfr, portable_samplepair_t *pbuffer, int SampleCount, int op )
{
int count = SampleCount;
portable_samplepair_t *pb = pbuffer;
switch (op)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -